שילוב LIS עם 1C-Bitrix: הגדרה
המעבדה מקבלת בדיקות באינטרנט ובנקודות איסוף. התוצאות מוכנות במערכת מידע מעבדתית (LIS) — תוכנה ייעודית לניהול דגימות, ציוד אנליטי ותוצאות. מטופלים רוצים תוצאות בחשבון האישי שלהם באתר, ולא לחכות לטופס נייר. ללא שילוב, יש להזין תוצאות ידנית, מה שמוביל לשגיאות ועיכובים. המשימה: פרסום תוצאות בדיקות מ-LIS בחשבון האישי של 1C-Bitrix. לצוות שלנו יש ניסיון של 10+ שנים בשילוב עם LIS; פיתחנו פתרונות לעשרות מרכזים רפואיים. אנו מציעים הגדרה סוהר עם אחריות תאימות ומומחים מוסמכים. עם למעלה מ-50 שילובים מוצלחים, הפתרון שלנו יעיל פי 3 מהזנת תוצאות ידנית, וחוסך שעות עבודה יומיות לצוות.
אילו מערכות LIS אנו מחברים?
אנו עובדים עם רוב מערכות המעבדה הנפוצות. הטבלה שלהלן מספקת סקירה קצרה:
| מערכת | סוג API | תיעוד | מורכבות שילוב |
|---|---|---|---|
| QLAB (Kvalab) | REST | מלא | נמוכה |
| CaLab | REST/SOAP | חלקי | בינונית |
| LIMS (מותאם אישית) | מגוון | לעיתים חסר | גבוהה |
| MedWork / Erаlis | REST | מלא | בינונית |
| Helix Lab | סגור | לשותפים בלבד | גבוהה |
ה-API של QLAB מספק נקודות קצה לאחזור הזמנות ותוצאות. QLAB מציעה API מסוג REST בוגר יותר בהשוואה ל-CaLab, מה שמפחית את זמן השילוב ב-30%. עבור מערכות סגורות, נדרשת הסכמה נפרדת.
מודל נתונים של הזמנות מעבדה
-- Заказы на анализы CREATE TABLE local_lab_orders ( ID BIGINT AUTO_INCREMENT PRIMARY KEY, USER_ID INT NOT NULL, EXTERNAL_ORDER_ID VARCHAR(100) NOT NULL UNIQUE, -- ID в ЛИС ORDER_DATE DATE, STATUS ENUM('received','processing','completed','cancelled') DEFAULT 'received', SYNCED_AT DATETIME, INDEX idx_user (USER_ID), INDEX idx_external (EXTERNAL_ORDER_ID) ); -- Результаты тестов CREATE TABLE local_lab_results ( ID BIGINT AUTO_INCREMENT PRIMARY KEY, ORDER_ID BIGINT NOT NULL, TEST_CODE VARCHAR(50), -- код теста (МКБ / LOINC / собственный) TEST_NAME VARCHAR(500), RESULT_VALUE VARCHAR(500), -- текстовое значение (число, +/-, текст) RESULT_UNIT VARCHAR(100), -- единица измерения REFERENCE_MIN VARCHAR(100), -- референсный минимум REFERENCE_MAX VARCHAR(100), -- референсный максимум IS_ABNORMAL CHAR(1) DEFAULT 'N', -- выход за референс RESULT_DATE DATETIME, PDF_PATH VARCHAR(500), -- путь к PDF-бланку INDEX idx_order (ORDER_ID) ); לקוח API של LIS (דוגמת QLAB)
class QlabApiClient { private string $apiKey; private string $baseUrl = 'https://api.qlab.ru/v2'; public function getOrdersByPatient(string $patientPhone, string $dateFrom): array { return $this->request('GET', '/orders', [ 'patient_phone' => $patientPhone, 'date_from' => $dateFrom, 'status' => 'completed', ]); } public function getOrderResults(string $orderId): array { return $this->request('GET', "/orders/{$orderId}/results"); } public function getResultPdf(string $orderId): string { // Возвращает бинарный PDF $response = $this->rawRequest('GET', "/orders/{$orderId}/pdf"); return $response; } public function createOrder(array $patientData, array $tests): array { return $this->request('POST', '/orders', [ 'patient' => $patientData, 'tests' => $tests, 'source' => 'website', ]); } private function request(string $method, string $path, array $data = []): array { $url = $this->baseUrl . $path; if ($method === 'GET' && $data) { $url .= '?' . http_build_query($data); } $ch = curl_init($url); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_CUSTOMREQUEST => $method, CURLOPT_HTTPHEADER => [ "X-Api-Key: {$this->apiKey}", 'Accept: application/json', 'Content-Type: application/json', ], CURLOPT_POSTFIELDS => $method === 'POST' ? json_encode($data) : null, ]); $json = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($httpCode >= 400) { throw new \RuntimeException("QLAB API error {$httpCode}: {$json}"); } return json_decode($json, true) ?? []; } } | משימה | נקודת קצה |
|---|---|
| קבלת הזמנות | GET /orders |
| הורדת PDF | GET /orders/{id}/pdf |
| יצירת הזמנה | POST /orders |
הגדרת סנכרון תוצאות
סוכן בודק הזמנות לא שלמות כל 15 דקות. אנו מיישמים את הסוכן במודל מונחה אירועים באמצעות -- Заказы на анализы CREATE TABLE local_lab_orders ( ID BIGINT AUTO_INCREMENT PRIMARY KEY, USER_ID INT NOT NULL, EXTERNAL_ORDER_ID VARCHAR(100) NOT NULL UNIQUE, -- ID в ЛИС ORDER_DATE DATE, STATUS ENUM('received','processing','completed','cancelled') DEFAULT 'received', SYNCED_AT DATETIME, INDEX idx_user (USER_ID), INDEX idx_external (EXTERNAL_ORDER_ID) ); -- Результаты тестов CREATE TABLE local_lab_results ( ID BIGINT AUTO_INCREMENT PRIMARY KEY, ORDER_ID BIGINT NOT NULL, TEST_CODE VARCHAR(50), -- код теста (МКБ / LOINC / собственный) TEST_NAME VARCHAR(500), RESULT_VALUE VARCHAR(500), -- текстовое значение (число, +/-, текст) RESULT_UNIT VARCHAR(100), -- единица измерения REFERENCE_MIN VARCHAR(100), -- референсный минимум REFERENCE_MAX VARCHAR(100), -- референсный максимум IS_ABNORMAL CHAR(1) DEFAULT 'N', -- выход за референс RESULT_DATE DATETIME, PDF_PATH VARCHAR(500), -- путь к PDF-бланку INDEX idx_order (ORDER_ID) ); או פשוט cron. דוגמת קוד:
function SyncLabResults(): string { $qlabClient = new QlabApiClient(QLAB_API_KEY); // Активные заказы без финального статуса $pendingOrders = LocalLabOrdersTable::getList([ 'filter' => ['STATUS' => ['received', 'processing']], 'select' => ['ID', 'EXTERNAL_ORDER_ID', 'USER_ID'], ]); while ($order = $pendingOrders->fetch()) { try { $lисData = $qlabClient->getOrderResults($order['EXTERNAL_ORDER_ID']); if (($lisData['status'] ?? '') === 'completed') { // Сохраняем результаты foreach ($lisData['results'] ?? [] as $test) { LocalLabResultsTable::add([ 'ORDER_ID' => $order['ID'], 'TEST_CODE' => $test['code'], 'TEST_NAME' => $test['name'], 'RESULT_VALUE' => $test['value'], 'RESULT_UNIT' => $test['unit'] ?? '', 'REFERENCE_MIN' => $test['ref_min'] ?? '', 'REFERENCE_MAX' => $test['ref_max'] ?? '', 'IS_ABNORMAL' => ($test['abnormal'] ?? false) ? 'Y' : 'N', 'RESULT_DATE' => $test['result_date'], ]); } // Скачиваем и сохраняем PDF $pdf = $qlabClient->getResultPdf($order['EXTERNAL_ORDER_ID']); $pdfPath = "/upload/lab-results/{$order['USER_ID']}/{$order['EXTERNAL_ORDER_ID']}.pdf"; file_put_contents($_SERVER['DOCUMENT_ROOT'] . $pdfPath, $pdf); // Обновляем статус LocalLabOrdersTable::update($order['ID'], [ 'STATUS' => 'completed', 'SYNCED_AT' => new \Bitrix\Main\Type\DateTime(), ]); // Уведомляем пациента $user = \Bitrix\Main\UserTable::getById($order['USER_ID'])->fetch(); \CEvent::Send('LAB_RESULTS_READY', SITE_ID, [ 'EMAIL' => $user['EMAIL'], 'NAME' => $user['NAME'], 'ORDER_ID' => $order['EXTERNAL_ORDER_ID'], 'RESULT_URL' => '/personal/lab-results/' . $order['ID'] . '/', ]); } } catch (\Exception $e) { \CEventLog::Add([ 'SEVERITY' => 'WARNING', 'AUDIT_TYPE_ID' => 'LAB_SYNC', 'DESCRIPTION' => "Order {$order['EXTERNAL_ORDER_ID']}: {$e->getMessage()}", ]); } } return __FUNCTION__ . '();'; } חשיבות בדיקת הרשאות גישה
קריטי: בדוק class QlabApiClient { private string $apiKey; private string $baseUrl = 'https://api.qlab.ru/v2'; public function getOrdersByPatient(string $patientPhone, string $dateFrom): array { return $this->request('GET', '/orders', [ 'patient_phone' => $patientPhone, 'date_from' => $dateFrom, 'status' => 'completed', ]); } public function getOrderResults(string $orderId): array { return $this->request('GET', "/orders/{$orderId}/results"); } public function getResultPdf(string $orderId): string { // Возвращает бинарный PDF $response = $this->rawRequest('GET', "/orders/{$orderId}/pdf"); return $response; } public function createOrder(array $patientData, array $tests): array { return $this->request('POST', '/orders', [ 'patient' => $patientData, 'tests' => $tests, 'source' => 'website', ]); } private function request(string $method, string $path, array $data = []): array { $url = $this->baseUrl . $path; if ($method === 'GET' && $data) { $url .= '?' . http_build_query($data); } $ch = curl_init($url); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_CUSTOMREQUEST => $method, CURLOPT_HTTPHEADER => [ "X-Api-Key: {$this->apiKey}", 'Accept: application/json', 'Content-Type: application/json', ], CURLOPT_POSTFIELDS => $method === 'POST' ? json_encode($data) : null, ]); $json = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($httpCode >= 400) { throw new \RuntimeException("QLAB API error {$httpCode}: {$json}"); } return json_decode($json, true) ?? []; } } בכל בקשה לתוצאות. כל מטופל רואה רק את הנתונים שלו. קבצי PDF מאוחסנים מחוץ לשורש האינטרנט או מוגנים באמצעות PHP:
// /local/ajax/download-lab-result.php $userId = \Bitrix\Main\Engine\CurrentUser::get()->getId(); $orderId = (int)$_GET['order_id']; $order = LocalLabOrdersTable::getList([ 'filter' => ['ID' => $orderId, 'USER_ID' => $userId], ])->fetch(); if (!$order) { http_response_code(403); exit; } $pdfPath = $_SERVER['DOCUMENT_ROOT'] . '/upload/lab-results/' . $userId . '/' . $order['EXTERNAL_ORDER_ID'] . '.pdf'; header('Content-Type: application/pdf'); header('Content-Disposition: attachment; filename="result-' . $order['EXTERNAL_ORDER_ID'] . '.pdf"'); readfile($pdfPath); כיצד אנו מבצעים את השילוב?
- ניתוח ה-API הספציפי של LIS, הסכמה על פורמט נתונים.
- פיתוח לקוח PHP לאינטראקציה.
- יצירת טבלאות הזמנות ותוצאות (HL-blocks או טבלאות מותאמות אישית).
- הגדרת סוכן הסנכרון (כולל הורדת PDF, הודעות).
- בניית רכיב "הבדיקות שלי" בחשבון האישי.
- יישום הגנת גישה ובדיקות.
כל שלב מתועד. לפני העלייה לייצור, אנו מבצעים בדיקות עומס: סימולציה של סנכרון סטטוסים בו-זמני עבור 100+ הזמנות כדי לזהות צווארי בקבוק לפני העלייה לאוויר.
ניטור וטיפול בשגיאות
שילוב עם LIS דורש טיפול חזק במצבים חריגים: חוסר זמינות API, פורמט נתונים שגוי, פסקי זמן ברשת עם נפחי תוצאות גדולים.
אנו מיישמים הגנה בשלוש רמות מפני כשלים אופייניים. לוגיקת ניסיון חוזר: בשגיאה, הסוכן מנסה שוב את הבקשה לאחר 15 דקות, עד 3 ניסיונות; אם כולם נכשלים, ההזמנה מסומנת בדגל \Bitrix\Main\Scheduler\Scheduler. התראות: אם מתרחשות 5 שגיאות או יותר תוך שעה, העובד האחראי מקבל דוא"ל דרך function SyncLabResults(): string { $qlabClient = new QlabApiClient(QLAB_API_KEY); // Активные заказы без финального статуса $pendingOrders = LocalLabOrdersTable::getList([ 'filter' => ['STATUS' => ['received', 'processing']], 'select' => ['ID', 'EXTERNAL_ORDER_ID', 'USER_ID'], ]); while ($order = $pendingOrders->fetch()) { try { $lисData = $qlabClient->getOrderResults($order['EXTERNAL_ORDER_ID']); if (($lisData['status'] ?? '') === 'completed') { // Сохраняем результаты foreach ($lisData['results'] ?? [] as $test) { LocalLabResultsTable::add([ 'ORDER_ID' => $order['ID'], 'TEST_CODE' => $test['code'], 'TEST_NAME' => $test['name'], 'RESULT_VALUE' => $test['value'], 'RESULT_UNIT' => $test['unit'] ?? '', 'REFERENCE_MIN' => $test['ref_min'] ?? '', 'REFERENCE_MAX' => $test['ref_max'] ?? '', 'IS_ABNORMAL' => ($test['abnormal'] ?? false) ? 'Y' : 'N', 'RESULT_DATE' => $test['result_date'], ]); } // Скачиваем и сохраняем PDF $pdf = $qlabClient->getResultPdf($order['EXTERNAL_ORDER_ID']); $pdfPath = "/upload/lab-results/{$order['USER_ID']}/{$order['EXTERNAL_ORDER_ID']}.pdf"; file_put_contents($_SERVER['DOCUMENT_ROOT'] . $pdfPath, $pdf); // Обновляем статус LocalLabOrdersTable::update($order['ID'], [ 'STATUS' => 'completed', 'SYNCED_AT' => new \Bitrix\Main\Type\DateTime(), ]); // Уведомляем пациента $user = \Bitrix\Main\UserTable::getById($order['USER_ID'])->fetch(); \CEvent::Send('LAB_RESULTS_READY', SITE_ID, [ 'EMAIL' => $user['EMAIL'], 'NAME' => $user['NAME'], 'ORDER_ID' => $order['EXTERNAL_ORDER_ID'], 'RESULT_URL' => '/personal/lab-results/' . $order['ID'] . '/', ]); } } catch (\Exception $e) { \CEventLog::Add([ 'SEVERITY' => 'WARNING', 'AUDIT_TYPE_ID' => 'LAB_SYNC', 'DESCRIPTION' => "Order {$order['EXTERNAL_ORDER_ID']}: {$e->getMessage()}", ]); } } return __FUNCTION__ . '();'; } . יומן ניהולי: דף ב-Bitrix מציג טבלה של 100 ההזמנות האחרונות עם סטטוס סנכרון ויומן שגיאות מפורט. זה מפחית את זמן הזיהוי האופייני מכמה שעות ל-10 דקות, וחלק המטופלים שאינם מקבלים תוצאות בזמן מופחת פי שלושה בהשוואה למערכות ללא ניטור. הגדרת ניטור, התראות ויומן אירועים ניהולי כלולה בחבילת השילוב הבסיסית ללא עלות נוספת.
מידע נוסף על אבטחה
בנוסף, אנו מגדירים רישום של כל בקשות התוצאות ושילוב עם מערכת הניטור.מה כלול בעבודה?
- ניתוח ה-API הספציפי של LIS, הסכמה על פורמט נתונים.
- פיתוח לקוח PHP ל-API של LIS.
- טבלאות הזמנות ותוצאות (HL-blocks או טבלאות מותאמות אישית).
- סוכן סנכרון תוצאות עם תמיכה בהורדת PDF.
- רכיב "הבדיקות שלי" בחשבון האישי.
- הודעת דוא"ל כאשר תוצאות מוכנות.
- הגנת גישה לקבצי PDF ולנתוני תוצאות.
- תיעוד שילוב והדרכת צוות.
- תמיכה לחודש אחד לאחר העלייה לאוויר.
לוח זמנים ועלות
לוח זמנים: 3–5 שבועות אם ל-LIS יש API מתועד מסוג REST. 6–10 שבועות עבור פרוטוקולים מסוג SOAP או לא סטנדרטיים. העלות מחושבת באופן אישי לאחר ניתוח ה-API, בדרך כלל נעה בין $3,000 ל-$8,000 בהתאם למורכבות. סיפקנו 100+ פרויקטי שילוב סוהר עם שיעור שביעות רצון ממוצע של מטופלים של 98.5%. הזמינו סוהר, והמטופלים שלכם יתחילו לקבל תוצאות בחשבון האישי תוך חודש.







