feat: 동물약 안내서 기능 추가

- 동물약 뱃지 클릭 시 약품 정보 모달 표시
- APC 코드로 PostgreSQL 조회 (효능효과, 용법용량, 주의사항)
- HTML 태그 파싱하여 텍스트 표시
- ESC/POS 인쇄 API 준비 (프린터 연결 시 활성화)
- 미리보기 API: /api/animal-drug-info/preview
- 인쇄 API: /api/animal-drug-info/print
This commit is contained in:
thug0bin
2026-03-04 19:18:10 +09:00
parent 77c667e1f6
commit 321fd0de1e
2 changed files with 361 additions and 1 deletions

View File

@@ -6417,6 +6417,253 @@ def api_product_images_stats():
return jsonify({'success': False, 'error': str(e)}), 500
# ═══════════════════════════════════════════════════════════════════════════════
# 동물약 정보 인쇄 API (ESC/POS 80mm)
# ═══════════════════════════════════════════════════════════════════════════════
@app.route('/api/animal-drug-info/print', methods=['POST'])
def api_animal_drug_info_print():
"""동물약 정보 인쇄 (APC로 PostgreSQL 조회 후 ESC/POS 출력)"""
try:
import re
from html import unescape
data = request.get_json()
apc = data.get('apc', '')
product_name = data.get('product_name', '')
if not apc:
return jsonify({'success': False, 'error': 'APC 코드가 필요합니다'}), 400
# PostgreSQL에서 약품 정보 조회
try:
from sqlalchemy import create_engine
pg_engine = create_engine(
'postgresql://admin:trajet6640@192.168.0.87:5432/apdb_master',
connect_args={'connect_timeout': 5}
)
with pg_engine.connect() as conn:
result = conn.execute(text("""
SELECT
product_name,
company_name,
main_ingredient,
efficacy_effect,
dosage_instructions,
precautions,
weight_min_kg,
weight_max_kg,
pet_size_label
FROM apc
WHERE apc = :apc
LIMIT 1
"""), {'apc': apc})
row = result.fetchone()
if not row:
return jsonify({'success': False, 'error': f'APC {apc} 정보를 찾을 수 없습니다'}), 404
except Exception as e:
logging.error(f"PostgreSQL 조회 오류: {e}")
return jsonify({'success': False, 'error': f'DB 조회 오류: {str(e)}'}), 500
# HTML 태그 제거 함수
def strip_html(html_text):
if not html_text:
return ''
# HTML 태그 제거
text = re.sub(r'<[^>]+>', '', html_text)
# HTML 엔티티 변환
text = unescape(text)
# 연속 공백/줄바꿈 정리
text = re.sub(r'\s+', ' ', text).strip()
return text
# 텍스트를 줄 단위로 분리 (80mm ≈ 42자)
def wrap_text(text, width=40):
lines = []
words = text.split()
current_line = ""
for word in words:
if len(current_line) + len(word) + 1 <= width:
current_line += (" " if current_line else "") + word
else:
if current_line:
lines.append(current_line)
current_line = word
if current_line:
lines.append(current_line)
return lines
# 데이터 파싱
pg_product_name = row.product_name or product_name
company = row.company_name or ''
ingredient = row.main_ingredient or ''
efficacy = strip_html(row.efficacy_effect)
dosage = strip_html(row.dosage_instructions)
precautions = strip_html(row.precautions)
# ESC/POS 인쇄 데이터 생성
from escpos.printer import Usb
# 프린터 연결 (아침에 설정한 영수증 프린터)
try:
# POS 프린터 (VID/PID 확인 필요)
printer = Usb(0x0483, 0x5743, profile="default")
except Exception as e:
logging.error(f"프린터 연결 실패: {e}")
return jsonify({'success': False, 'error': f'프린터 연결 실패: {str(e)}'}), 500
try:
# 헤더
printer.set(align='center', bold=True, double_height=True)
printer.text("🐾 동물약 안내서\n")
printer.set(align='center', bold=False, double_height=False)
printer.text("=" * 42 + "\n\n")
# 제품명
printer.set(align='center', bold=True)
printer.text(f"{pg_product_name}\n")
printer.set(bold=False)
if company:
printer.text(f"제조: {company}\n")
printer.text("\n")
# 주성분
if ingredient and ingredient != 'NaN':
printer.set(align='left')
printer.text("-" * 42 + "\n")
printer.set(bold=True)
printer.text("▶ 주성분\n")
printer.set(bold=False)
for line in wrap_text(ingredient):
printer.text(f" {line}\n")
printer.text("\n")
# 효능효과
if efficacy:
printer.set(align='left')
printer.text("-" * 42 + "\n")
printer.set(bold=True)
printer.text("▶ 효능효과\n")
printer.set(bold=False)
for line in wrap_text(efficacy[:300]): # 최대 300자
printer.text(f" {line}\n")
printer.text("\n")
# 용법용량
if dosage:
printer.text("-" * 42 + "\n")
printer.set(bold=True)
printer.text("▶ 용법용량\n")
printer.set(bold=False)
for line in wrap_text(dosage[:400]): # 최대 400자
printer.text(f" {line}\n")
printer.text("\n")
# 주의사항
if precautions:
printer.text("-" * 42 + "\n")
printer.set(bold=True)
printer.text("▶ 주의사항\n")
printer.set(bold=False)
for line in wrap_text(precautions[:300]): # 최대 300자
printer.text(f" {line}\n")
printer.text("\n")
# 푸터
printer.set(align='center')
printer.text("=" * 42 + "\n")
printer.text("청 춘 약 국\n")
printer.text("Tel: 033-481-5222\n\n")
# 커팅
printer.cut()
printer.close()
return jsonify({'success': True, 'message': '동물약 안내서 인쇄 완료'})
except Exception as e:
logging.error(f"인쇄 오류: {e}")
try:
printer.close()
except:
pass
return jsonify({'success': False, 'error': f'인쇄 오류: {str(e)}'}), 500
except Exception as e:
logging.error(f"동물약 정보 인쇄 API 오류: {e}")
return jsonify({'success': False, 'error': str(e)}), 500
@app.route('/api/animal-drug-info/preview', methods=['POST'])
def api_animal_drug_info_preview():
"""동물약 정보 미리보기 (텍스트 반환)"""
try:
import re
from html import unescape
data = request.get_json()
apc = data.get('apc', '')
if not apc:
return jsonify({'success': False, 'error': 'APC 코드가 필요합니다'}), 400
# PostgreSQL에서 약품 정보 조회
try:
from sqlalchemy import create_engine
pg_engine = create_engine(
'postgresql://admin:trajet6640@192.168.0.87:5432/apdb_master',
connect_args={'connect_timeout': 5}
)
with pg_engine.connect() as conn:
result = conn.execute(text("""
SELECT
product_name,
company_name,
main_ingredient,
efficacy_effect,
dosage_instructions,
precautions
FROM apc
WHERE apc = :apc
LIMIT 1
"""), {'apc': apc})
row = result.fetchone()
if not row:
return jsonify({'success': False, 'error': f'APC {apc} 정보 없음'}), 404
except Exception as e:
return jsonify({'success': False, 'error': f'DB 오류: {str(e)}'}), 500
# HTML 태그 제거
def strip_html(html_text):
if not html_text:
return ''
text = re.sub(r'<[^>]+>', '', html_text)
text = unescape(text)
text = re.sub(r'\s+', ' ', text).strip()
return text
return jsonify({
'success': True,
'data': {
'product_name': row.product_name,
'company_name': row.company_name,
'main_ingredient': row.main_ingredient if row.main_ingredient != 'NaN' else None,
'efficacy_effect': strip_html(row.efficacy_effect),
'dosage_instructions': strip_html(row.dosage_instructions),
'precautions': strip_html(row.precautions)
}
})
except Exception as e:
return jsonify({'success': False, 'error': str(e)}), 500
if __name__ == '__main__':
import os