hapi-fhir-structures-r5 là thư viện Java cung cấp các lớp mô hình (model classes) cho phiên bản FHIR R5 (phiên bản 5.0.0), phát hành chính thức vào đầu năm 2023. Thư viện này triển khai đầy đủ các resource, datatype và các thành phần khác được định nghĩa trong đặc tả FHIR R5, cho phép nhà phát triển làm việc với FHIR R5 một cách tự nhiên trong môi trường Java.
FHIR R5 là phiên bản hiện đại nhất của tiêu chuẩn FHIR, mang đến nhiều cải tiến và bổ sung quan trọng so với các phiên bản trước đó. Thư viện hapi-fhir-structures-r5 phản ánh đầy đủ các thay đổi này và cung cấp các API để thao tác với các cấu trúc dữ liệu FHIR R5.
Đặc điểm chính
Package Structure
Các lớp model trong hapi-fhir-structures-r5 được tổ chức trong package org.hl7.fhir.r5.model. Cấu trúc package này tuân theo cấu trúc được định nghĩa bởi HL7, giúp nhất quán với các triển khai tham chiếu chính thức.
Các loại lớp cơ bản
Thư viện bao gồm các loại lớp chính sau:
Resource Classes: Đại diện cho các FHIR resources (ví dụ: Patient, Observation, Encounter)
Datatype Classes: Đại diện cho các FHIR datatypes (ví dụ: HumanName, Address, Coding)
Enumeration Classes: Đại diện cho các giá trị enum được định nghĩa trong FHIR
Base Classes: Các lớp cơ sở cung cấp chức năng chung cho các resource và datatype
Resources mới trong R5
FHIR R5 giới thiệu nhiều resource mới không có trong R4, và tất cả đều được triển khai đầy đủ trong hapi-fhir-structures-r5:
InventoryReport: Quản lý báo cáo hàng tồn kho
Requirements: Xác định các yêu cầu cho hệ thống, phần mềm
TestPlan: Quản lý kế hoạch kiểm thử
TestScript: Quản lý kịch bản kiểm thử
SubscriptionStatus: Quản lý trạng thái subscription
SubscriptionTopic: Định nghĩa topic cho subscription
CitationStatus: Trạng thái của citation
Evidence: Bằng chứng khoa học
EvidenceReport: Báo cáo bằng chứng
ChargeItemDefinition: Định nghĩa các mục tính phí
Các thay đổi và cải tiến quan trọng
FHIR R5 có nhiều thay đổi đáng kể so với R4, và hapi-fhir-structures-r5 phản ánh đầy đủ các thay đổi này:
Cải tiến vòng đời của resources: Trạng thái và quản lý vòng đời resources được cải thiện
Mở rộng các datatype: Thêm các field và tính năng mới cho các datatype
Cải thiện terminology: Bổ sung và cải tiến các cơ chế terminologies
Cập nhật search parameters: Cải tiến và thêm các search parameters mới
Extensions được chuẩn hóa: Nhiều extensions phổ biến đã trở thành phần chính thức của model
Cài đặt và sử dụng
Thêm vào Maven Project
<!-- Thư viện cơ sở - luôn cần thiết -->
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-base</artifactId>
<version>6.4.0</version>
</dependency>
<!-- Thư viện structures cho FHIR R5 -->
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-r5</artifactId>
<version>6.4.0</version>
</dependency>
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.parser.IParser;
import org.hl7.fhir.r5.model.*;
import java.util.Date;
public class PatientExample {
public static void main(String[] args) {
// Khởi tạo FhirContext cho FHIR R5
FhirContext ctx = FhirContext.forR5();
// Tạo Patient resource
Patient patient = new Patient();
// Thêm identifier (mã bệnh nhân)
Identifier identifier = patient.addIdentifier();
identifier.setSystem("http://hospital.example.org/patients");
identifier.setValue("123456");
// Thêm tên bệnh nhân
HumanName name = patient.addName();
name.setFamily("Nguyễn");
name.addGiven("Văn");
name.addGiven("A");
name.setUse(HumanName.NameUse.OFFICIAL);
// Thêm ngày sinh
patient.setBirthDate(new Date());
// Thêm giới tính
patient.setGender(Enumerations.AdministrativeGender.MALE);
// Thêm địa chỉ
Address address = patient.addAddress();
address.addLine("123 Đường Lê Lợi");
address.setCity("Hà Nội");
address.setCountry("VN");
address.setUse(Address.AddressUse.HOME);
// Thêm thông tin liên hệ
ContactPoint phone = patient.addTelecom();
phone.setSystem(ContactPoint.ContactPointSystem.PHONE);
phone.setValue("0123456789");
phone.setUse(ContactPoint.ContactPointUse.MOBILE);
// Thiết lập trạng thái active
patient.setActive(true);
// Thêm liên hệ khẩn cấp (đặc trưng R5)
Patient.ContactComponent contact = patient.addContact();
contact.addRelationship().addCoding()
.setSystem("http://terminology.hl7.org/CodeSystem/v2-0131")
.setCode("C")
.setDisplay("Emergency Contact");
HumanName contactName = contact.setName(new HumanName());
contactName.setFamily("Trần");
contactName.addGiven("Thị B");
ContactPoint contactPhone = contact.addTelecom();
contactPhone.setSystem(ContactPoint.ContactPointSystem.PHONE);
contactPhone.setValue("0987654321");
// Thêm quốc tịch (tính năng mới trong R5)
Patient.CitizenshipComponent citizenship = patient.addCitizenship();
CodeableConcept citizenshipCode = citizenship.setCode(new CodeableConcept());
citizenshipCode.addCoding()
.setSystem("urn:iso:std:iso:3166")
.setCode("VN")
.setDisplay("Vietnam");
// Chuyển đổi thành JSON
IParser parser = ctx.newJsonParser().setPrettyPrint(true);
String json = parser.encodeResourceToString(patient);
System.out.println(json);
}
}
Ví dụ 2: Tạo Observation Resource
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.parser.IParser;
import org.hl7.fhir.r5.model.*;
import java.util.Date;
public class ObservationExample {
public static void main(String[] args) {
// Khởi tạo FhirContext cho FHIR R5
FhirContext ctx = FhirContext.forR5();
// Tạo Observation resource
Observation observation = new Observation();
// Thiết lập identifier
observation.addIdentifier()
.setSystem("http://lab.example.org/observations")
.setValue("OBS12345");
// Thiết lập status
observation.setStatus(Observation.ObservationStatus.FINAL);
// Thiết lập category
CodeableConcept category = observation.addCategory();
category.addCoding()
.setSystem("http://terminology.hl7.org/CodeSystem/observation-category")
.setCode("vital-signs")
.setDisplay("Vital Signs");
// Thiết lập code (loại observation)
CodeableConcept code = observation.getCode();
code.addCoding()
.setSystem("http://loinc.org")
.setCode("8867-4")
.setDisplay("Heart rate");
code.setText("Nhịp tim");
// Thiết lập subject (bệnh nhân)
observation.setSubject(new Reference("Patient/123456"));
// Thiết lập thời gian thực hiện
observation.setEffective(new DateTimeType(new Date()));
observation.setIssued(new Date());
// Thiết lập performer (người thực hiện)
observation.addPerformer(new Reference("Practitioner/987654"));
// Thiết lập kết quả (giá trị)
Quantity value = new Quantity();
value.setValue(80);
value.setUnit("beats/minute");
value.setSystem("http://unitsofmeasure.org");
value.setCode("/min");
observation.setValue(value);
// Thiết lập range tham chiếu
Observation.ObservationReferenceRangeComponent range = observation.addReferenceRange();
range.setLow(new Quantity().setValue(60).setUnit("beats/minute"));
range.setHigh(new Quantity().setValue(100).setUnit("beats/minute"));
range.setText("60-100 bpm");
// Thêm thông tin về phương pháp
CodeableConcept method = observation.setMethod(new CodeableConcept());
method.addCoding()
.setSystem("http://terminology.hl7.org/CodeSystem/v2-0936")
.setCode("AUSCULTATE")
.setDisplay("Auscultation");
// Thêm thông tin về thiết bị đo
observation.setDevice(new Reference("Device/sphygmomanometer"));
// Thêm note
Annotation note = observation.addNote();
note.setText("Bệnh nhân ổn định, không có triệu chứng bất thường");
note.setTime(new Date());
note.setAuthor(new Reference("Practitioner/987654"));
// Thêm components (tính năng nâng cao trong R5)
Observation.ObservationComponentComponent systolicComponent = observation.addComponent();
systolicComponent.getCode().addCoding()
.setSystem("http://loinc.org")
.setCode("8480-6")
.setDisplay("Systolic blood pressure");
systolicComponent.setValue(new Quantity().setValue(120).setUnit("mmHg"));
Observation.ObservationComponentComponent diastolicComponent = observation.addComponent();
diastolicComponent.getCode().addCoding()
.setSystem("http://loinc.org")
.setCode("8462-4")
.setDisplay("Diastolic blood pressure");
diastolicComponent.setValue(new Quantity().setValue(80).setUnit("mmHg"));
// Chuyển đổi thành JSON
IParser parser = ctx.newJsonParser().setPrettyPrint(true);
String json = parser.encodeResourceToString(observation);
System.out.println(json);
}
}
Ví dụ 3: Tạo Encounter Resource
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.parser.IParser;
import org.hl7.fhir.r5.model.*;
import java.text.SimpleDateFormat;
import java.util.Date;
public class EncounterExample {
public static void main(String[] args) throws Exception {
// Khởi tạo FhirContext cho FHIR R5
FhirContext ctx = FhirContext.forR5();
// Tạo Encounter resource
Encounter encounter = new Encounter();
// Thiết lập identifier
encounter.addIdentifier()
.setSystem("http://hospital.example.org/encounters")
.setValue("ENC12345");
// Thiết lập status
encounter.setStatus(Encounter.EncounterStatus.INPROGRESS);
// Thiết lập class
encounter.setClass_(new Coding()
.setSystem("http://terminology.hl7.org/CodeSystem/v3-ActCode")
.setCode("AMB")
.setDisplay("ambulatory"));
// Thiết lập type
CodeableConcept type = encounter.addType();
type.addCoding()
.setSystem("http://terminology.hl7.org/CodeSystem/encounter-type")
.setCode("CHECKUP")
.setDisplay("Routine check-up");
// Thiết lập subject (bệnh nhân)
encounter.setSubject(new Reference("Patient/123456"));
// Thiết lập thời gian
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
Date startDate = dateFormat.parse("2023-05-15T09:00:00");
Period period = new Period();
period.setStart(startDate);
period.setEnd(new Date(startDate.getTime() + 3600000)); // 1 giờ sau
encounter.setPeriod(period);
// Thiết lập service provider (cơ sở y tế)
encounter.setServiceProvider(new Reference("Organization/hospital123"));
// Thiết lập location
Encounter.EncounterLocationComponent location = encounter.addLocation();
location.setLocation(new Reference("Location/clinic-outpatient"));
location.setStatus(Encounter.EncounterLocationStatus.ACTIVE);
// Thiết lập participant (người tham gia)
Encounter.EncounterParticipantComponent doctor = encounter.addParticipant();
doctor.addType().addCoding()
.setSystem("http://terminology.hl7.org/CodeSystem/v3-ParticipationType")
.setCode("PPRF")
.setDisplay("primary performer");
doctor.setIndividual(new Reference("Practitioner/doctor123"));
// Thiết lập reason (lý do khám)
CodeableConcept reason = encounter.addReasonCode();
reason.addCoding()
.setSystem("http://snomed.info/sct")
.setCode("162673000")
.setDisplay("General examination of patient");
reason.setText("Khám sức khỏe định kỳ");
// Thiết lập diagnosis
Encounter.DiagnosisComponent diagnosis = encounter.addDiagnosis();
diagnosis.setCondition(new Reference("Condition/diabetes123"));
diagnosis.setUse(new CodeableConcept().addCoding()
.setSystem("http://terminology.hl7.org/CodeSystem/diagnosis-role")
.setCode("CC")
.setDisplay("Chief complaint"));
diagnosis.setRank(1);
// Thiết lập hospitalization (thông tin nhập viện) - tính năng đặc biệt của R5
Encounter.EncounterHospitalizationComponent hospitalization = encounter.getHospitalization();
hospitalization.setAdmitSource(new CodeableConcept().addCoding()
.setSystem("http://terminology.hl7.org/CodeSystem/admit-source")
.setCode("gp")
.setDisplay("General Practitioner referral"));
// Thiết lập appointment
encounter.setAppointment(new Reference("Appointment/appt123"));
// Chuyển đổi thành JSON
IParser parser = ctx.newJsonParser().setPrettyPrint(true);
String json = parser.encodeResourceToString(encounter);
System.out.println(json);
}
}
Ví dụ 4: Sử dụng Extension
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.parser.IParser;
import org.hl7.fhir.r5.model.*;
public class ExtensionExample {
public static void main(String[] args) {
// Khởi tạo FhirContext cho FHIR R5
FhirContext ctx = FhirContext.forR5();
// Tạo Patient resource
Patient patient = new Patient();
patient.addName().setFamily("Nguyễn").addGiven("Văn A");
// Thêm extension đơn giản - nhóm máu
Extension bloodTypeExtension = new Extension();
bloodTypeExtension.setUrl("http://example.org/fhir/StructureDefinition/blood-type");
CodeableConcept bloodType = new CodeableConcept();
bloodType.addCoding()
.setSystem("http://terminology.hl7.org/CodeSystem/v3-ObservationValue")
.setCode("BLD-A")
.setDisplay("Blood group A");
bloodTypeExtension.setValue(bloodType);
patient.addExtension(bloodTypeExtension);
// Thêm extension với giá trị đơn giản
Extension heightExtension = new Extension();
heightExtension.setUrl("http://example.org/fhir/StructureDefinition/height");
heightExtension.setValue(new Quantity().setValue(170).setUnit("cm"));
patient.addExtension(heightExtension);
// Thêm extension phức tạp - thông tin sinh trắc học
Extension biometricsExtension = new Extension();
biometricsExtension.setUrl("http://example.org/fhir/StructureDefinition/biometrics");
// Thông tin vân tay
Extension fingerprintExtension = new Extension();
fingerprintExtension.setUrl("fingerprint");
fingerprintExtension.setValue(new Attachment()
.setContentType("image/png")
.setUrl("http://example.org/fingerprints/patient123.png"));
biometricsExtension.addExtension(fingerprintExtension);
// Thông tin mống mắt
Extension irisExtension = new Extension();
irisExtension.setUrl("iris");
irisExtension.setValue(new BooleanType(true));
biometricsExtension.addExtension(irisExtension);
patient.addExtension(biometricsExtension);
// Thêm extension trực tiếp vào field cụ thể (modifierExtension)
HumanName name = patient.getNameFirstRep();
Extension nameValidityExtension = new Extension();
nameValidityExtension.setUrl("http://example.org/fhir/StructureDefinition/name-validity");
nameValidityExtension.setValue(new DateTimeType("2023-01-01"));
name.addExtension(nameValidityExtension);
// Thêm modifier extension
Extension vipExtension = new Extension();
vipExtension.setUrl("http://example.org/fhir/StructureDefinition/patient-vip");
vipExtension.setValue(new BooleanType(true));
patient.addModifierExtension(vipExtension);
// Chuyển đổi thành JSON
IParser parser = ctx.newJsonParser().setPrettyPrint(true);
String json = parser.encodeResourceToString(patient);
System.out.println(json);
}
}
Ví dụ 5: Tạo MedicationRequest với các tính năng đặc biệt của R5
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.parser.IParser;
import org.hl7.fhir.r5.model.*;
import java.math.BigDecimal;
import java.util.Date;
public class MedicationRequestExample {
public static void main(String[] args) {
// Khởi tạo FhirContext cho FHIR R5
FhirContext ctx = FhirContext.forR5();
// Tạo MedicationRequest resource
MedicationRequest medicationRequest = new MedicationRequest();
// Thiết lập identifier
medicationRequest.addIdentifier()
.setSystem("http://hospital.example.org/prescriptions")
.setValue("PRE12345");
// Thiết lập status
medicationRequest.setStatus(MedicationRequest.MedicationRequestStatus.ACTIVE);
// Thiết lập intent
medicationRequest.setIntent(MedicationRequest.MedicationRequestIntent.ORDER);
// Thiết lập thuốc
CodeableConcept medication = new CodeableConcept();
medication.addCoding()
.setSystem("http://www.nlm.nih.gov/research/umls/rxnorm")
.setCode("211307")
.setDisplay("Paracetamol 500mg tablet");
medication.setText("Paracetamol 500mg");
medicationRequest.setMedication(medication);
// Thiết lập bệnh nhân
medicationRequest.setSubject(new Reference("Patient/123456"));
// Thiết lập ngày kê đơn
medicationRequest.setAuthoredOn(new Date());
// Thiết lập bác sĩ kê đơn
medicationRequest.setRequester(new Reference("Practitioner/doctor123"));
// Thiết lập encounter
medicationRequest.setEncounter(new Reference("Encounter/enc12345"));
// Thiết lập lý do kê đơn
CodeableConcept reasonCode = medicationRequest.addReasonCode();
reasonCode.addCoding()
.setSystem("http://snomed.info/sct")
.setCode("386661006")
.setDisplay("Fever");
reasonCode.setText("Sốt");
// Thiết lập hướng dẫn dùng thuốc
Dosage dosage = medicationRequest.addDosageInstruction();
// Cách sử dụng
dosage.setText("Uống 1 viên mỗi 6 giờ khi cần thiết để giảm đau hoặc sốt. Không vượt quá 4 viên trong 24 giờ.");
// Liều lượng
Dosage.DosageDoseAndRateComponent doseAndRate = dosage.addDoseAndRate();
doseAndRate.setType(new CodeableConcept().addCoding()
.setSystem("http://terminology.hl7.org/CodeSystem/dose-rate-type")
.setCode("ordered")
.setDisplay("Ordered"));
Quantity dose = new Quantity();
dose.setValue(1);
dose.setUnit("tablet");
dose.setSystem("http://terminology.hl7.org/CodeSystem/v3-orderableDrugForm");
dose.setCode("TAB");
doseAndRate.setDose(dose);
// Tần suất sử dụng
Timing timing = new Timing();
timing.setRepeat(new Timing.TimingRepeatComponent()
.setFrequency(1)
.setPeriod(6)
.setPeriodUnit(Timing.UnitsOfTime.H)
.setWhen(Timing.EventTiming.HS));
dosage.setTiming(timing);
// Đường dùng
dosage.setRoute(new CodeableConcept().addCoding()
.setSystem("http://snomed.info/sct")
.setCode("26643006")
.setDisplay("Oral route"));
// Liều tối đa
Dosage.DosageDoseAndRateComponent maxDosePerDay = dosage.addDoseAndRate();
maxDosePerDay.setType(new CodeableConcept().addCoding()
.setSystem("http://terminology.hl7.org/CodeSystem/dose-rate-type")
.setCode("maximum")
.setDisplay("Maximum"));
Quantity maxDose = new Quantity();
maxDose.setValue(4);
maxDose.setUnit("tablet");
maxDose.setSystem("http://terminology.hl7.org/CodeSystem/v3-orderableDrugForm");
maxDose.setCode("TAB");
maxDosePerDay.setDose(maxDose);
// Thiết lập thông tin cấp phát (tính năng mới trong R5)
MedicationRequest.MedicationRequestDispenseRequestComponent dispenseRequest = medicationRequest.getDispenseRequest();
// Số lượng
Quantity quantity = new Quantity();
quantity.setValue(20);
quantity.setUnit("tablet");
quantity.setSystem("http://terminology.hl7.org/CodeSystem/v3-orderableDrugForm");
quantity.setCode("TAB");
dispenseRequest.setQuantity(quantity);
// Thời gian cấp thuốc
Period dispenseInterval = new Period();
dispenseInterval.setStart(new Date());
// Thêm 10 ngày
dispenseInterval.setEnd(new Date(System.currentTimeMillis() + 10*24*60*60*1000L));
dispenseRequest.setValidityPeriod(dispenseInterval);
// Số lần tái cấp
dispenseRequest.setNumberOfRepeatsAllowed(2);
// Thiết lập priority (tính năng đặc biệt của R5)
medicationRequest.setPriority(MedicationRequest.MedicationRequestPriority.ROUTINE);
// Thêm category (tính năng đặc biệt của R5)
medicationRequest.addCategory().addCoding()
.setSystem("http://terminology.hl7.org/CodeSystem/medicationrequest-category")
.setCode("outpatient")
.setDisplay("Outpatient");
// Thêm note
medicationRequest.addNote()
.setText("Bệnh nhân có tiền sử dị ứng với aspirin")
.setTime(new Date())
.setAuthor(new Reference("Practitioner/doctor123"));
// Substitution - cho phép thay thế thuốc generic (tính năng đặc biệt R5)
MedicationRequest.MedicationRequestSubstitutionComponent substitution = medicationRequest.getSubstitution();
substitution.setAllowed(new BooleanType(true));
substitution.setReason(new CodeableConcept().addCoding()
.setSystem("http://terminology.hl7.org/CodeSystem/v3-ActReason")
.setCode("FP")
.setDisplay("formulary policy"));
// Course of Therapy Type (tính năng đặc biệt R5)
medicationRequest.setCourseOfTherapyType(new CodeableConcept().addCoding()
.setSystem("http://terminology.hl7.org/CodeSystem/medicationrequest-course-of-therapy")
.setCode("acute")
.setDisplay("Short term"));
// Chuyển đổi thành JSON
IParser parser = ctx.newJsonParser().setPrettyPrint(true);
String json = parser.encodeResourceToString(medicationRequest);
System.out.println(json);
}
}
Đặc điểm nâng cao của FHIR R5 trong hapi-fhir-structures-r5
1. Resource Lifecycle Management
FHIR R5 đã cải thiện đáng kể việc quản lý vòng đời của resource thông qua các trạng thái và các tính năng mới. Trong hapi-fhir-structures-r5, điều này được thể hiện qua:
Status histories
Version tracking
Improved status enumerations
Lifecycle events
2. Enhanced Extensions
FHIR R5 đã chuẩn hóa nhiều extensions phổ biến thành các field chính thức trong resource, và hapi-fhir-structures-r5 phản ánh đầy đủ các thay đổi này:
Extensions để biểu thị ethnicity và race
Extensions về citizenship
Extensions về gender identity
3. Canonical References
R5 giới thiệu khái niệm canonical references, và hapi-fhir-structures-r5 triển khai đầy đủ tính năng này:
// Sử dụng canonical reference trong R5
CanonicalType canonicalRef = new CanonicalType("http://example.org/fhir/StructureDefinition/my-profile|1.0.0");
resource.getType().addProfile(canonicalRef);
Canonical references cho phép tham chiếu chính xác đến các artifact có URL và phiên bản cụ thể.
4. Improved Search Parameters
FHIR R5 đã cải thiện và mở rộng các search parameters, và hapi-fhir-structures-r5 đã cập nhật các model class để phản ánh những thay đổi này:
// Sử dụng enhanced search parameters trong Resource class
SearchParameter searchParam = new SearchParameter();
searchParam.setName("patient-name");
searchParam.setDescription("Search by patient name");
searchParam.setCode("name");
searchParam.setType(Enumerations.SearchParamType.STRING);
searchParam.setExpression("Patient.name");
5. Enhanced Terminology Support
R5 có hỗ trợ terminology mạnh mẽ hơn, và hapi-fhir-structures-r5 bao gồm:
Cải tiến trong CodeSystem và ValueSet
Concept maps và translations
Subsumption checking (kiểm tra quan hệ phân cấp)
6. Subscription Resources
FHIR R5 giới thiệu các resources mới cho subscription framework, và tất cả đều có trong hapi-fhir-structures-r5:
// Tạo SubscriptionTopic resource
SubscriptionTopic topic = new SubscriptionTopic();
topic.setUrl("http://example.org/fhir/SubscriptionTopic/patient-admission");
topic.setTitle("Patient Admission Notifications");
topic.setStatus(Enumerations.PublicationStatus.ACTIVE);
// Định nghĩa trigger
SubscriptionTopic.SubscriptionTopicTriggerComponent trigger = topic.addTrigger();
trigger.setType(SubscriptionTopic.SubscriptionTriggerType.RESOURCEUPDATE);
trigger.setResource("Encounter");
trigger.setContextFilterElement(new FHIRPathType("status = 'arrived' or status = 'triaged'"));
// Tạo Subscription sử dụng topic này
Subscription subscription = new Subscription();
subscription.setTopic("http://example.org/fhir/SubscriptionTopic/patient-admission");
subscription.setReason("Notify on patient admissions");
subscription.setStatus(Subscription.SubscriptionStatus.ACTIVE);
// Cấu hình endpoint
Subscription.SubscriptionChannelComponent channel = subscription.getChannel();
channel.setType(Subscription.SubscriptionChannelType.WEBSOCKET);
channel.setEndpoint("wss://example.org/fhir/subscription");
Cấu trúc package của hapi-fhir-structures-r5
Thư viện hapi-fhir-structures-r5 có cấu trúc package như sau:
org.hl7.fhir.r5.model: Package chính chứa tất cả các model classes
Patient, Observation, Encounter, etc.: Các resource classes
HumanName, Address, CodeableConcept, etc.: Các datatype classes
Enumerations.java: Chứa các enumerations
org.hl7.fhir.r5.utils: Chứa các utility classes cho model R5
FhirPathEngine: Triển khai FHIRPath cho R5
ValidationEngine: Engine validation cho R5
org.hl7.fhir.r5.formats: Chứa các formatter và parser cấp thấp
XmlParser: Parser XML cấp thấp
JsonParser: Parser JSON cấp thấp
org.hl7.fhir.r5.terminologies: Chứa các utility cho terminology
ValueSetExpansionEngine: Engine mở rộng ValueSet
CodeSystemEngine: Engine làm việc với CodeSystem
Tích hợp với Spring Boot
Khi sử dụng hapi-fhir-structures-r5 trong dự án Spring Boot, ta có thể tích hợp như sau:
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.parser.IParser;
import org.hl7.fhir.r5.model.Patient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FhirConfig {
@Bean
public FhirContext fhirContext() {
return FhirContext.forR5();
}
@Bean
public IParser jsonParser(FhirContext fhirContext) {
return fhirContext.newJsonParser().setPrettyPrint(true);
}
@Bean
public IParser xmlParser(FhirContext fhirContext) {
return fhirContext.newXmlParser().setPrettyPrint(true);
}
}
Và sau đó sử dụng trong các service:
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.parser.IParser;
import org.hl7.fhir.r5.model.Patient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class PatientService {
private final FhirContext fhirContext;
private final IParser jsonParser;
@Autowired
public PatientService(FhirContext fhirContext, IParser jsonParser) {
this.fhirContext = fhirContext;
this.jsonParser = jsonParser;
}
public Patient createPatient(String familyName, String givenName) {
Patient patient = new Patient();
patient.addName().setFamily(familyName).addGiven(givenName);
return patient;
}
public String serializePatient(Patient patient) {
return jsonParser.encodeResourceToString(patient);
}
public Patient parsePatient(String json) {
return jsonParser.parseResource(Patient.class, json);
}
}
Làm việc với Bundle và Transaction
Trong FHIR R5, Bundle được sử dụng để nhóm các resource lại với nhau cho các mục đích khác nhau (transaction, message, document, etc.). Dưới đây là ví dụ về việc tạo và xử lý Bundle transaction với hapi-fhir-structures-r5:
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.parser.IParser;
import org.hl7.fhir.r5.model.*;
import java.util.Date;
public class BundleTransactionExample {
public static void main(String[] args) {
// Khởi tạo context
FhirContext ctx = FhirContext.forR5();
// Tạo Bundle kiểu transaction
Bundle bundle = new Bundle();
bundle.setType(Bundle.BundleType.TRANSACTION);
bundle.setTimestamp(new Date());
// 1. Tạo Patient resource
Patient patient = new Patient();
patient.addIdentifier()
.setSystem("http://hospital.example.org/patients")
.setValue("12345");
patient.addName()
.setFamily("Nguyễn")
.addGiven("Văn")
.addGiven("A");
// Thêm Patient vào bundle với HTTP POST operation
Bundle.BundleEntryComponent patientEntry = bundle.addEntry();
patientEntry.setResource(patient);
patientEntry.getRequest()
.setMethod(Bundle.HTTPVerb.POST)
.setUrl("Patient");
// 2. Tạo Encounter resource
Encounter encounter = new Encounter();
encounter.setStatus(Encounter.EncounterStatus.INPROGRESS);
encounter.setClass_(new Coding()
.setSystem("http://terminology.hl7.org/CodeSystem/v3-ActCode")
.setCode("AMB")
.setDisplay("ambulatory"));
// Tham chiếu đến Patient đã tạo trước đó
// Sử dụng conditional reference
encounter.setSubject(new Reference("Patient?identifier=http://hospital.example.org/patients|12345"));
// Thêm Encounter vào bundle với HTTP POST operation
Bundle.BundleEntryComponent encounterEntry = bundle.addEntry();
encounterEntry.setResource(encounter);
encounterEntry.getRequest()
.setMethod(Bundle.HTTPVerb.POST)
.setUrl("Encounter");
// 3. Tạo Observation resource
Observation observation = new Observation();
observation.setStatus(Observation.ObservationStatus.FINAL);
observation.addCategory().addCoding()
.setSystem("http://terminology.hl7.org/CodeSystem/observation-category")
.setCode("vital-signs")
.setDisplay("Vital Signs");
observation.getCode().addCoding()
.setSystem("http://loinc.org")
.setCode("8867-4")
.setDisplay("Heart rate");
// Tham chiếu đến Patient
observation.setSubject(new Reference("Patient?identifier=http://hospital.example.org/patients|12345"));
// Tham chiếu đến Encounter
observation.setEncounter(new Reference("Encounter/urn:uuid:" + encounterEntry.getResource().getId()));
// Thiết lập giá trị
Quantity heartRate = new Quantity()
.setValue(80)
.setUnit("beats/minute")
.setSystem("http://unitsofmeasure.org")
.setCode("/min");
observation.setValue(heartRate);
// Thêm Observation vào bundle với HTTP POST operation
Bundle.BundleEntryComponent observationEntry = bundle.addEntry();
observationEntry.setResource(observation);
observationEntry.getRequest()
.setMethod(Bundle.HTTPVerb.POST)
.setUrl("Observation");
// Chuyển đổi bundle thành JSON
IParser parser = ctx.newJsonParser().setPrettyPrint(true);
String json = parser.encodeResourceToString(bundle);
System.out.println("Bundle Transaction JSON:");
System.out.println(json);
}
}
Sử dụng FHIRPath với hapi-fhir-structures-r5
FHIR R5 cải thiện đáng kể sự hỗ trợ cho FHIRPath, và hapi-fhir-structures-r5 cung cấp API để làm việc với FHIRPath:
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.fhirpath.IFhirPath;
import org.hl7.fhir.r5.model.*;
import java.util.List;
public class FhirPathExample {
public static void main(String[] args) {
// Khởi tạo context
FhirContext ctx = FhirContext.forR5();
// Tạo FHIRPath engine
IFhirPath fhirPath = ctx.newFhirPath();
// Tạo Patient với nhiều tên và địa chỉ
Patient patient = new Patient();
// Tên chính thức
patient.addName()
.setUse(HumanName.NameUse.OFFICIAL)
.setFamily("Nguyễn")
.addGiven("Văn")
.addGiven("A");
// Biệt danh
patient.addName()
.setUse(HumanName.NameUse.NICKNAME)
.addGiven("Tony");
// Địa chỉ
patient.addAddress()
.setUse(Address.AddressUse.HOME)
.addLine("123 Đường Lê Lợi")
.setCity("Hà Nội")
.setCountry("VN");
patient.addAddress()
.setUse(Address.AddressUse.WORK)
.addLine("456 Đường Nguyễn Huệ")
.setCity("Hồ Chí Minh")
.setCountry("VN");
// Sử dụng FHIRPath để truy vấn
// 1. Lấy tất cả tên họ
List<StringType> familyNames = fhirPath.evaluate(patient, "name.family", StringType.class);
System.out.println("Family names:");
for (StringType name : familyNames) {
System.out.println(" - " + name.getValue());
}
// 2. Lấy tất cả tên đầu tiên
List<StringType> givenNames = fhirPath.evaluate(patient, "name.given", StringType.class);
System.out.println("\nGiven names:");
for (StringType name : givenNames) {
System.out.println(" - " + name.getValue());
}
// 3. Lấy biệt danh
List<HumanName> nicknames = fhirPath.evaluate(patient, "name.where(use = 'nickname')", HumanName.class);
System.out.println("\nNicknames:");
for (HumanName name : nicknames) {
System.out.println(" - " + name.getGivenAsSingleString());
}
// 4. Lấy tất cả thành phố
List<StringType> cities = fhirPath.evaluate(patient, "address.city", StringType.class);
System.out.println("\nCities:");
for (StringType city : cities) {
System.out.println(" - " + city.getValue());
}
// 5. Kiểm tra xem bệnh nhân có địa chỉ ở Hà Nội không
List<BooleanType> hasHanoi = fhirPath.evaluate(patient, "address.where(city = 'Hà Nội').exists()", BooleanType.class);
System.out.println("\nHas address in Hanoi: " + hasHanoi.get(0).getValue());
// 6. Lấy địa chỉ nhà
List<Address> homeAddresses = fhirPath.evaluate(patient, "address.where(use = 'home')", Address.class);
System.out.println("\nHome addresses:");
for (Address address : homeAddresses) {
System.out.println(" - " + address.getLine().get(0).getValue() + ", " + address.getCity());
}
}
}
Sử dụng Contained Resources trong R5
FHIR R5 cải thiện cách xử lý contained resources, và hapi-fhir-structures-r5 cung cấp API để làm việc với chúng:
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.parser.IParser;
import org.hl7.fhir.r5.model.*;
public class ContainedResourceExample {
public static void main(String[] args) {
// Khởi tạo context
FhirContext ctx = FhirContext.forR5();
// Tạo Practitioner resource (bác sĩ)
Practitioner practitioner = new Practitioner();
practitioner.setId("#doctor-1");
practitioner.addName().setFamily("Trần").addGiven("Bác Sĩ");
practitioner.addIdentifier()
.setSystem("http://hospital.example.org/practitioners")
.setValue("MD12345");
// Tạo Organization resource (bệnh viện)
Organization hospital = new Organization();
hospital.setId("#hospital-1");
hospital.setName("Bệnh viện Đa khoa Trung ương");
hospital.addIdentifier()
.setSystem("http://hospital.example.org/organizations")
.setValue("ORG12345");
// Tạo MedicationRequest với contained resources
MedicationRequest medicationRequest = new MedicationRequest();
// Thêm các contained resources
medicationRequest.addContained(practitioner);
medicationRequest.addContained(hospital);
// Thiết lập thuộc tính cơ bản
medicationRequest.setStatus(MedicationRequest.MedicationRequestStatus.ACTIVE);
medicationRequest.setIntent(MedicationRequest.MedicationRequestIntent.ORDER);
// Tham chiếu đến contained practitioner
medicationRequest.setRequester(new Reference("#doctor-1"));
// Thêm extension với tham chiếu đến contained organization
Extension performerOrg = new Extension();
performerOrg.setUrl("http://example.org/fhir/StructureDefinition/performing-organization");
performerOrg.setValue(new Reference("#hospital-1"));
medicationRequest.addExtension(performerOrg);
// Tham chiếu đến bệnh nhân (không contained)
medicationRequest.setSubject(new Reference("Patient/123456"));
// Thiết lập thuốc
CodeableConcept medication = new CodeableConcept();
medication.addCoding()
.setSystem("http://www.nlm.nih.gov/research/umls/rxnorm")
.setCode("211307")
.setDisplay("Paracetamol 500mg tablet");
medicationRequest.setMedication(medication);
// Thêm hướng dẫn dùng thuốc
Dosage dosage = medicationRequest.addDosageInstruction();
dosage.setText("Uống 1 viên mỗi 6 giờ khi cần");
// Chuyển đổi thành JSON
IParser parser = ctx.newJsonParser().setPrettyPrint(true);
String json = parser.encodeResourceToString(medicationRequest);
System.out.println("MedicationRequest with Contained Resources:");
System.out.println(json);
// Truy cập contained resources từ MedicationRequest
System.out.println("\nAccessing contained resources:");
for (Resource contained : medicationRequest.getContained()) {
if (contained instanceof Practitioner) {
Practitioner doc = (Practitioner) contained;
System.out.println("Practitioner: " + doc.getNameFirstRep().getNameAsSingleString());
} else if (contained instanceof Organization) {
Organization org = (Organization) contained;
System.out.println("Organization: " + org.getName());
}
}
}
}
Validation trong FHIR R5
Validation là một tính năng quan trọng trong FHIR R5, và hapi-fhir-structures-r5 cung cấp sự hỗ trợ mạnh mẽ cho validation:
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.validation.FhirValidator;
import ca.uhn.fhir.validation.ValidationResult;
import org.hl7.fhir.r5.model.*;
import org.hl7.fhir.r5.hapi.validation.FhirInstanceValidator;
public class ValidationExample {
public static void main(String[] args) {
// Khởi tạo context
FhirContext ctx = FhirContext.forR5();
// Tạo validator
FhirValidator validator = ctx.newValidator();
// Thêm instance validator (kiểm tra cấu trúc và ràng buộc)
validator.registerValidatorModule(new FhirInstanceValidator(ctx));
// Tạo Patient resource hợp lệ
Patient validPatient = new Patient();
validPatient.addIdentifier()
.setSystem("http://hospital.example.org/patients")
.setValue("12345");
validPatient.addName()
.setFamily("Nguyễn")
.addGiven("Văn")
.addGiven("A");
validPatient.setGender(Enumerations.AdministrativeGender.MALE);
validPatient.setBirthDate(new DateType("1980-01-01").getValue());
// Validate patient hợp lệ
ValidationResult validResult = validator.validateWithResult(validPatient);
System.out.println("Valid Patient Validation:");
System.out.println("Is successful: " + validResult.isSuccessful());
System.out.println("Message count: " + validResult.getMessages().size());
validResult.getMessages().forEach(message ->
System.out.println(" - " + message.getSeverity() + ": " + message.getMessage()));
// Tạo Patient resource không hợp lệ (thiếu identifier, name)
Patient invalidPatient = new Patient();
// Không thêm identifier
// Thêm tên nhưng không có phần family name (bắt buộc)
invalidPatient.addName().addGiven("Invalid");
// Thiết lập giới tính với giá trị không hợp lệ
// invalidPatient.setGenderElement(new Enumeration<>(new EnumFactory<>()));
// Validate patient không hợp lệ
ValidationResult invalidResult = validator.validateWithResult(invalidPatient);
System.out.println("\nInvalid Patient Validation:");
System.out.println("Is successful: " + invalidResult.isSuccessful());
System.out.println("Message count: " + invalidResult.getMessages().size());
invalidResult.getMessages().forEach(message ->
System.out.println(" - " + message.getSeverity() + ": " + message.getMessage()));
}
}
So sánh giữa hapi-fhir-structures-r5 và các phiên bản trước
Tính năng
R4
R5
Canonical References
Chưa có
Được triển khai đầy đủ
Subscription Resources
Chỉ có Subscription
Thêm SubscriptionTopic, SubscriptionStatus
Citizenship
Chỉ có extension
Field chính thức trong Patient
Enhanced Search Parameters
Hỗ trợ cơ bản
Được mở rộng và cải tiến
Bundled Parameters
Hạn chế
Được mở rộng
FHIRPath Support
Hỗ trợ cơ bản
Được mở rộng
Resource Lifecycle
Đơn giản
Được cải tiến đáng kể
Kết luận
Thư viện hapi-fhir-structures-r5 cung cấp triển khai đầy đủ và mạnh mẽ cho chuẩn FHIR R5, mang đến cho nhà phát triển các công cụ cần thiết để xây dựng ứng dụng y tế hiện đại. Với việc bổ sung các resource mới, cải tiến các datatype hiện có, và cải thiện nhiều khía cạnh khác của FHIR, thư viện này là một thành phần thiết yếu cho bất kỳ dự án nào làm việc với FHIR R5.
Các lợi ích chính khi sử dụng hapi-fhir-structures-r5 bao gồm:
Triển khai đầy đủ chuẩn FHIR R5
API trực quan và dễ sử dụng
Tích hợp tốt với các thành phần khác của HAPI FHIR
Hỗ trợ mạnh mẽ cho validation
Hiệu suất tốt khi làm việc với large datasets
Khi xây dựng ứng dụng y tế hiện đại với FHIR R5, thư viện hapi-fhir-structures-r5 là một công cụ không thể thiếu, giúp nhà phát triển tập trung vào logic nghiệp vụ thay vì phải lo lắng về các chi tiết triển khai của chuẩn FHIR.