feat(admin): 제품 사용이력 + 환자 최근처방 모달 기능
- GET /api/products/<drug_code>/usage-history - 제품별 처방 이력 조회 (환자명, 수량, 횟수, 일수, 총투약량) - 페이지네이션 + 기간 필터 지원 - GET /api/patients/<cus_code>/recent-prescriptions - 환자 최근 6개월 처방 내역 - 약품별 분류(CD_MC.PRINT_TYPE) 표시 (당뇨치료, 고지혈치료 등) - admin_products.html 모달 2개 추가 - 제품명 클릭 → 사용이력 모달 (횟수 포함 정확한 총투약량) - 환자명 클릭 → 최근처방 모달 (z-index 2100으로 위에 표시) DB 조인: - PS_main.PreSerial = PS_sub_pharm.PreSerial - CD_GOODS + CD_MC (PRINT_TYPE 분류)
This commit is contained in:
232
backend/app.py
232
backend/app.py
@@ -3872,6 +3872,238 @@ def api_drug_purchase_history(drug_code):
|
||||
return jsonify({'success': False, 'error': str(e)}), 500
|
||||
|
||||
|
||||
# ==================== 처방 사용이력 API ====================
|
||||
|
||||
@app.route('/api/products/<drug_code>/usage-history')
|
||||
def api_product_usage_history(drug_code):
|
||||
"""
|
||||
제품 처방 사용이력 조회 API
|
||||
- PS_main + PS_sub_pharm JOIN
|
||||
- 페이지네이션, 기간 필터 지원
|
||||
- 환자명 마스킹 처리
|
||||
"""
|
||||
page = int(request.args.get('page', 1))
|
||||
per_page = int(request.args.get('per_page', 20))
|
||||
months = int(request.args.get('months', 12))
|
||||
|
||||
offset = (page - 1) * per_page
|
||||
|
||||
try:
|
||||
pres_session = db_manager.get_session('PM_PRES')
|
||||
|
||||
# 기간 계산 (N개월 전부터)
|
||||
from datetime import datetime, timedelta
|
||||
start_date = (datetime.now() - timedelta(days=months * 30)).strftime('%Y%m%d')
|
||||
|
||||
# 총 건수 조회 (COUNT)
|
||||
count_result = pres_session.execute(text("""
|
||||
SELECT COUNT(*) as total_count
|
||||
FROM PS_sub_pharm sp
|
||||
JOIN PS_main pm ON pm.PreSerial = sp.PreSerial
|
||||
WHERE sp.DrugCode = :drug_code
|
||||
AND pm.Indate >= :start_date
|
||||
AND (sp.PS_Type IS NULL OR sp.PS_Type != '9')
|
||||
"""), {'drug_code': drug_code, 'start_date': start_date})
|
||||
total_count = count_result.fetchone()[0]
|
||||
|
||||
# 데이터 조회 (페이지네이션)
|
||||
data_result = pres_session.execute(text("""
|
||||
SELECT
|
||||
pm.Paname as patient_name,
|
||||
pm.CusCode as cus_code,
|
||||
pm.Indate as rx_date,
|
||||
sp.QUAN as quantity,
|
||||
sp.QUAN_TIME as times,
|
||||
sp.Days as days
|
||||
FROM PS_sub_pharm sp
|
||||
JOIN PS_main pm ON pm.PreSerial = sp.PreSerial
|
||||
WHERE sp.DrugCode = :drug_code
|
||||
AND pm.Indate >= :start_date
|
||||
AND (sp.PS_Type IS NULL OR sp.PS_Type != '9')
|
||||
ORDER BY pm.Indate DESC
|
||||
OFFSET :offset ROWS
|
||||
FETCH NEXT :per_page ROWS ONLY
|
||||
"""), {
|
||||
'drug_code': drug_code,
|
||||
'start_date': start_date,
|
||||
'offset': offset,
|
||||
'per_page': per_page
|
||||
})
|
||||
|
||||
items = []
|
||||
for row in data_result.fetchall():
|
||||
patient_name = row.patient_name or ''
|
||||
cus_code = row.cus_code or ''
|
||||
|
||||
# 날짜 포맷팅 (YYYYMMDD -> YYYY-MM-DD)
|
||||
rx_date = row.rx_date or ''
|
||||
if len(rx_date) == 8:
|
||||
rx_date = f"{rx_date[:4]}-{rx_date[4:6]}-{rx_date[6:8]}"
|
||||
|
||||
quantity = int(row.quantity) if row.quantity else 1
|
||||
times = int(row.times) if row.times else 1 # 횟수 (QUAN_TIME)
|
||||
days = int(row.days) if row.days else 1
|
||||
total_dose = quantity * times * days # 수량 × 횟수 × 일수
|
||||
|
||||
items.append({
|
||||
'patient_name': patient_name,
|
||||
'cus_code': cus_code,
|
||||
'rx_date': rx_date,
|
||||
'quantity': quantity,
|
||||
'times': times,
|
||||
'days': days,
|
||||
'total_dose': total_dose
|
||||
})
|
||||
|
||||
# 약품명 조회
|
||||
drug_session = db_manager.get_session('PM_DRUG')
|
||||
name_result = drug_session.execute(text("""
|
||||
SELECT GoodsName FROM CD_GOODS WHERE DrugCode = :drug_code
|
||||
"""), {'drug_code': drug_code})
|
||||
name_row = name_result.fetchone()
|
||||
product_name = name_row[0] if name_row else drug_code
|
||||
|
||||
# 총 페이지 수 계산
|
||||
total_pages = (total_count + per_page - 1) // per_page
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'product_name': product_name,
|
||||
'pagination': {
|
||||
'page': page,
|
||||
'per_page': per_page,
|
||||
'total_count': total_count,
|
||||
'total_pages': total_pages
|
||||
},
|
||||
'items': items
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"사용이력 조회 오류: {e}")
|
||||
return jsonify({'success': False, 'error': str(e)}), 500
|
||||
|
||||
|
||||
@app.route('/api/patients/<cus_code>/recent-prescriptions')
|
||||
def api_patient_recent_prescriptions(cus_code):
|
||||
"""
|
||||
환자 최근 처방 내역 조회 API
|
||||
- 해당 환자가 최근에 어떤 약을 처방받았는지 확인
|
||||
- DB 부담 최소화: 최근 6개월, 최대 30건
|
||||
"""
|
||||
try:
|
||||
pres_session = db_manager.get_session('PM_PRES')
|
||||
drug_session = db_manager.get_session('PM_DRUG')
|
||||
|
||||
# 최근 6개월
|
||||
from datetime import datetime, timedelta
|
||||
start_date = (datetime.now() - timedelta(days=180)).strftime('%Y%m%d')
|
||||
|
||||
# 환자의 최근 처방전 조회 (최대 10건)
|
||||
rx_result = pres_session.execute(text("""
|
||||
SELECT TOP 10
|
||||
pm.PreSerial,
|
||||
pm.Paname as patient_name,
|
||||
pm.Indate as rx_date,
|
||||
pm.Drname as doctor_name,
|
||||
pm.OrderName as hospital_name
|
||||
FROM PS_main pm
|
||||
WHERE pm.CusCode = :cus_code
|
||||
AND pm.Indate >= :start_date
|
||||
ORDER BY pm.Indate DESC
|
||||
"""), {'cus_code': cus_code, 'start_date': start_date})
|
||||
|
||||
prescriptions = []
|
||||
for rx in rx_result.fetchall():
|
||||
# 날짜 포맷팅
|
||||
rx_date = rx.rx_date or ''
|
||||
if len(rx_date) == 8:
|
||||
rx_date = f"{rx_date[:4]}-{rx_date[4:6]}-{rx_date[6:8]}"
|
||||
|
||||
# 해당 처방의 약품 목록 조회
|
||||
items_result = pres_session.execute(text("""
|
||||
SELECT
|
||||
sp.DrugCode,
|
||||
sp.QUAN as quantity,
|
||||
sp.QUAN_TIME as times,
|
||||
sp.Days as days
|
||||
FROM PS_sub_pharm sp
|
||||
WHERE sp.PreSerial = :pre_serial
|
||||
AND (sp.PS_Type IS NULL OR sp.PS_Type != '9')
|
||||
"""), {'pre_serial': rx.PreSerial})
|
||||
|
||||
# 먼저 모든 약품 데이터를 리스트로 가져오기
|
||||
raw_items = items_result.fetchall()
|
||||
drug_codes = [item.DrugCode for item in raw_items]
|
||||
|
||||
# 약품명 + 성분명 + 분류(PRINT_TYPE) 일괄 조회
|
||||
drug_info_map = {}
|
||||
if drug_codes:
|
||||
placeholders = ','.join([f"'{dc}'" for dc in drug_codes])
|
||||
name_result = drug_session.execute(text(f"""
|
||||
SELECT g.DrugCode, g.GoodsName, s.SUNG_HNM, m.PRINT_TYPE
|
||||
FROM CD_GOODS g
|
||||
LEFT JOIN CD_SUNG s ON g.SUNG_CODE = s.SUNG_CODE
|
||||
LEFT JOIN CD_MC m ON g.DrugCode = m.DRUGCODE
|
||||
WHERE g.DrugCode IN ({placeholders})
|
||||
"""))
|
||||
for row in name_result.fetchall():
|
||||
drug_info_map[row[0]] = {
|
||||
'name': row[1],
|
||||
'ingredient': row[2] or '',
|
||||
'category': row[3] or '' # 분류 (알러지질환약 등)
|
||||
}
|
||||
|
||||
items = []
|
||||
for item in raw_items:
|
||||
info = drug_info_map.get(item.DrugCode, {})
|
||||
drug_name = info.get('name', item.DrugCode)
|
||||
ingredient = info.get('ingredient', '')
|
||||
category = info.get('category', '') # 분류 (알러지질환약 등)
|
||||
|
||||
quantity = int(item.quantity) if item.quantity else 1
|
||||
times = int(item.times) if item.times else 1
|
||||
days = int(item.days) if item.days else 1
|
||||
|
||||
items.append({
|
||||
'drug_code': item.DrugCode,
|
||||
'drug_name': drug_name,
|
||||
'category': category, # 분류 추가
|
||||
'quantity': quantity,
|
||||
'times': times,
|
||||
'days': days,
|
||||
'total_dose': quantity * times * days
|
||||
})
|
||||
|
||||
prescriptions.append({
|
||||
'pre_serial': rx.PreSerial,
|
||||
'rx_date': rx_date,
|
||||
'doctor_name': rx.doctor_name or '',
|
||||
'hospital_name': rx.hospital_name or '',
|
||||
'items': items
|
||||
})
|
||||
|
||||
# 환자명
|
||||
patient_name = prescriptions[0]['items'][0]['drug_name'] if prescriptions else ''
|
||||
if prescriptions and pres_session.execute(text("""
|
||||
SELECT TOP 1 Paname FROM PS_main WHERE CusCode = :cus_code
|
||||
"""), {'cus_code': cus_code}).fetchone():
|
||||
patient_name = pres_session.execute(text("""
|
||||
SELECT TOP 1 Paname FROM PS_main WHERE CusCode = :cus_code
|
||||
"""), {'cus_code': cus_code}).fetchone()[0]
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'cus_code': cus_code,
|
||||
'patient_name': patient_name,
|
||||
'prescription_count': len(prescriptions),
|
||||
'prescriptions': prescriptions
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"환자 처방 조회 오류: {e}")
|
||||
return jsonify({'success': False, 'error': str(e)}), 500
|
||||
|
||||
|
||||
# ==================== 위치 정보 API ====================
|
||||
|
||||
@app.route('/api/locations')
|
||||
|
||||
Reference in New Issue
Block a user