feature: FHIR R4 Inbound Facade
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
using Hl7.Fhir.Model;
|
||||
|
||||
public class FhirBundleProcessor
|
||||
{
|
||||
private readonly IPatientService _patients;
|
||||
private readonly IEncounterService _encounters;
|
||||
private readonly IObservationService _observations;
|
||||
private readonly IMedicationService _medications;
|
||||
private readonly PatientFhirMapper _patientMapper;
|
||||
private readonly EncounterFhirMapper _encounterMapper;
|
||||
private readonly ObservationFhirMapper _observationMapper;
|
||||
private readonly MedicationAdministrationFhirMapper _medMapper;
|
||||
|
||||
public FhirBundleProcessor(
|
||||
IPatientService patients,
|
||||
IEncounterService encounters,
|
||||
IObservationService observations,
|
||||
IMedicationService medications,
|
||||
PatientFhirMapper patientMapper,
|
||||
EncounterFhirMapper encounterMapper,
|
||||
ObservationFhirMapper observationMapper,
|
||||
MedicationAdministrationFhirMapper medMapper)
|
||||
{
|
||||
_patients = patients;
|
||||
_encounters = encounters;
|
||||
_observations = observations;
|
||||
_medications = medications;
|
||||
_patientMapper = patientMapper;
|
||||
_encounterMapper = encounterMapper;
|
||||
_observationMapper = observationMapper;
|
||||
_medMapper = medMapper;
|
||||
}
|
||||
|
||||
public async Task<Bundle> ProcessTransactionAsync(Bundle transaction)
|
||||
{
|
||||
var response = new Bundle { Type = Bundle.BundleType.TransactionResponse };
|
||||
|
||||
// Process in dependency order: Patient → Encounter → Observation/MedAdmin
|
||||
var entries = transaction.Entry
|
||||
.OrderBy(e => Priority(e.Resource))
|
||||
.ToList();
|
||||
|
||||
foreach (var entry in entries)
|
||||
{
|
||||
var resource = entry.Resource;
|
||||
try
|
||||
{
|
||||
var location = resource switch
|
||||
{
|
||||
Hl7.Fhir.Model.Patient p => await ProcessPatientAsync(p),
|
||||
Hl7.Fhir.Model.Encounter e => await ProcessEncounterAsync(e),
|
||||
Hl7.Fhir.Model.Observation o => await ProcessObservationAsync(o),
|
||||
Hl7.Fhir.Model.MedicationAdministration m => await ProcessMedAsync(m),
|
||||
_ => throw new FhirMappingException(
|
||||
$"Unsupported resource type in bundle: {resource.TypeName}", "not-supported")
|
||||
};
|
||||
|
||||
response.Entry.Add(new Bundle.EntryComponent
|
||||
{
|
||||
Response = new Bundle.ResponseComponent
|
||||
{
|
||||
Status = "201 Created",
|
||||
Location = location
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
response.Entry.Add(new Bundle.EntryComponent
|
||||
{
|
||||
Response = new Bundle.ResponseComponent
|
||||
{
|
||||
Status = "422 Unprocessable Entity",
|
||||
Outcome = FhirOperationOutcomeBuilder.FromException(ex)
|
||||
}
|
||||
});
|
||||
break; // transaction semantics — stop on first failure
|
||||
}
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
private static int Priority(Resource? r) => r switch
|
||||
{
|
||||
Hl7.Fhir.Model.Patient => 0,
|
||||
Hl7.Fhir.Model.Encounter => 1,
|
||||
_ => 2
|
||||
};
|
||||
|
||||
private async Task<string> ProcessPatientAsync(Hl7.Fhir.Model.Patient fhir)
|
||||
{
|
||||
var req = _patientMapper.ToUpsertRequest(fhir);
|
||||
var patient = await _patients.RegisterOrUpdateByIdentifierAsync(req);
|
||||
return $"Patient/{patient.Id}";
|
||||
}
|
||||
|
||||
private async Task<string> ProcessEncounterAsync(Hl7.Fhir.Model.Encounter fhir)
|
||||
{
|
||||
var req = await _encounterMapper.ToUpsertRequestAsync(fhir);
|
||||
var encounter = await _encounters.OpenOrUpdateByIdentifierAsync(req);
|
||||
return $"Encounter/{encounter.Id}";
|
||||
}
|
||||
|
||||
private async Task<string> ProcessObservationAsync(Hl7.Fhir.Model.Observation fhir)
|
||||
{
|
||||
var mapped = await _observationMapper.ToIngestRequestsAsync(fhir);
|
||||
Guid? lastId = null;
|
||||
foreach (var item in mapped)
|
||||
{
|
||||
var result = await _observations.IngestAsync(item.EncounterId, item.Request);
|
||||
lastId = result.Observation.Id;
|
||||
}
|
||||
return $"Observation/{lastId}";
|
||||
}
|
||||
|
||||
private async Task<string> ProcessMedAsync(Hl7.Fhir.Model.MedicationAdministration fhir)
|
||||
{
|
||||
var (req, encounterId) = await _medMapper.ToCreateRequestAsync(fhir);
|
||||
var med = await _medications.CreateAsync(encounterId, req);
|
||||
return $"MedicationAdministration/{med.Id}";
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user