shoc-backend/SeaHaven.Services/Implementation/SlaBreachAcknowledgementService.cs

77 lines
3.4 KiB
C#
Raw Normal View History

using System.Security.Claims;
using Data.SeaHavenIndustries;
using SeaHaven.DataServices.Interfaces;
using SeaHaven.DataServices.Models;
using SeaHaven.Services.Exceptions;
using SeaHaven.Services.Helpers;
using SeaHaven.Services.Interfaces;
namespace SeaHaven.Services.Implementation
{
public class SlaBreachAcknowledgementService : ISlaBreachAcknowledgementService
{
private readonly INotificationFeedDataService _feedData;
private readonly IWorkOrderAuditDataService _auditData;
private readonly IUserDataService _userData;
private readonly IWorkOrderAccountResolver _accountResolver;
private readonly TimeProvider _timeProvider;
public SlaBreachAcknowledgementService(
INotificationFeedDataService feedData,
IWorkOrderAuditDataService auditData,
IUserDataService userData,
IWorkOrderAccountResolver accountResolver,
TimeProvider timeProvider)
{
_feedData = feedData;
_auditData = auditData;
_userData = userData;
_accountResolver = accountResolver;
_timeProvider = timeProvider;
}
public async Task<SlaBreachAcknowledgementOutcome> AcknowledgeAsync(
ClaimsPrincipal user, int workOrderId, CancellationToken cancellationToken)
{
// The same audience as the feed: a row the caller cannot see cannot be acknowledged.
var accountId = _accountResolver.ResolveAccountFilter(user);
var (dispatcherId, _) = NotificationAudience.Resolve(user);
var userId = user.FindFirstValue(ClaimTypes.NameIdentifier);
if (string.IsNullOrWhiteSpace(userId))
throw new WorkOrderBoardValidationException("Forbidden", "Acknowledging requires a signed-in user.");
var candidate = await _feedData.GetSlaCandidateAsync(
new NotificationFeedScope(accountId, dispatcherId), workOrderId, cancellationToken);
if (candidate == null)
return SlaBreachAcknowledgementOutcome.NotFound;
var now = _timeProvider.GetUtcNow().UtcDateTime;
var clock = SlaResponseWindows.Evaluate(candidate.Severity, candidate.CreatedAt, now);
if (clock is not { State: SlaState.Breached })
return SlaBreachAcknowledgementOutcome.NotBreached;
if (candidate.BreachAcknowledged)
return SlaBreachAcknowledgementOutcome.AlreadyAcknowledged;
var names = await _userData.GetDisplayNamesByIdsAsync(new[] { userId });
var name = names.TryGetValue(userId, out var displayName) && !string.IsNullOrWhiteSpace(displayName)
? displayName
: user.FindFirstValue(ClaimTypes.Name) ?? userId;
_auditData.EnqueueAuditLog(new WorkOrderAuditLog
{
WorkOrderId = workOrderId,
UserId = userId,
FieldName = SlaBreachAcknowledgementAudit.FieldName,
OldValue = clock.DeadlineAt.ToString("o"),
NewValue = candidate.Severity,
Action = $"SLA breach acknowledged by {name}",
EventType = "system",
ActorType = "internal",
CreatedAt = now
});
await _auditData.PersistAsync(cancellationToken);
return SlaBreachAcknowledgementOutcome.Acknowledged;
}
}
}