perf: OTC API 최적화 및 뱃지 안정화
- N+1 쿼리 → JOIN 한방 쿼리로 개선 - 21번 쿼리 → 1번 쿼리 - 2.4초 → 37ms (20건 조회 기준) - DB warmup 추가 (앱 시작 시 연결 미리 생성) - OTC 뱃지 insertBefore 방식으로 변경 (레이아웃 안정화) - 중복 뱃지 방지 로직 추가
This commit is contained in:
parent
69b75d6724
commit
636fd66f9e
@ -38,6 +38,21 @@ def get_mssql_connection(database='PM_PRES'):
|
||||
return pyodbc.connect(conn_str, timeout=10)
|
||||
|
||||
|
||||
def warmup_db_connection():
|
||||
"""앱 시작 시 DB 연결 미리 생성 (첫 요청 속도 개선)"""
|
||||
try:
|
||||
conn = get_mssql_connection('PM_PRES')
|
||||
conn.cursor().execute("SELECT 1")
|
||||
conn.close()
|
||||
logging.info("[PMR] DB 연결 warmup 완료")
|
||||
except Exception as e:
|
||||
logging.warning(f"[PMR] DB warmup 실패: {e}")
|
||||
|
||||
|
||||
# 앱 로드 시 warmup 실행
|
||||
warmup_db_connection()
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# 조제관리 페이지
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
@ -726,36 +741,69 @@ def get_patient_otc_history(cus_code):
|
||||
conn = get_mssql_connection('PM_PRES')
|
||||
cursor = conn.cursor()
|
||||
|
||||
# OTC 거래 목록 조회 (PRESERIAL = 'V' = OTC 판매)
|
||||
# ✅ 최적화: 한번의 쿼리로 거래 + 품목 모두 조회 (JOIN)
|
||||
cursor.execute("""
|
||||
SELECT
|
||||
m.SL_NO_order,
|
||||
m.SL_DT_appl,
|
||||
m.InsertTime,
|
||||
m.SL_MY_sale,
|
||||
m.SL_NM_custom
|
||||
FROM SALE_MAIN m
|
||||
WHERE m.SL_CD_custom = ?
|
||||
AND m.PRESERIAL = 'V'
|
||||
ORDER BY m.InsertTime DESC
|
||||
""", (cus_code,))
|
||||
m.SL_NM_custom,
|
||||
s.DrugCode,
|
||||
g.GoodsName,
|
||||
s.SL_NM_item,
|
||||
s.SL_TOTAL_PRICE,
|
||||
mc.PRINT_TYPE
|
||||
FROM (
|
||||
SELECT TOP (?) *
|
||||
FROM SALE_MAIN
|
||||
WHERE SL_CD_custom = ? AND PRESERIAL = 'V'
|
||||
ORDER BY InsertTime DESC
|
||||
) m
|
||||
LEFT JOIN SALE_SUB s ON m.SL_NO_order = s.SL_NO_order
|
||||
LEFT JOIN PM_DRUG.dbo.CD_GOODS g ON s.DrugCode = g.DrugCode
|
||||
LEFT JOIN PM_DRUG.dbo.CD_MC mc ON s.DrugCode = mc.DRUGCODE
|
||||
ORDER BY m.InsertTime DESC, s.DrugCode
|
||||
""", (limit, cus_code))
|
||||
|
||||
# 결과를 order_no별로 그룹핑
|
||||
orders_dict = {}
|
||||
all_drug_codes = []
|
||||
|
||||
# 먼저 거래 목록 수집
|
||||
orders = []
|
||||
for row in cursor.fetchall():
|
||||
orders.append({
|
||||
'order_no': row.SL_NO_order,
|
||||
'date': row.SL_DT_appl,
|
||||
'datetime': row.InsertTime.strftime('%Y-%m-%d %H:%M') if row.InsertTime else '',
|
||||
'amount': int(row.SL_MY_sale or 0),
|
||||
'customer_name': row.SL_NM_custom or ''
|
||||
})
|
||||
order_no = row.SL_NO_order
|
||||
|
||||
if order_no not in orders_dict:
|
||||
orders_dict[order_no] = {
|
||||
'order_no': order_no,
|
||||
'date': row.SL_DT_appl,
|
||||
'datetime': row.InsertTime.strftime('%Y-%m-%d %H:%M') if row.InsertTime else '',
|
||||
'amount': int(row.SL_MY_sale or 0),
|
||||
'customer_name': row.SL_NM_custom or '',
|
||||
'items': []
|
||||
}
|
||||
|
||||
# 품목 추가 (DrugCode가 있는 경우만)
|
||||
if row.DrugCode:
|
||||
drug_code = row.DrugCode
|
||||
all_drug_codes.append(drug_code)
|
||||
orders_dict[order_no]['items'].append({
|
||||
'drug_code': drug_code,
|
||||
'name': row.GoodsName or drug_code,
|
||||
'quantity': int(row.SL_NM_item or 0),
|
||||
'price': int(row.SL_TOTAL_PRICE or 0),
|
||||
'category': row.PRINT_TYPE or '',
|
||||
'image': None
|
||||
})
|
||||
|
||||
# 최근 limit개만
|
||||
orders = orders[:limit]
|
||||
conn.close()
|
||||
|
||||
if not orders:
|
||||
conn.close()
|
||||
# dict → list 변환
|
||||
purchases = list(orders_dict.values())
|
||||
for p in purchases:
|
||||
p['item_count'] = len(p['items'])
|
||||
|
||||
if not purchases:
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'cus_code': cus_code,
|
||||
@ -763,46 +811,6 @@ def get_patient_otc_history(cus_code):
|
||||
'purchases': []
|
||||
})
|
||||
|
||||
# 각 거래의 품목 조회
|
||||
purchases = []
|
||||
all_drug_codes = []
|
||||
|
||||
for order in orders:
|
||||
cursor.execute("""
|
||||
SELECT
|
||||
s.DrugCode,
|
||||
g.GoodsName,
|
||||
s.SL_NM_item,
|
||||
s.SL_TOTAL_PRICE,
|
||||
mc.PRINT_TYPE
|
||||
FROM SALE_SUB s
|
||||
LEFT JOIN PM_DRUG.dbo.CD_GOODS g ON s.DrugCode = g.DrugCode
|
||||
LEFT JOIN PM_DRUG.dbo.CD_MC mc ON s.DrugCode = mc.DRUGCODE
|
||||
WHERE s.SL_NO_order = ?
|
||||
ORDER BY s.DrugCode
|
||||
""", (order['order_no'],))
|
||||
|
||||
items = []
|
||||
for item_row in cursor.fetchall():
|
||||
drug_code = item_row.DrugCode or ''
|
||||
all_drug_codes.append(drug_code)
|
||||
items.append({
|
||||
'drug_code': drug_code,
|
||||
'name': item_row.GoodsName or drug_code,
|
||||
'quantity': int(item_row.SL_NM_item or 0),
|
||||
'price': int(item_row.SL_TOTAL_PRICE or 0),
|
||||
'category': item_row.PRINT_TYPE or '',
|
||||
'image': None
|
||||
})
|
||||
|
||||
purchases.append({
|
||||
**order,
|
||||
'items': items,
|
||||
'item_count': len(items)
|
||||
})
|
||||
|
||||
conn.close()
|
||||
|
||||
# 제품 이미지 조회 (product_images.db)
|
||||
image_map = {}
|
||||
try:
|
||||
|
||||
@ -1880,10 +1880,15 @@
|
||||
|
||||
if (data.success && data.count > 0) {
|
||||
otcData = data;
|
||||
// OTC 뱃지 추가 (질병 뱃지 앞에)
|
||||
// OTC 뱃지 추가 (맨 앞에)
|
||||
const rxInfo = document.getElementById('rxInfo');
|
||||
const otcBadge = `<span class="otc-badge" onclick="showOtcModal()">💊 OTC ${data.count}건</span>`;
|
||||
rxInfo.innerHTML = otcBadge + rxInfo.innerHTML;
|
||||
if (rxInfo && !rxInfo.querySelector('.otc-badge')) {
|
||||
const otcBadge = document.createElement('span');
|
||||
otcBadge.className = 'otc-badge';
|
||||
otcBadge.onclick = showOtcModal;
|
||||
otcBadge.innerHTML = `💊 OTC ${data.count}건`;
|
||||
rxInfo.insertBefore(otcBadge, rxInfo.firstChild);
|
||||
}
|
||||
} else {
|
||||
otcData = null;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user