feature: RBAC + Clinical Audit Logging
This commit is contained in:
@@ -6,11 +6,19 @@ public class AlertService : IAlertService
|
||||
{
|
||||
private readonly AppDbContext _db;
|
||||
private readonly IServiceProvider _services;
|
||||
private readonly ICurrentUserService _currentUser;
|
||||
private readonly IAuditService _audit;
|
||||
|
||||
public AlertService(AppDbContext db, IServiceProvider services)
|
||||
public AlertService(
|
||||
AppDbContext db,
|
||||
IServiceProvider services,
|
||||
ICurrentUserService currentUser,
|
||||
IAuditService audit)
|
||||
{
|
||||
_db = db;
|
||||
_services = services;
|
||||
_currentUser = currentUser;
|
||||
_audit = audit;
|
||||
}
|
||||
|
||||
public async Task<PagedResult<ClinicalAlert>> ListByEncounterAsync(
|
||||
@@ -75,6 +83,12 @@ public class AlertService : IAlertService
|
||||
|
||||
public async Task<ClinicalAlert> AcknowledgeAsync(Guid id, AcknowledgeAlertRequest req)
|
||||
{
|
||||
if (!_currentUser.IsAuthenticated)
|
||||
throw new ValidationException("Authentication required.", "AUTH_REQUIRED");
|
||||
|
||||
var displayName = _currentUser.DisplayName ?? _currentUser.Username
|
||||
?? throw new ValidationException("Authenticated user identity missing.", "AUTH_REQUIRED");
|
||||
|
||||
var alert = await _db.ClinicalAlerts.FindAsync(id);
|
||||
if (alert is null)
|
||||
throw new NotFoundException("Alert not found.", "ALERT_NOT_FOUND");
|
||||
@@ -84,9 +98,10 @@ public class AlertService : IAlertService
|
||||
$"Alert cannot be acknowledged from status '{alert.Status}'.",
|
||||
"ALERT_NOT_ACKNOWLEDGEABLE");
|
||||
|
||||
var previousStatus = alert.Status;
|
||||
alert.Status = AlertStatus.Acknowledged;
|
||||
alert.AcknowledgedAt = DateTimeOffset.UtcNow;
|
||||
alert.AcknowledgedBy = req.ClinicianId;
|
||||
alert.AcknowledgedBy = displayName;
|
||||
|
||||
// Write an outbox event so the Kafka consumer (Phase 6) can cancel the
|
||||
// pending RabbitMQ escalation timer when it sees this acknowledgment.
|
||||
@@ -98,7 +113,7 @@ public class AlertService : IAlertService
|
||||
{
|
||||
alertId = alert.Id,
|
||||
encounterId = alert.EncounterId,
|
||||
acknowledgedBy = req.ClinicianId,
|
||||
acknowledgedBy = displayName,
|
||||
acknowledgedAt = alert.AcknowledgedAt,
|
||||
note = req.Note
|
||||
}),
|
||||
@@ -120,6 +135,25 @@ public class AlertService : IAlertService
|
||||
}
|
||||
|
||||
await _db.SaveChangesAsync();
|
||||
|
||||
await _audit.WriteAsync(
|
||||
AuditAction.AlertAcknowledged,
|
||||
"ClinicalAlert",
|
||||
alert.Id,
|
||||
previousValue: new { status = previousStatus.ToDbString() },
|
||||
newValue: new { status = alert.Status.ToDbString(), alert.AcknowledgedBy },
|
||||
reason: req.Note);
|
||||
|
||||
if (alert.AlertType.IsSuppressible())
|
||||
{
|
||||
await _audit.WriteAsync(
|
||||
AuditAction.SuppressionWindowSet,
|
||||
"ClinicalAlert",
|
||||
alert.Id,
|
||||
newValue: new { alert.AlertType, alert.EncounterId },
|
||||
reason: req.Note);
|
||||
}
|
||||
|
||||
return alert;
|
||||
}
|
||||
|
||||
@@ -157,6 +191,13 @@ public class AlertService : IAlertService
|
||||
alert.ResolvedAt = DateTimeOffset.UtcNow;
|
||||
await _db.SaveChangesAsync();
|
||||
|
||||
await _audit.WriteAsync(
|
||||
AuditAction.AlertResolved,
|
||||
"ClinicalAlert",
|
||||
alert.Id,
|
||||
previousValue: new { status = AlertStatus.Acknowledged.ToDbString() },
|
||||
newValue: new { status = alert.Status.ToDbString() });
|
||||
|
||||
return alert;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user