feature: Ward Gateway Service (Local-First Clinical Path)
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
public class LocalAlertService : ILocalAlertService
|
||||
{
|
||||
private readonly GatewayDbContext _db;
|
||||
|
||||
public LocalAlertService(GatewayDbContext db) => _db = db;
|
||||
|
||||
public async Task<LocalClinicalAlert> AcknowledgeAsync(
|
||||
Guid id, AcknowledgeAlertRequest req, string clinicianId)
|
||||
{
|
||||
var alert = await _db.ClinicalAlerts.FindAsync(id)
|
||||
?? throw new NotFoundException("Alert not found.", "ALERT_NOT_FOUND");
|
||||
|
||||
if (alert.Status is not (AlertStatus.Open or AlertStatus.Escalated))
|
||||
throw new ConflictException("Alert cannot be acknowledged.", "ALERT_NOT_ACKNOWLEDGEABLE");
|
||||
|
||||
alert.Status = AlertStatus.Acknowledged;
|
||||
alert.AcknowledgedAt = DateTimeOffset.UtcNow;
|
||||
alert.AcknowledgedBy = clinicianId;
|
||||
|
||||
BufferedSyncWriter.Enqueue(_db, BufferedSyncItemType.Ack, new
|
||||
{
|
||||
clientRef = Guid.NewGuid(),
|
||||
alert.ClientAlertId,
|
||||
clinicianId,
|
||||
acknowledgedAt = alert.AcknowledgedAt,
|
||||
req.Note
|
||||
}, $"ack:{alert.ClientAlertId}:{alert.AcknowledgedAt:o}", alert.EncounterId, alert.AcknowledgedAt!.Value);
|
||||
|
||||
await _db.SaveChangesAsync();
|
||||
return alert;
|
||||
}
|
||||
|
||||
public async Task<LocalClinicalAlert> ResolveAsync(Guid id)
|
||||
{
|
||||
var alert = await _db.ClinicalAlerts.FindAsync(id)
|
||||
?? throw new NotFoundException("Alert not found.", "ALERT_NOT_FOUND");
|
||||
|
||||
if (alert.Status != AlertStatus.Acknowledged)
|
||||
throw new ConflictException("Alert must be acknowledged first.", "ALERT_NOT_ACKNOWLEDGED");
|
||||
|
||||
alert.Status = AlertStatus.Resolved;
|
||||
alert.ResolvedAt = DateTimeOffset.UtcNow;
|
||||
|
||||
BufferedSyncWriter.Enqueue(_db, BufferedSyncItemType.Resolve, new
|
||||
{
|
||||
clientRef = Guid.NewGuid(),
|
||||
alert.ClientAlertId,
|
||||
clinicianId = alert.AcknowledgedBy,
|
||||
resolvedAt = alert.ResolvedAt,
|
||||
note = (string?)null
|
||||
}, $"resolve:{alert.ClientAlertId}", alert.EncounterId, alert.ResolvedAt!.Value);
|
||||
|
||||
await _db.SaveChangesAsync();
|
||||
return alert;
|
||||
}
|
||||
|
||||
public async Task<PagedResult<LocalClinicalAlert>> ListAsync(
|
||||
Guid? encounterId, string? status, int page, int pageSize)
|
||||
{
|
||||
page = Math.Max(1, page);
|
||||
pageSize = Math.Clamp(pageSize, 1, 100);
|
||||
|
||||
var q = _db.ClinicalAlerts.AsNoTracking().AsQueryable();
|
||||
if (encounterId.HasValue) q = q.Where(a => a.EncounterId == encounterId);
|
||||
if (!string.IsNullOrEmpty(status))
|
||||
{
|
||||
var parsedStatus = AlertStatusExtensions.FromDbString(status);
|
||||
q = q.Where(a => a.Status == parsedStatus);
|
||||
}
|
||||
var total = await q.CountAsync();
|
||||
var items = await q.OrderByDescending(a => a.TriggeredAt)
|
||||
.Skip((page - 1) * pageSize).Take(pageSize).ToListAsync();
|
||||
return new PagedResult<LocalClinicalAlert>(items, page, pageSize, total);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user