141 lines
4.7 KiB
PHP
141 lines
4.7 KiB
PHP
<?php
|
|
namespace App\Services;
|
|
|
|
/**
|
|
* Class FhirService
|
|
* HL7 FHIR R4 Interoperability Engine (Patient, Encounter, Observation, Procedure)
|
|
*
|
|
* @package App\Services
|
|
*/
|
|
class FhirService
|
|
{
|
|
private string $serverUrl;
|
|
private string $facilityId;
|
|
|
|
public function __construct()
|
|
{
|
|
$config = require __DIR__ . '/../../config/integration.php';
|
|
$fhir = $config['fhir'];
|
|
$this->serverUrl = rtrim($fhir['server_url'], '/');
|
|
$this->facilityId = $fhir['facility_id'] ?? 'THAI_MED_CENTER';
|
|
}
|
|
|
|
/**
|
|
* Build FHIR R4 Patient Resource
|
|
*/
|
|
public function buildPatientResource(array $patient): array
|
|
{
|
|
return [
|
|
'resourceType' => 'Patient',
|
|
'id' => (string)$patient['id'],
|
|
'identifier' => [
|
|
[
|
|
'use' => 'official',
|
|
'system' => 'https://www.dopa.go.th/cid',
|
|
'value' => $patient['cid'],
|
|
],
|
|
[
|
|
'use' => 'usual',
|
|
'system' => "https://his.hospital.local/hn/{$this->facilityId}",
|
|
'value' => $patient['hn'],
|
|
]
|
|
],
|
|
'active' => true,
|
|
'name' => [
|
|
[
|
|
'use' => 'official',
|
|
'text' => trim(($patient['first_name_th'] ?? '') . ' ' . ($patient['last_name_th'] ?? '')),
|
|
'family' => $patient['last_name_th'] ?? '',
|
|
'given' => [$patient['first_name_th'] ?? ''],
|
|
]
|
|
],
|
|
'gender' => strtolower($patient['gender'] ?? 'unknown'),
|
|
'birthDate' => $patient['birth_date'] ?? null,
|
|
'telecom' => [
|
|
[
|
|
'system' => 'phone',
|
|
'value' => $patient['phone'] ?? '',
|
|
'use' => 'mobile',
|
|
]
|
|
],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Build FHIR R4 Observation Resource for Pain Score (VAS 0-10)
|
|
*/
|
|
public function buildPainScoreObservation(int $patientId, int $encounterId, int $painScore, string $type = 'Pre-treatment'): array
|
|
{
|
|
return [
|
|
'resourceType' => 'Observation',
|
|
'status' => 'final',
|
|
'category' => [
|
|
[
|
|
'coding' => [
|
|
[
|
|
'system' => 'http://terminology.hl7.org/CodeSystem/observation-category',
|
|
'code' => 'exam',
|
|
'display' => 'Exam',
|
|
]
|
|
]
|
|
]
|
|
],
|
|
'code' => [
|
|
'coding' => [
|
|
[
|
|
'system' => 'http://loinc.org',
|
|
'code' => '72514-3',
|
|
'display' => 'Pain severity - 0-10 verbal numeric rating [Score] - Reported',
|
|
]
|
|
],
|
|
'text' => "VAS Pain Score ({$type})",
|
|
],
|
|
'subject' => [
|
|
'reference' => "Patient/{$patientId}",
|
|
],
|
|
'encounter' => [
|
|
'reference' => "Encounter/{$encounterId}",
|
|
],
|
|
'effectiveDateTime' => date('c'),
|
|
'valueInteger' => $painScore,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Export complete bundle to FHIR Server
|
|
*/
|
|
public function exportEncounterBundle(array $queue, array $soapNote): array
|
|
{
|
|
$patientRes = $this->buildPatientResource(['id' => $queue['patient_id'], 'cid' => $queue['cid'] ?? '', 'hn' => $queue['hn'] ?? '', 'first_name_th' => $queue['patient_name'] ?? '']);
|
|
|
|
$bundle = [
|
|
'resourceType' => 'Bundle',
|
|
'type' => 'transaction',
|
|
'entry' => [
|
|
[
|
|
'resource' => $patientRes,
|
|
'request' => ['method' => 'PUT', 'url' => "Patient/{$queue['patient_id']}"],
|
|
]
|
|
],
|
|
];
|
|
|
|
if (isset($soapNote['pre_pain_score'])) {
|
|
$obsPre = $this->buildPainScoreObservation((int)$queue['patient_id'], (int)$queue['id'], (int)$soapNote['pre_pain_score'], 'Pre-treatment');
|
|
$bundle['entry'][] = [
|
|
'resource' => $obsPre,
|
|
'request' => ['method' => 'POST', 'url' => "Observation"],
|
|
];
|
|
}
|
|
|
|
if (isset($soapNote['post_pain_score'])) {
|
|
$obsPost = $this->buildPainScoreObservation((int)$queue['patient_id'], (int)$queue['id'], (int)$soapNote['post_pain_score'], 'Post-treatment');
|
|
$bundle['entry'][] = [
|
|
'resource' => $obsPost,
|
|
'request' => ['method' => 'POST', 'url' => "Observation"],
|
|
];
|
|
}
|
|
|
|
return $bundle;
|
|
}
|
|
}
|