From 68dcb919e4f428952dc47be09812a3a413bd4745 Mon Sep 17 00:00:00 2001 From: thug0bin Date: Sat, 28 Feb 2026 13:15:31 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20KIMS=20=EC=95=BD=EB=AC=BC=20=EC=83=81?= =?UTF-8?q?=ED=98=B8=EC=9E=91=EC=9A=A9=20=EC=B2=B4=ED=81=AC=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EC=B6=94=EA=B0=80=20(=EC=A1=B0=EC=A0=9C=20?= =?UTF-8?q?=ED=83=AD=20=EB=B2=84=ED=8A=BC=20+=20=EB=AA=A8=EB=8B=AC)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app.py | 166 +++++++++++++++++++++++++++ backend/templates/admin_members.html | 159 +++++++++++++++++++++++++ 2 files changed, 325 insertions(+) diff --git a/backend/app.py b/backend/app.py index f4d45e1..d91b6f3 100644 --- a/backend/app.py +++ b/backend/app.py @@ -4116,6 +4116,172 @@ def kill_process_on_port(port: int) -> bool: return False +# ═══════════════════════════════════════════════════════════ +# KIMS 약물 상호작용 API +# ═══════════════════════════════════════════════════════════ + +@app.route('/api/kims/interaction-check', methods=['POST']) +def api_kims_interaction_check(): + """ + KIMS 약물 상호작용 체크 API + + Request: + { + "drug_codes": ["055101150", "622801610"], // DrugCode 배열 + "pre_serial": "P20250630001" // 처방번호 (로깅용, optional) + } + + Response: + { + "success": true, + "interactions": [...], + "safe_count": 2, + "drugs_checked": [{"code": "...", "name": "...", "kd_code": "..."}] + } + """ + import requests as http_requests + + try: + data = request.get_json() + drug_codes = data.get('drug_codes', []) + pre_serial = data.get('pre_serial', '') + + if len(drug_codes) < 2: + return jsonify({ + 'success': False, + 'error': '상호작용 체크를 위해 최소 2개 이상의 약품이 필요합니다.' + }), 400 + + # 1. DrugCode → BASECODE(KIMS 9자리) 변환 + drug_session = db_manager.get_session('PM_DRUG') + placeholders = ','.join([f"'{c}'" for c in drug_codes]) + + code_query = text(f""" + SELECT DISTINCT G.DrugCode, G.GoodsName, B.BASECODE + FROM CD_GOODS G + LEFT JOIN CD_BARCODE B ON G.DrugCode = B.DrugCode + WHERE G.DrugCode IN ({placeholders}) + """) + code_result = drug_session.execute(code_query).fetchall() + + # KIMS 코드 매핑 + kd_codes = [] + drugs_info = [] + for row in code_result: + kd_code = row.BASECODE + if kd_code: + kd_codes.append(str(kd_code)) + drugs_info.append({ + 'drug_code': row.DrugCode, + 'name': row.GoodsName[:50] if row.GoodsName else '알 수 없음', + 'kd_code': str(kd_code) + }) + + if len(kd_codes) < 2: + return jsonify({ + 'success': False, + 'error': 'KIMS 코드로 변환 가능한 약품이 2개 미만입니다.', + 'drugs_checked': drugs_info + }), 400 + + # 2. KIMS API 호출 + kims_url = "https://api2.kims.co.kr/api/interaction/info" + kims_headers = { + 'Authorization': 'Basic VFNQTUtSOg==', + 'Content-Type': 'application/json', + 'Accept': 'application/json; charset=utf-8' + } + kims_payload = {'KDCodes': kd_codes} + + try: + kims_response = http_requests.get( + kims_url, + headers=kims_headers, + data=json.dumps(kims_payload), + timeout=10, + verify=False # SSL 검증 비활성화 (프로덕션에서는 주의) + ) + + if kims_response.status_code != 200: + return jsonify({ + 'success': False, + 'error': f'KIMS API 응답 오류: HTTP {kims_response.status_code}', + 'drugs_checked': drugs_info + }), 502 + + kims_data = kims_response.json() + + if kims_data.get('Message') != 'SUCCESS': + return jsonify({ + 'success': False, + 'error': f'KIMS API 처리 실패: {kims_data.get("Message", "알 수 없는 오류")}', + 'drugs_checked': drugs_info + }), 502 + + except http_requests.Timeout: + return jsonify({ + 'success': False, + 'error': 'KIMS API 타임아웃 (10초 초과)', + 'drugs_checked': drugs_info + }), 504 + except Exception as kims_err: + logging.error(f"KIMS API 호출 실패: {kims_err}") + return jsonify({ + 'success': False, + 'error': f'KIMS API 연결 실패: {str(kims_err)}', + 'drugs_checked': drugs_info + }), 502 + + # 3. 상호작용 결과 파싱 + interactions = [] + severity_text = {1: '심각', 2: '중등도', 3: '경미', 4: '참고', 5: '일반'} + severity_color = {1: '#dc2626', 2: '#f59e0b', 3: '#3b82f6', 4: '#6b7280', 5: '#9ca3af'} + + for alert in kims_data.get('AlertList', []): + for item in alert.get('AlertInfo', []): + severity = item.get('SeverityLevel', 5) + interactions.append({ + 'drug1_code': item.get('DrugCode1'), + 'drug1_name': item.get('ProductName1'), + 'drug2_code': item.get('DrugCode2'), + 'drug2_name': item.get('ProductName2'), + 'generic1': item.get('GenericName1'), + 'generic2': item.get('GenericName2'), + 'severity': severity, + 'severity_text': severity_text.get(severity, '알 수 없음'), + 'severity_color': severity_color.get(severity, '#9ca3af'), + 'description': item.get('Observation', ''), + 'management': item.get('ClinicalMng', ''), + 'action': item.get('ActionToTake', ''), + 'likelihood': item.get('LikelihoodDesc', '') + }) + + # 심각도 순 정렬 (1=심각이 먼저) + interactions.sort(key=lambda x: x['severity']) + + # 총 약품 쌍 수 계산 + total_pairs = len(kd_codes) * (len(kd_codes) - 1) // 2 + safe_count = total_pairs - len(interactions) + + logging.info(f"KIMS 상호작용 체크 완료: {len(kd_codes)}개 약품, {len(interactions)}건 발견 (처방: {pre_serial})") + + return jsonify({ + 'success': True, + 'interactions': interactions, + 'interaction_count': len(interactions), + 'safe_count': max(0, safe_count), + 'total_pairs': total_pairs, + 'drugs_checked': drugs_info + }) + + except Exception as e: + logging.error(f"KIMS 상호작용 체크 오류: {e}") + return jsonify({ + 'success': False, + 'error': f'서버 오류: {str(e)}' + }), 500 + + if __name__ == '__main__': import os diff --git a/backend/templates/admin_members.html b/backend/templates/admin_members.html index 405cd6c..894ebe5 100644 --- a/backend/templates/admin_members.html +++ b/backend/templates/admin_members.html @@ -1038,6 +1038,10 @@ `; }).join(''); + // 약품 코드 배열 (상호작용 체크용) + const drugCodes = (rx.items || []).map(item => item.drug_code).filter(c => c); + const drugCodesJson = JSON.stringify(drugCodes); + return `
@@ -1050,6 +1054,14 @@ ${rx.items && rx.items.length > 0 ? `
${itemsHtml}
` : ''} + ${drugCodes.length >= 2 ? ` +
+ +
+ ` : ''}
`; }).join(''); @@ -1111,6 +1123,153 @@ // 페이지 로드 시 검색창 포커스 document.getElementById('searchInput').focus(); + + // ═══════════════════════════════════════════════════ + // KIMS 약물 상호작용 체크 + // ═══════════════════════════════════════════════════ + + async function checkDrugInteraction(drugCodes, preSerial) { + // 로딩 모달 표시 + showInteractionModal('loading'); + + try { + const response = await fetch('/api/kims/interaction-check', { + method: 'POST', + headers: {'Content-Type': 'application/json'}, + body: JSON.stringify({ + drug_codes: drugCodes, + pre_serial: preSerial + }) + }); + + const data = await response.json(); + + if (data.success) { + showInteractionModal('result', data); + } else { + showInteractionModal('error', data.error || '알 수 없는 오류'); + } + } catch (err) { + showInteractionModal('error', '서버 연결 실패: ' + err.message); + } + } + + function showInteractionModal(type, data) { + let modal = document.getElementById('interactionModal'); + if (!modal) { + // 모달 생성 + modal = document.createElement('div'); + modal.id = 'interactionModal'; + modal.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,0.5);display:flex;align-items:center;justify-content:center;z-index:9999;'; + modal.onclick = (e) => { if (e.target === modal) modal.remove(); }; + document.body.appendChild(modal); + } + + let content = ''; + + if (type === 'loading') { + content = ` +
+
🔬
+
상호작용 분석 중...
+
KIMS 데이터베이스 조회 중
+
+ `; + } else if (type === 'error') { + content = ` +
+
⚠️
+
분석 실패
+
${escapeHtml(data)}
+
+ +
+
+ `; + } else if (type === 'result') { + const interactions = data.interactions || []; + const drugsChecked = data.drugs_checked || []; + + // 약품 목록 + const drugsHtml = drugsChecked.map(d => + `${escapeHtml(d.name.slice(0,20))}` + ).join(''); + + // 상호작용 목록 + let interactionsHtml = ''; + if (interactions.length === 0) { + interactionsHtml = ` +
+
+
상호작용 없음
+
+ ${data.total_pairs}개 약품 조합을 검사했습니다.
+ 주의가 필요한 상호작용이 발견되지 않았습니다. +
+
+ `; + } else { + interactionsHtml = interactions.map(item => ` +
+
+ + ${escapeHtml(item.drug1_name?.slice(0,20) || '')} ↔ ${escapeHtml(item.drug2_name?.slice(0,20) || '')} + + + ${item.severity_text} + +
+ ${item.description ? ` +
+ 📋 ${escapeHtml(item.description)} +
+ ` : ''} + ${item.management ? ` +
+ 💡 ${escapeHtml(item.management)} +
+ ` : ''} +
+ `).join(''); + } + + content = ` +
+
+
+ 🔬 약물 상호작용 분석 +
+
+ ${drugsChecked.length}개 약품 · ${data.total_pairs}개 조합 검사 +
+
+
+
분석 약품
+ ${drugsHtml} +
+
+ ${interactions.length > 0 ? ` +
+ ⚠️ ${interactions.length}건의 상호작용 발견 +
+ ` : ''} + ${interactionsHtml} +
+
+ +
+
+ `; + } + + modal.innerHTML = content; + }