نظرة عامة موجزة
HL7 FHIR (مصادر تبادل معلومات الرعاية الصحية السريعة) هو المعيار الحديث لتبادل بيانات الرعاية الصحية، باستخدام واجهات برمجة تطبيقات RESTful مع استجابات JSON/XML. يوفر موارد موحدة للمرضى والملاحظات والأدوية والمزيد، مع مصادقة OAuth 2.0 وSMART على FHIR لدمج التطبيقات. يغطي هذا الدليل بنية FHIR وأنواع الموارد ومعلمات البحث والمصادقة واستراتيجيات التنفيذ في الإنتاج.
مقدمة
يكلف تفتت بيانات الرعاية الصحية نظام الرعاية الصحية الأمريكي 30 مليار دولار سنويًا. بالنسبة للمطورين الذين ينشئون تطبيقات الرعاية الصحية، فإن دمج واجهة برمجة تطبيقات HL7 FHIR ليس اختياريًا - إنه المعيار الصناعي الذي تفرضه CMS وتتبناه Epic وCerner وجميع بائعي السجلات الصحية الإلكترونية (EHR) الرئيسيين.
إليك الحقيقة: مقدمو الخدمات الذين يستخدمون تطبيقات تدعم FHIR يقللون وقت تنسيق الرعاية بنسبة 40% ويلغون 85% من طلبات السجلات القائمة على الفاكس. يتيح التكامل القوي لواجهة برمجة تطبيقات FHIR تبادل البيانات بسلاسة عبر السجلات الصحية الإلكترونية وبوابات المرضى ومنصات تنسيق الرعاية.
يرشدك هذا الدليل خلال عملية دمج واجهة برمجة تطبيقات HL7 FHIR بالكامل. ستتعرف على بنية FHIR وأنواع الموارد ومعلمات البحث ومصادقة OAuth 2.0 ودمج SMART على FHIR واستراتيجيات النشر في الإنتاج. بنهاية المطاف، سيكون لديك دمج FHIR جاهز للإنتاج.
ما هو HL7 FHIR؟
FHIR (مصادر تبادل معلومات الرعاية الصحية السريعة) هو إطار عمل معايير لتبادل معلومات الرعاية الصحية إلكترونيًا. تم تطويره بواسطة Health Level Seven International (HL7)، ويستخدم FHIR تقنيات الويب الحديثة بما في ذلك واجهات برمجة تطبيقات RESTful وJSON وXML وOAuth 2.0.

أنواع موارد FHIR
يحدد FHIR أكثر من 140 نوعًا من الموارد. تشمل الموارد الأساسية:
| المورد | الغرض | حالات الاستخدام الشائعة |
|---|---|---|
| المريض | المعلومات الديموغرافية | البحث عن المريض، التسجيل |
| الممارس الصحي | معلومات مقدم الخدمة | الدليل، الجدولة |
| اللقاء | الزيارات/القبول | حلقات الرعاية، الفوترة |
| الملاحظة | البيانات السريرية | العلامات الحيوية، نتائج المختبر، التقييمات |
| الحالة الصحية | المشاكل/التشخيصات | قوائم المشاكل، تخطيط الرعاية |
| طلب الدواء | الوصفات الطبية | الوصفات الإلكترونية، تاريخ الأدوية |
| الحساسية/عدم التحمل | الحساسيات | فحوصات السلامة، التنبيهات |
| التطعيم | التطعيمات | سجلات التطعيم |
| تقرير التشخيص | تقارير المختبر/التصوير | تسليم النتائج |
| مرجع المستند | المستندات السريرية | CCD، ملخصات الخروج |
بنية واجهة برمجة تطبيقات FHIR
يستخدم FHIR بنية واجهة برمجة تطبيقات RESTful:
https://fhir-server.com/fhir/{resourceType}/{id}
مقارنة إصدارات FHIR
| الإصدار | الحالة | حالة الاستخدام |
|---|---|---|
| R4 (4.0.1) | STU الحالي | تطبيقات الإنتاج |
| R4B (4.3) | تنفيذ تجريبي | المتبنون الأوائل |
| R5 (5.0.0) | مسودة STU | تطبيقات مستقبلية |
| DSTU2 | مهمل | الأنظمة القديمة فقط |
تتطلب CMS من السجلات الصحية الإلكترونية المعتمدة دعم FHIR R4 لواجهات برمجة تطبيقات وصول المرضى ووصول مقدمي الخدمة.
البدء: الوصول إلى خادم FHIR
الخطوة 1: اختر خادم FHIR الخاص بك
خيارات نشر خادم FHIR:
| الخادم | النوع | التكلفة | الأفضل لـ |
|---|---|---|---|
| Azure API for FHIR | مُدار | الدفع حسب الاستخدام | المؤسسات، عملاء Azure |
| AWS HealthLake | مُدار | الدفع حسب الاستخدام | بيئات AWS |
| Google Cloud Healthcare API | مُدار | الدفع حسب الاستخدام | بيئات GCP |
| HAPI FHIR | مفتوح المصدر | مستضاف ذاتيًا | النشر المخصص |
| Epic FHIR Server | تجاري | عملاء Epic | دمج سجلات Epic الصحية الإلكترونية |
| Cerner Ignite FHIR | تجاري | عملاء Cerner | دمج سجلات Cerner الصحية الإلكترونية |
الخطوة 2: الحصول على بيانات اعتماد الخادم
لخدمات FHIR السحابية:
# Azure API لـ FHIR
# 1. أنشئ خدمة FHIR في بوابة Azure
# 2. قم بتكوين المصادقة (OAuth 2.0 أو AAD)
# 3. احصل على نقطة نهاية FHIR: https://{service-name}.azurehealthcareapis.com
# 4. سجل تطبيق العميل لـ OAuth
# AWS HealthLake
# 1. أنشئ متجر بيانات في وحدة تحكم AWS
# 2. قم بتكوين أدوار IAM
# 3. احصل على نقطة النهاية: https://healthlake.{region}.amazonaws.com
الخطوة 3: فهم عمليات FHIR RESTful
يدعم FHIR أساليب HTTP القياسية:
| العملية | طريقة HTTP | نقطة النهاية | الوصف |
|---|---|---|---|
| قراءة | GET | /{resourceType}/{id} |
الحصول على مورد معين |
| بحث | GET | /{resourceType}?param=value |
البحث عن الموارد |
| إنشاء | POST | /{resourceType} |
إنشاء مورد جديد |
| تحديث | PUT | /{resourceType}/{id} |
استبدال المورد |
| تصحيح | PATCH | /{resourceType}/{id} |
تحديث جزئي |
| حذف | DELETE | /{resourceType}/{id} |
إزالة المورد |
| السجل | GET | /{resourceType}/{id}/_history |
إصدارات المورد |
الخطوة 4: إجراء أول اتصال FHIR
اختبار الاتصال:
curl -X GET "https://fhir-server.com/fhir/metadata" \
-H "Accept: application/fhir+json" \
-H "Authorization: Bearer {token}"
الاستجابة المتوقعة:
{
"resourceType": "CapabilityStatement",
"status": "active",
"date": "2026-03-25",
"fhirVersion": "4.0.1",
"rest": [{
"mode": "server",
"resource": [
{ "type": "Patient" },
{ "type": "Observation" },
{ "type": "Condition" }
]
}]
}
عمليات FHIR الأساسية
قراءة مورد المريض
جلب المريض بواسطة المعرف:
const FHIR_BASE_URL = process.env.FHIR_BASE_URL;
const FHIR_TOKEN = process.env.FHIR_TOKEN;
const fhirRequest = async (endpoint, options = {}) => {
const response = await fetch(`${FHIR_BASE_URL}/fhir${endpoint}`, {
...options,
headers: {
'Accept': 'application/fhir+json',
'Authorization': `Bearer ${FHIR_TOKEN}`,
...options.headers
}
});
if (!response.ok) {
const error = await response.json();
throw new Error(`FHIR Error: ${error.issue?.[0]?.details?.text || response.statusText}`);
}
return response.json();
};
// قراءة المريض بواسطة المعرف
const getPatient = async (patientId) => {
const patient = await fhirRequest(`/Patient/${patientId}`);
return patient;
};
// الاستخدام
const patient = await getPatient('12345');
console.log(`Patient: ${patient.name[0].given[0]} ${patient.name[0].family}`);
console.log(`DOB: ${patient.birthDate}`);
console.log(`Gender: ${patient.gender}`);
بنية مورد المريض
{
"resourceType": "Patient",
"id": "12345",
"identifier": [
{
"use": "usual",
"type": {
"coding": [{
"system": "http://terminology.hl7.org/CodeSystem/v2-0203",
"code": "MR"
}]
},
"system": "http://hospital.example.org",
"value": "MRN123456"
}
],
"name": [
{
"use": "official",
"family": "Smith",
"given": ["John", "Michael"]
}
],
"telecom": [
{
"system": "phone",
"value": "555-123-4567",
"use": "home"
},
{
"system": "email",
"value": "john.smith@email.com"
}
],
"gender": "male",
"birthDate": "1985-06-15",
"address": [
{
"use": "home",
"line": ["123 Main Street"],
"city": "Springfield",
"state": "IL",
"postalCode": "62701"
}
]
}
البحث عن الموارد
البحث عن المرضى بالاسم:
const searchPatients = async (searchParams) => {
const query = new URLSearchParams();
// إضافة معلمات البحث
if (searchParams.name) {
query.append('name', searchParams.name);
}
if (searchParams.birthDate) {
query.append('birthdate', searchParams.birthDate);
}
if (searchParams.identifier) {
query.append('identifier', searchParams.identifier);
}
if (searchParams.gender) {
query.append('gender', searchParams.gender);
}
const response = await fhirRequest(`/Patient?${query.toString()}`);
return response;
};
// الاستخدام
const results = await searchPatients({ name: 'Smith', birthDate: '1985-06-15' });
console.log(`Found ${results.total} patients`);
results.entry.forEach(entry => {
const patient = entry.resource;
console.log(`${patient.name[0].family}, ${patient.name[0].given[0]}`);
});
معلمات البحث الشائعة
| المورد | معلمات البحث | مثال |
|---|---|---|
| المريض | الاسم، تاريخ الميلاد، المعرف، الجنس، الهاتف، البريد الإلكتروني | ?name=Smith&birthdate=1985-06-15 |
| الملاحظة | المريض، الرمز، التاريخ، الفئة، الحالة | ?patient=123&code=8480-6&date=ge2026-01-01 |
| الحالة الصحية | المريض، الحالة السريرية، الفئة، تاريخ البدء | ?patient=123&clinical-status=active |
| طلب الدواء | المريض، الحالة، النية، الدواء | ?patient=123&status=active |
| اللقاء | المريض، التاريخ، الحالة، الفئة | ?patient=123&date=ge2026-01-01 |
| تقرير التشخيص | المريض، الفئة، التاريخ، الحالة | ?patient=123&category=laboratory |
معدّلات البحث
| المعدّل | الوصف | مثال |
|---|---|---|
:exact |
مطابقة تامة | name:exact=Smith |
:contains |
يحتوي على | name:contains=smi |
:missing |
قيمة موجودة/مفقودة | phone:missing=true |
: (بادئة) |
عوامل تشغيل البادئة | birthdate=ge1980-01-01 |
بادئات البحث للتواريخ والأرقام
| البادئة | المعنى | مثال |
|---|---|---|
eq |
يساوي | birthdate=eq1985-06-15 |
ne |
لا يساوي | birthdate=ne1985-06-15 |
gt |
أكبر من | birthdate=gt1980-01-01 |
lt |
أقل من | birthdate=lt1990-01-01 |
ge |
أكبر من أو يساوي | birthdate=ge1980-01-01 |
le |
أقل من أو يساوي | birthdate=le1990-01-01 |
sa |
يبدأ بعد | date=sa2026-01-01 |
eb |
ينتهي قبل | date=eb2026-12-31 |
العمل مع البيانات السريرية
إنشاء ملاحظة (علامات حيوية)
تسجيل العلامات الحيوية:
const createObservation = async (observationData) => {
const observation = {
resourceType: 'Observation',
status: 'final',
category: [
{
coding: [{
system: 'http://terminology.hl7.org/CodeSystem/observation-category',
code: 'vital-signs'
}]
}
],
code: {
coding: [{
system: 'http://loinc.org',
code: observationData.code, // على سبيل المثال، '8480-6' لضغط الدم الانقباضي
display: observationData.display
}]
},
subject: {
reference: `Patient/${observationData.patientId}`
},
effectiveDateTime: observationData.effectiveDate || new Date().toISOString(),
valueQuantity: {
value: observationData.value,
unit: observationData.unit,
system: 'http://unitsofmeasure.org',
code: observationData.ucumCode
},
performer: [
{
reference: `Practitioner/${observationData.performerId}`
}
]
};
const response = await fhirRequest('/Observation', {
method: 'POST',
body: JSON.stringify(observation)
});
return response;
};
// الاستخدام - تسجيل ضغط الدم
const systolicBP = await createObservation({
patientId: '12345',
code: '8480-6',
display: 'Systolic blood pressure',
value: 120,
unit: 'mmHg',
ucumCode: 'mm[Hg]',
performerId: '67890'
});
console.log(`Observation created: ${systolicBP.id}`);
رموز LOINC الشائعة
| الرمز | العرض | الفئة |
|---|---|---|
| 8480-6 | ضغط الدم الانقباضي | العلامات الحيوية |
| 8462-4 | ضغط الدم الانبساطي | العلامات الحيوية |
| 8867-4 | معدل ضربات القلب | العلامات الحيوية |
| 8310-5 | درجة حرارة الجسم | العلامات الحيوية |
| 8302-2 | طول الجسم | العلامات الحيوية |
| 29463-7 | وزن الجسم | العلامات الحيوية |
| 8871-5 | معدل التنفس | العلامات الحيوية |
| 2339-0 | الجلوكوز [الكتلة/الحجم] | المختبر |
| 4548-4 | الهيموغلوبين A1c | المختبر |
| 2093-3 | الكوليسترول [الكتلة/الحجم] | المختبر |
إنشاء حالة صحية (إدخال في قائمة المشاكل)
إضافة تشخيص إلى قائمة المشاكل:
const createCondition = async (conditionData) => {
const condition = {
resourceType: 'Condition',
clinicalStatus: {
coding: [{
system: 'http://terminology.hl7.org/CodeSystem/condition-clinical',
code: conditionData.status || 'active'
}]
},
verificationStatus: {
coding: [{
system: 'http://terminology.hl7.org/CodeSystem/condition-ver-status',
code: 'confirmed'
}]
},
category: [
{
coding: [{
system: 'http://terminology.hl7.org/CodeSystem/condition-category',
code: conditionData.category || 'problem-list-item'
}]
}
],
code: {
coding: [{
system: 'http://snomed.info/sct',
code: conditionData.sctCode,
display: conditionData.display
}]
},
subject: {
reference: `Patient/${conditionData.patientId}`
},
onsetDateTime: conditionData.onsetDate,
recordedDate: new Date().toISOString()
};
const response = await fhirRequest('/Condition', {
method: 'POST',
body: JSON.stringify(condition)
});
return response;
};
// الاستخدام - إضافة مرض السكري إلى قائمة المشاكل
const diabetes = await createCondition({
patientId: '12345',
sctCode: '44054006',
display: 'Type 2 Diabetes Mellitus',
status: 'active',
category: 'problem-list-item',
onsetDate: '2024-01-15'
});
رموز SNOMED CT الشائعة
| الرمز | العرض | الفئة |
|---|---|---|
| 44054006 | داء السكري من النوع 2 | مشكلة |
| 38341003 | ارتفاع ضغط الدم | مشكلة |
| 195967001 | الربو | مشكلة |
| 13645005 | مرض الانسداد الرئوي المزمن | مشكلة |
| 35489007 | اضطراب الاكتئاب | مشكلة |
| 22298006 | احتشاء عضلة القلب | مشكلة |
| 26929004 | مرض الزهايمر | مشكلة |
| 396275006 | التهاب المفاصل العظمي | مشكلة |
استرداد أدوية المريض
الحصول على طلبات الأدوية النشطة:
const getPatientMedications = async (patientId) => {
const response = await fhirRequest(
`/MedicationRequest?patient=${patientId}&status=active`
);
return response;
};
// الاستخدام
const medications = await getPatientMedications('12345');
medications.entry?.forEach(entry => {
const med = entry.resource;
console.log(`${med.medicationCodeableConcept.coding[0].display}`);
console.log(` Dose: ${med.dosageInstruction[0]?.text}`);
console.log(` Status: ${med.status}`);
});
استرداد نتائج المختبر
الحصول على التقارير التشخيصية والملاحظات:
const getPatientLabResults = async (patientId, options = {}) => {
const params = new URLSearchParams({
patient: patientId,
category: options.category || 'laboratory'
});
if (options.dateFrom) {
params.append('date', `ge${options.dateFrom}`);
}
const response = await fhirRequest(`/DiagnosticReport?${params.toString()}`);
return response;
};
// الحصول على اختبار معملي محدد (على سبيل المثال، HbA1c)
const getLabValue = async (patientId, loincCode) => {
const params = new URLSearchParams({
patient: patientId,
code: loincCode
});
const response = await fhirRequest(`/Observation?${params.toString()}`);
return response;
};
// الاستخدام - الحصول على نتائج HbA1c
const hba1c = await getLabValue('12345', '4548-4');
hba1c.entry?.forEach(entry => {
const obs = entry.resource;
console.log(`HbA1c: ${obs.valueQuantity.value} ${obs.valueQuantity.unit}`);
console.log(`Date: ${obs.effectiveDateTime}`);
});
OAuth 2.0 وSMART على FHIR
فهم مصادقة FHIR
تستخدم خوادم FHIR بروتوكول OAuth 2.0 مع OpenID Connect:
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Client │───▶│ Auth │───▶│ FHIR │
│ (App) │ │ Server │ │ Server │
└─────────────┘ └─────────────┘ └─────────────┘
│ │ │
│ 1. Auth Request │ │
│───────────────────▶│ │
│ │ │
│ 2. User Login │ │
│◀───────────────────│ │
│ │ │
│ 3. Auth Code │ │
│───────────────────▶│ │
│ │ │
│ 4. Token Request │ │
│───────────────────▶│ │
│ │ 5. Token + ID │
│◀───────────────────│ │
│ │ │
│ 6. API Request │ │
│────────────────────────────────────────▶│
│ │ │
│ 7. FHIR Data │ │
│◀────────────────────────────────────────│
إطلاق تطبيق SMART على FHIR
تطبيق إطلاق تطبيق SMART:
const crypto = require('crypto');
class SMARTClient {
constructor(config) {
this.clientId = config.clientId;
this.redirectUri = config.redirectUri;
this.issuer = config.issuer; // عنوان URL لخادم FHIR
this.scopes = config.scopes;
}
generatePKCE() {
const codeVerifier = crypto.randomBytes(32).toString('base64url');
const codeChallenge = crypto
.createHash('sha256')
.update(codeVerifier)
.digest('base64url');
return { codeVerifier, codeChallenge };
}
buildAuthUrl(state, patientId = null) {
const { codeVerifier, codeChallenge } = this.generatePKCE();
// تخزين codeVerifier لتبادل الرمز
this.codeVerifier = codeVerifier;
const params = new URLSearchParams({
response_type: 'code',
client_id: this.clientId,
redirect_uri: this.redirectUri,
scope: this.scopes.join(' '),
state: state,
code_challenge: codeChallenge,
code_challenge_method: 'S256',
aud: this.issuer,
launch: patientId ? `patient-${patientId}` : null
});
return `${this.issuer}/authorize?${params.toString()}`;
}
async exchangeCodeForToken(code) {
const response = await fetch(`${this.issuer}/token`, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
grant_type: 'authorization_code',
code: code,
redirect_uri: this.redirectUri,
client_id: this.clientId,
code_verifier: this.codeVerifier
})
});
const data = await response.json();
return {
accessToken: data.access_token,
refreshToken: data.refresh_token,
expiresIn: data.expires_in,
patientId: data.patient,
encounterId: data.encounter
};
}
}
// الاستخدام
const smartClient = new SMARTClient({
clientId: 'my-app-client-id',
redirectUri: 'https://myapp.com/callback',
issuer: 'https://fhir.epic.com',
scopes: [
'openid',
'profile',
'patient/Patient.read',
'patient/Observation.read',
'patient/Condition.read',
'patient/MedicationRequest.read',
'offline_access'
]
});
// إعادة توجيه المستخدم إلى عنوان URL للمصادقة
const state = crypto.randomBytes(16).toString('hex');
const authUrl = smartClient.buildAuthUrl(state);
console.log(`Redirect to: ${authUrl}`);
نطاقات SMART المطلوبة
| النطاق | الإذن | حالة الاستخدام |
|---|---|---|
openid |
مصادقة OIDC | مطلوب لجميع التطبيقات |
profile |
معلومات ملف تعريف المستخدم | دليل مقدمي الخدمة |
patient/Patient.read |
قراءة المعلومات الديموغرافية للمريض | البحث عن المريض |
patient/Observation.read |
قراءة الملاحظات | العلامات الحيوية، المختبرات |
patient/Condition.read |
قراءة الحالات الصحية | قوائم المشاكل |
patient/MedicationRequest.read |
قراءة الأدوية | تاريخ الأدوية |
patient/*.read |
قراءة جميع موارد المريض | بيانات المريض الكاملة |
user/*.read |
قراءة جميع الموارد المتاحة | عرض مقدم الخدمة |
offline_access |
تحديث الرمز | جلسات طويلة الأمد |
إجراء طلبات FHIR الموثقة
class FHIRClient {
constructor(accessToken, fhirBaseUrl) {
this.accessToken = accessToken;
this.baseUrl = fhirBaseUrl;
}
async request(endpoint, options = {}) {
const response = await fetch(`${this.baseUrl}/fhir${endpoint}`, {
...options,
headers: {
'Accept': 'application/fhir+json',
'Authorization': `Bearer ${this.accessToken}`,
...options.headers
}
});
if (!response.ok) {
const error = await response.json();
throw new Error(`FHIR Error: ${error.issue?.[0]?.details?.text}`);
}
return response.json();
}
async getPatient(patientId) {
return this.request(`/Patient/${patientId}`);
}
async searchPatients(params) {
const query = new URLSearchParams(params);
return this.request(`/Patient?${query.toString()}`);
}
}
// الاستخدام بعد رد اتصال OAuth
const fhirClient = new FHIRClient(tokens.accessToken, 'https://fhir.epic.com');
const patient = await fhirClient.getPatient(tokens.patientId);
عمليات الدفعة والمعاملات
طلبات الدفعة
تنفيذ طلبات متعددة ومستقلة:
const batchRequest = async (requests) => {
const bundle = {
resourceType: 'Bundle',
type: 'batch',
entry: requests.map(req => ({
resource: req.resource,
request: {
method: req.method,
url: req.url
}
}))
};
const response = await fhirRequest('', {
method: 'POST',
body: JSON.stringify(bundle)
});
return response;
};
// الاستخدام - جلب موارد متعددة
const bundle = await batchRequest([
{ method: 'GET', url: 'Patient/12345' },
{ method: 'GET', url: 'Patient/12345/Observation?category=vital-signs' },
{ method: 'GET', url: 'Patient/12345/Condition?clinical-status=active' }
]);
bundle.entry.forEach((entry, index) => {
console.log(`Response ${index}: ${entry.response.status}`);
console.log(entry.resource);
});
طلبات المعاملات
تنفيذ طلبات متعددة كوحدة ذرية:
const transactionRequest = async (requests) => {
const bundle = {
resourceType: 'Bundle',
type: 'transaction',
entry: requests.map(req => ({
resource: req.resource,
request: {
method: req.method,
url: req.url
}
}))
};
const response = await fhirRequest('', {
method: 'POST',
body: JSON.stringify(bundle)
});
return response;
};
// الاستخدام - إنشاء مريض وموارد ذات صلة
const transaction = await transactionRequest([
{
method: 'POST',
url: 'Patient',
resource: {
resourceType: 'Patient',
name: [{ family: 'Doe', given: ['Jane'] }],
gender: 'female',
birthDate: '1990-01-01'
}
},
{
method: 'POST',
url: 'Condition',
resource: {
resourceType: 'Condition',
clinicalStatus: { coding: [{ code: 'active' }] },
code: { coding: [{ system: 'http://snomed.info/sct', code: '38341003' }] },
subject: { reference: 'Patient/-1' } // مرجع للمورد الأول
}
}
]);
الاشتراكات وWebhooks
اشتراكات FHIR (R4B+)
الاشتراك في تغييرات الموارد:
const createSubscription = async (subscriptionData) => {
const subscription = {
resourceType: 'Subscription',
status: 'requested',
criteria: subscriptionData.criteria,
reason: subscriptionData.reason,
channel: {
type: 'rest-hook',
endpoint: subscriptionData.endpoint,
payload: 'application/fhir+json'
}
};
const response = await fhirRequest('/Subscription', {
method: 'POST',
body: JSON.stringify(subscription)
});
return response;
};
// الاستخدام - الاشتراك في نتائج المختبر الجديدة
const subscription = await createSubscription({
criteria: 'DiagnosticReport?category=laboratory&patient=12345',
reason: 'Monitor patient lab results',
endpoint: 'https://myapp.com/webhooks/fhir'
});
التعامل مع Webhooks في FHIR
const express = require('express');
const app = express();
app.post('/webhooks/fhir', express.json({ type: 'application/fhir+json' }), async (req, res) => {
const notification = req.body;
// التحقق من مرجع الاشتراك
if (notification.subscription !== expectedSubscription) {
return res.status(401).send('Unauthorized');
}
// معالجة الإشعار
if (notification.event?.resourceType === 'DiagnosticReport') {
const reportId = notification.event.resourceId;
const report = await fhirRequest(`/DiagnosticReport/${reportId}`);
// معالجة نتائج المختبر الجديدة
await processLabResult(report);
}
res.status(200).send('OK');
});
استكشاف المشاكل الشائعة وإصلاحها
المشكلة: 401 غير مصرح به
الأعراض: ظهور أخطاء "غير مصرح به" أو "رمز غير صالح".
الحلول:
- التحقق من أن الرمز لم تنته صلاحيته
- التحقق من أن نطاق الرمز يتضمن المورد المطلوب
- التأكد من وجود ترويسة
Authorization: Bearer {token} - التحقق من أن عنوان URL لخادم FHIR يطابق جمهور الرمز
المشكلة: 403 ممنوع
الأعراض: الرمز صالح ولكن تم رفض الوصول.
الحلول:
- التحقق من أن المستخدم لديه إذن للمورد المطلوب
- التحقق من أن سياق المريض متطابق (للرموز ذات النطاق الخاص بالمريض)
- التأكد من أن نطاقات SMART تتضمن العملية المطلوبة
- التحقق من ضوابط الوصول على مستوى المورد
المشكلة: 404 غير موجود
الأعراض: المورد غير موجود أو نقطة النهاية غير صحيحة.
الحلول:
- التحقق من صحة معرف المورد
- التحقق من صحة عنوان URL الأساسي لـ FHIR
- التأكد من أن نوع المورد مدعوم من قبل الخادم
- التحقق من نقطة النهاية الخاصة بالإصدار (R4 مقابل R4B)
المشكلة: 422 كيان غير قابل للمعالجة
الأعراض: أخطاء التحقق عند الإنشاء/التحديث.
الحلول:
// تحليل أخطاء التحقق
const error = await response.json();
error.issue?.forEach(issue => {
console.log(`Severity: ${issue.severity}`);
console.log(`Location: ${issue.expression?.join('.')}`);
console.log(`Message: ${issue.details?.text}`);
});
الأسباب الشائعة:
- حقول مطلوبة مفقودة
- قيم نظام رمز غير صالحة
- تنسيق مرجعي غير صحيح
- مشاكل تنسيق التاريخ
قائمة التحقق من النشر في الإنتاج
قبل الإطلاق:
- [ ] تكوين OAuth 2.0 مع SMART على FHIR
- [ ] تطبيق منطق تحديث الرمز
- [ ] إعداد معالجة الأخطاء المناسبة
- [ ] إضافة تسجيل شامل (لا توجد معلومات صحية محمية في السجلات)
- [ ] تطبيق تحديد المعدل
- [ ] تكوين منطق إعادة المحاولة مع التراجع الأسي
- [ ] الاختبار مع بائعي EHR متعددين
- [ ] التحقق مقابل مدقق FHIR
- [ ] توثيق جميع عمليات FHIR
- [ ] إعداد المراقبة والتنبيه
- [ ] إنشاء دليل تشغيل للمشاكل الشائعة
التحقق من صحة FHIR
const { FhirValidator } = require('fhir-validator');
const validator = new FhirValidator('4.0.1');
const validateResource = async (resource) => {
const validationResult = await validator.validate(resource);
if (!validationResult.valid) {
validationResult.issues.forEach(issue => {
console.error(`Validation Error: ${issue.message}`);
console.error(`Location: ${issue.path}`);
});
throw new Error('Resource validation failed');
}
return true;
};
// الاستخدام قبل الإنشاء/التحديث
await validateResource(patientResource);
حالات الاستخدام الواقعية
دمج بوابة المرضى
نظام صحي ينشئ بوابة للمرضى:
- التحدي: لم يتمكن المرضى من الوصول إلى سجلاتهم من مقدمي خدمات متعددين
- الحل: تطبيق SMART على FHIR مع دمج Epic وCerner
- النتيجة: تبني بنسبة 80% من المرضى، وانخفاض بنسبة 50% في طلبات السجلات
التنفيذ الرئيسي:
- إطلاق تطبيق SMART للمريض
- وصول للقراءة فقط إلى Patient, Observation, Condition, MedicationRequest
- تحديث الرمز للجلسات المستمرة
- واجهة مستخدم متجاوبة مع الجوال
دعم القرار السريري
منصة إدارة رعاية تضيف دعم القرار السريري:
- التحدي: يفوت مقدمو الخدمات فرص الرعاية الوقائية
- الحل: استعلامات FHIR في الوقت الفعلي عن فجوات الرعاية
- النتيجة: تحسن بنسبة 25% في درجات HEDIS
التنفيذ الرئيسي:
- تطبيق SMART موجه لمقدمي الخدمة
- استعلام Patient, Condition, Observation, Immunization
- حساب فجوات الرعاية بناءً على الإرشادات
- توصيات مضمنة في سير عمل EHR
تحليلات صحة السكان
جهة دفع تنشئ لوحة معلومات لصحة السكان:
- التحدي: بيانات غير مكتملة عبر شبكات مقدمي الخدمة
- الحل: تصدير بيانات FHIR بكميات كبيرة للتحليلات
- النتيجة: رؤية شاملة للمريض، وتقليل تكاليف PMPM
التنفيذ الرئيسي:
- الوصول إلى بيانات FHIR بكميات كبيرة ($export)
- تصدير ليلي إلى مستودع البيانات
- نماذج تقسيم المخاطر
- تنبيهات مدير الرعاية
الخلاصة
يوفر HL7 FHIR الأساس لقابلية التشغيل البيني الحديثة في الرعاية الصحية. النقاط الرئيسية:
- FHIR R4 هو المعيار الحالي لدمج واجهة برمجة تطبيقات الرعاية الصحية
- SMART على FHIR يتيح مصادقة OAuth 2.0 الآمنة
- تعمل أنواع الموارد على توحيد بيانات المرضى والملاحظات والحالات الصحية والأدوية
- تمكن معلمات البحث الاستعلام المرن
- تدعم عمليات الدفعة والمعاملات سير العمل المعقدة
- Apidog يبسط اختبار وتوثيق واجهة برمجة تطبيقات FHIR
قسم الأسئلة الشائعة
ما هو استخدام HL7 FHIR؟
يتيح FHIR التبادل الموحد لبيانات الرعاية الصحية بين السجلات الصحية الإلكترونية، وبوابات المرضى، وتطبيقات الهاتف المحمول، وأنظمة تكنولوجيا المعلومات الصحية الأخرى. تشمل حالات الاستخدام الشائعة تطبيقات وصول المرضى، ودعم القرار السريري، وصحة السكان، وتنسيق الرعاية.
كيف أبدأ باستخدام FHIR؟
ابدأ بالوصول إلى خادم FHIR عام (مثل خادم اختبار HAPI FHIR) أو قم بإعداد خدمة FHIR سحابية (Azure API لـ FHIR، AWS HealthLake). تدرب على قراءة الموارد واستخدام معلمات البحث.
ما الفرق بين HL7 v2 وFHIR؟
يستخدم HL7 v2 رسائل محددة بواسطة الأنابيب (ADT, ORM, ORU) لتبادل البيانات القائم على الأحداث. بينما يستخدم FHIR واجهات برمجة تطبيقات RESTful مع JSON/XML للوصول القائم على الموارد. FHIR أسهل في التنفيذ وأكثر ملاءمة لتطبيقات الويب/الجوال الحديثة.
هل FHIR متوافق مع HIPAA؟
FHIR بحد ذاته هو معيار لتنسيق البيانات. يعتمد التوافق مع HIPAA على التنفيذ: التشفير، والمصادقة، وضوابط الوصول، وتسجيل التدقيق. استخدم OAuth 2.0 مع SMART على FHIR للوصول الآمن.
ما هي نطاقات SMART؟
تحدد نطاقات SMART أذونات وصول دقيقة لموارد FHIR (على سبيل المثال، patient/Observation.read، user/*.read). اطلب فقط النطاقات التي يحتاجها تطبيقك.
كيف أبحث عن الموارد في FHIR؟
استخدم طلبات GET مع معلمات الاستعلام: /Patient?name=Smith&birthdate=ge1980-01-01. يدعم FHIR المعدّلات (:exact، :contains) والبادئات (gt، lt، ge، le).
ما هو Bulk FHIR؟
يتيح Bulk FHIR ($export) تصدير مجموعات بيانات كبيرة بشكل غير متزامن بتنسيق NDJSON. يستخدم لصحة السكان، والتحليلات، وتخزين البيانات.
كيف أتعامل مع إصدارات FHIR؟
استهدف إصدارًا محددًا من FHIR (يوصى بـ R4) واستخدم نقاط نهاية خاصة بالإصدار. تحقق من CapabilityStatement للإصدارات والموارد المدعومة.
هل يمكنني توسيع FHIR بحقول مخصصة؟
نعم، استخدم امتدادات FHIR لإضافة عناصر بيانات مخصصة. حدد الامتدادات في دليل التنفيذ الخاص بك وسجلها مع HL7 إذا كنت تشاركها على نطاق واسع.
ما الأدوات التي تساعد في تطوير FHIR؟
تشمل الأدوات الشائعة HAPI FHIR (خادم مفتوح المصدر)، ومدقق FHIR، ومجموعات Postman، وApidog لاختبار وتوثيق واجهات برمجة التطبيقات.
