102 lines
3.6 KiB
C#
102 lines
3.6 KiB
C#
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.Extensions.Options;
|
|
|
|
public class MedicationService : IMedicationService
|
|
{
|
|
private readonly AppDbContext _db;
|
|
private readonly MedicationCorrelationOptions _correlationOptions;
|
|
|
|
public MedicationService(
|
|
AppDbContext db,
|
|
IOptions<MedicationCorrelationOptions> correlationOptions)
|
|
{
|
|
_db = db;
|
|
_correlationOptions = correlationOptions.Value;
|
|
}
|
|
|
|
public async Task<MedicationAdministration> CreateAsync(
|
|
Guid encounterId, CreateMedicationAdministrationRequest req)
|
|
{
|
|
var encounter = await _db.Encounters.FindAsync(encounterId);
|
|
if (encounter is null)
|
|
throw new NotFoundException("Encounter not found.", "ENCOUNTER_NOT_FOUND");
|
|
|
|
if (encounter.Status != EncounterStatus.Active)
|
|
throw new ConflictException(
|
|
"Cannot record medications for a non-active encounter.",
|
|
"ENCOUNTER_NOT_ACTIVE");
|
|
|
|
var med = new MedicationAdministration
|
|
{
|
|
Id = Guid.NewGuid(),
|
|
EncounterId = encounterId,
|
|
DrugName = req.DrugName.Trim(),
|
|
Dose = req.Dose,
|
|
DoseUnit = req.DoseUnit.Trim(),
|
|
Route = req.Route.Trim(),
|
|
AdministeredAt = req.AdministeredAt ?? DateTimeOffset.UtcNow,
|
|
AdministeredBy = req.AdministeredBy.Trim()
|
|
};
|
|
|
|
_db.MedicationAdministrations.Add(med);
|
|
await _db.SaveChangesAsync();
|
|
return med;
|
|
}
|
|
|
|
public async Task<MedicationAdministration> GetByIdAsync(Guid id)
|
|
{
|
|
var med = await _db.MedicationAdministrations
|
|
.AsNoTracking()
|
|
.Include(m => m.Encounter)
|
|
.FirstOrDefaultAsync(m => m.Id == id);
|
|
|
|
if (med is null)
|
|
throw new NotFoundException("Medication administration not found.", "MEDICATION_NOT_FOUND");
|
|
|
|
return med;
|
|
}
|
|
|
|
public async Task<IReadOnlyList<MedicationAdministration>> GetRecentForEncounterAsync(
|
|
Guid encounterId, string observationCode, int windowMinutes)
|
|
{
|
|
var cutoff = DateTimeOffset.UtcNow.AddMinutes(-windowMinutes);
|
|
var mappings = _correlationOptions.DrugVitalMappings;
|
|
|
|
// Find drugs that affect this observation code
|
|
var relevantDrugs = mappings
|
|
.Where(kv => kv.Value.Contains(observationCode, StringComparer.OrdinalIgnoreCase))
|
|
.Select(kv => kv.Key)
|
|
.ToHashSet(StringComparer.OrdinalIgnoreCase);
|
|
|
|
if (relevantDrugs.Count == 0)
|
|
return Array.Empty<MedicationAdministration>();
|
|
|
|
return await _db.MedicationAdministrations
|
|
.AsNoTracking()
|
|
.Where(m => m.EncounterId == encounterId
|
|
&& m.AdministeredAt >= cutoff
|
|
&& relevantDrugs.Contains(m.DrugName.ToLower()))
|
|
.OrderByDescending(m => m.AdministeredAt)
|
|
.ToListAsync();
|
|
}
|
|
|
|
public async Task<PagedResult<MedicationAdministration>> ListByEncounterAsync(
|
|
Guid encounterId, DateTimeOffset? since, int page, int pageSize)
|
|
{
|
|
var query = _db.MedicationAdministrations
|
|
.AsNoTracking()
|
|
.Where(m => m.EncounterId == encounterId);
|
|
|
|
if (since.HasValue)
|
|
query = query.Where(m => m.AdministeredAt >= since.Value);
|
|
|
|
var total = await query.CountAsync();
|
|
var items = await query
|
|
.OrderByDescending(m => m.AdministeredAt)
|
|
.Skip((page - 1) * pageSize)
|
|
.Take(pageSize)
|
|
.ToListAsync();
|
|
|
|
return new PagedResult<MedicationAdministration>(items, page, pageSize, total);
|
|
}
|
|
} |