From c7169e667964a98576aabd2ec8f9e3bc39ba0ece Mon Sep 17 00:00:00 2001 From: thug0bin Date: Thu, 5 Mar 2026 12:56:39 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20=EC=9E=90=EB=8F=99=EC=9D=B8=EC=87=84=20?= =?UTF-8?q?=EC=A4=91=EB=B3=B5=20=EB=B0=A9=EC=A7=80=20+=20=EC=9D=B8?= =?UTF-8?q?=EC=87=84=20=EB=A1=9C=EA=B9=85=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 문제: 같은 처방이 2번 인쇄됨 원인: printPaaiResult가 2곳에서 호출 (API 응답 + WebSocket 이벤트) 해결: - window.printedSerials (Set) 으로 중복 인쇄 방지 - 인쇄 성공 시 Set에 추가 - 5분 후 자동 제거 (메모리 관리) 로깅 추가: - 프론트: 콘솔에 시간 + 환자명 + 상태 - 백엔드: logs/print_history.log 파일에 기록 --- backend/pmr_api.py | 27 +++++++++++++++++++++++++-- backend/templates/pmr.html | 34 +++++++++++++++++++++++++++------- 2 files changed, 52 insertions(+), 9 deletions(-) diff --git a/backend/pmr_api.py b/backend/pmr_api.py index f5e7914..06d6326 100644 --- a/backend/pmr_api.py +++ b/backend/pmr_api.py @@ -1461,6 +1461,23 @@ _INIT = _ESC + b'@' # 프린터 초기화 _CUT = _ESC + b'd\x03' # 피드 + 커트 +def _log_print_history(pre_serial, patient_name, success, error=None): + """인쇄 이력을 파일에 기록""" + try: + log_dir = Path(__file__).parent / 'logs' + log_dir.mkdir(exist_ok=True) + log_file = log_dir / 'print_history.log' + + timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S') + status = '✅ 성공' if success else f'❌ 실패: {error}' + line = f"[{timestamp}] {pre_serial} | {patient_name} | {status}\n" + + with open(log_file, 'a', encoding='utf-8') as f: + f.write(line) + except Exception as e: + logging.warning(f"인쇄 로그 기록 실패: {e}") + + @pmr_bp.route('/api/paai/print', methods=['POST']) def paai_print(): """PAAI 분석 결과 ESC/POS 인쇄""" @@ -1473,6 +1490,8 @@ def paai_print(): analysis = result.get('analysis', {}) kims_summary = result.get('kims_summary', {}) + logging.info(f"[PRINT] 요청 수신: {pre_serial} ({patient_name})") + # 영수증 텍스트 생성 message = _format_paai_receipt(pre_serial, patient_name, analysis, kims_summary) @@ -1480,13 +1499,17 @@ def paai_print(): success = _print_escpos_text(message) if success: - logging.info(f"PAAI 인쇄 완료: {pre_serial} ({patient_name})") + logging.info(f"[PRINT] ✅ 완료: {pre_serial} ({patient_name})") + _log_print_history(pre_serial, patient_name, True) return jsonify({'success': True, 'message': '인쇄 완료'}) else: + logging.error(f"[PRINT] ❌ 프린터 연결 실패: {pre_serial}") + _log_print_history(pre_serial, patient_name, False, '프린터 연결 실패') return jsonify({'success': False, 'error': '프린터 연결 실패'}), 500 except Exception as e: - logging.error(f"PAAI 인쇄 오류: {e}") + logging.error(f"[PRINT] ❌ 오류: {pre_serial} - {e}") + _log_print_history(pre_serial, patient_name, False, str(e)) return jsonify({'success': False, 'error': str(e)}), 500 diff --git a/backend/templates/pmr.html b/backend/templates/pmr.html index 7185f93..d6fbb1f 100644 --- a/backend/templates/pmr.html +++ b/backend/templates/pmr.html @@ -2650,6 +2650,7 @@ // 자동인쇄 기능 (모두 window 전역) // ═══════════════════════════════════════════════════════════════ window.autoPrintEnabled = localStorage.getItem('pmr_auto_print') === 'true'; + window.printedSerials = new Set(); // 중복 인쇄 방지용 // 간단한 토스트 알림 window.showToast = function(message, type) { @@ -2678,15 +2679,25 @@ } }; - // PAAI 결과 인쇄 + // PAAI 결과 인쇄 (중복 방지 포함) window.printPaaiResult = async function(preSerial, patientName, result) { + // 1. 자동인쇄 비활성화 체크 if (!window.autoPrintEnabled) { - console.log('[AutoPrint] 비활성화됨'); + console.log('[AutoPrint] 비활성화됨, 스킵:', preSerial); return; } + // 2. 중복 인쇄 방지 + if (window.printedSerials.has(preSerial)) { + console.log('[AutoPrint] 이미 인쇄됨, 스킵:', preSerial); + return; + } + + // 3. 인쇄 진행 try { - console.log('[AutoPrint] 인쇄 요청:', preSerial); + var now = new Date().toLocaleTimeString('ko-KR'); + console.log('[AutoPrint] ' + now + ' 인쇄 요청:', preSerial, patientName); + var response = await fetch('/pmr/api/paai/print', { method: 'POST', headers: {'Content-Type': 'application/json'}, @@ -2699,13 +2710,22 @@ var data = await response.json(); if (data.success) { - console.log('[AutoPrint] 인쇄 완료'); - window.showToast('인쇄 완료: ' + patientName, 'success'); + // 인쇄 성공 → Set에 추가 (중복 방지) + window.printedSerials.add(preSerial); + console.log('[AutoPrint] ' + now + ' ✅ 인쇄 완료:', preSerial, patientName); + window.showToast('🖨️ ' + patientName, 'success'); + + // 5분 후 Set에서 제거 (메모리 관리) + setTimeout(function() { + window.printedSerials.delete(preSerial); + }, 5 * 60 * 1000); } else { - console.error('[AutoPrint] 실패:', data.error); + console.error('[AutoPrint] ' + now + ' ❌ 실패:', preSerial, data.error); + window.showToast('인쇄 실패: ' + patientName, 'error'); } } catch (err) { - console.error('[AutoPrint] 오류:', err); + console.error('[AutoPrint] 오류:', preSerial, err); + window.showToast('인쇄 오류', 'error'); } };