feat: 도매상 API 통합 및 스키마 업데이트
- wholesale 패키지 연동 (SooinSession, GeoYoungSession) - Flask Blueprint 분리 (sooin_api.py, geoyoung_api.py) - order_context 스키마 확장 (wholesaler_id, internal_code 등) - 수인약품 개별 취소 기능 (cancel_item, restore_item) - 문서 추가: WHOLESALE_API_INTEGRATION.md - 테스트 스크립트들
This commit is contained in:
316
backend/geoyoung_api.py
Normal file
316
backend/geoyoung_api.py
Normal file
@@ -0,0 +1,316 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
지오영 도매상 API - Flask Blueprint
|
||||
|
||||
핵심 로직은 wholesale 패키지에서 가져옴
|
||||
이 파일은 Flask 웹 API 연동만 담당
|
||||
"""
|
||||
|
||||
import re
|
||||
import time
|
||||
import logging
|
||||
|
||||
from flask import Blueprint, jsonify, request
|
||||
|
||||
# wholesale 패키지 경로 설정
|
||||
import wholesale_path
|
||||
|
||||
# wholesale 패키지에서 핵심 클래스 가져오기
|
||||
from wholesale import GeoYoungSession
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Blueprint 생성
|
||||
geoyoung_bp = Blueprint('geoyoung', __name__, url_prefix='/api/geoyoung')
|
||||
|
||||
|
||||
# ========== 세션 관리 ==========
|
||||
|
||||
_geo_session = None
|
||||
|
||||
def get_geo_session():
|
||||
global _geo_session
|
||||
if _geo_session is None:
|
||||
_geo_session = GeoYoungSession()
|
||||
return _geo_session
|
||||
|
||||
|
||||
def search_geoyoung_stock(keyword: str):
|
||||
"""지오영 재고 검색 (동기, 빠름)"""
|
||||
try:
|
||||
session = get_geo_session()
|
||||
products = session.search_stock(keyword)
|
||||
|
||||
return {
|
||||
'success': True,
|
||||
'keyword': keyword,
|
||||
'count': len(products),
|
||||
'items': products
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"지오영 검색 오류: {e}")
|
||||
return {'success': False, 'error': 'SEARCH_ERROR', 'message': str(e)}
|
||||
|
||||
|
||||
# ========== Flask API Routes ==========
|
||||
|
||||
@geoyoung_bp.route('/stock', methods=['GET'])
|
||||
def api_geoyoung_stock():
|
||||
"""
|
||||
지오영 재고 조회 API (빠름)
|
||||
|
||||
GET /api/geoyoung/stock?kd_code=670400830
|
||||
GET /api/geoyoung/stock?keyword=레바미피드
|
||||
"""
|
||||
kd_code = request.args.get('kd_code', '').strip()
|
||||
keyword = request.args.get('keyword', '').strip()
|
||||
|
||||
search_term = kd_code or keyword
|
||||
|
||||
if not search_term:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': 'MISSING_PARAM',
|
||||
'message': 'kd_code 또는 keyword 파라미터가 필요합니다'
|
||||
}), 400
|
||||
|
||||
try:
|
||||
result = search_geoyoung_stock(search_term)
|
||||
return jsonify(result)
|
||||
except Exception as e:
|
||||
logger.error(f"지오영 API 오류: {e}")
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': 'API_ERROR',
|
||||
'message': str(e)
|
||||
}), 500
|
||||
|
||||
|
||||
@geoyoung_bp.route('/stock-by-name', methods=['GET'])
|
||||
def api_geoyoung_stock_by_name():
|
||||
"""
|
||||
제품명에서 성분명 추출 후 지오영 검색
|
||||
|
||||
GET /api/geoyoung/stock-by-name?product_name=휴니즈레바미피드정_(0.1g/1정)
|
||||
"""
|
||||
product_name = request.args.get('product_name', '').strip()
|
||||
|
||||
if not product_name:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': 'MISSING_PARAM',
|
||||
'message': 'product_name 파라미터가 필요합니다'
|
||||
}), 400
|
||||
|
||||
# 성분명 추출
|
||||
prefixes = ['휴니즈', '휴온스', '대웅', '한미', '종근당', '유한', '녹십자', '동아', '일동', '광동',
|
||||
'삼성', '안국', '보령', '광동', '경동', '현대', '일양', '태극', '환인', '에스케이']
|
||||
ingredient = product_name
|
||||
|
||||
for prefix in prefixes:
|
||||
if ingredient.startswith(prefix):
|
||||
ingredient = ingredient[len(prefix):]
|
||||
break
|
||||
|
||||
match = re.match(r'^([가-힣a-zA-Z]+)', ingredient)
|
||||
if match:
|
||||
ingredient = match.group(1)
|
||||
if ingredient.endswith('정'):
|
||||
ingredient = ingredient[:-1]
|
||||
elif ingredient.endswith('캡슐'):
|
||||
ingredient = ingredient[:-2]
|
||||
|
||||
if not ingredient:
|
||||
ingredient = product_name[:10]
|
||||
|
||||
try:
|
||||
result = search_geoyoung_stock(ingredient)
|
||||
result['extracted_ingredient'] = ingredient
|
||||
result['original_product_name'] = product_name
|
||||
return jsonify(result)
|
||||
except Exception as e:
|
||||
logger.error(f"지오영 API 오류: {e}")
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': 'API_ERROR',
|
||||
'message': str(e)
|
||||
}), 500
|
||||
|
||||
|
||||
@geoyoung_bp.route('/session-status', methods=['GET'])
|
||||
def api_session_status():
|
||||
"""세션 상태 확인"""
|
||||
session = get_geo_session()
|
||||
return jsonify({
|
||||
'logged_in': session._logged_in,
|
||||
'last_login': session._last_login,
|
||||
'session_age_sec': int(time.time() - session._last_login) if session._last_login else None
|
||||
})
|
||||
|
||||
|
||||
@geoyoung_bp.route('/cart', methods=['GET'])
|
||||
def api_geoyoung_cart():
|
||||
"""장바구니 조회 API"""
|
||||
try:
|
||||
session = get_geo_session()
|
||||
result = session.get_cart()
|
||||
return jsonify(result)
|
||||
except Exception as e:
|
||||
return jsonify({'success': False, 'error': str(e), 'items': []}), 500
|
||||
|
||||
|
||||
@geoyoung_bp.route('/cart/clear', methods=['POST'])
|
||||
def api_geoyoung_cart_clear():
|
||||
"""장바구니 비우기 API"""
|
||||
try:
|
||||
session = get_geo_session()
|
||||
result = session.clear_cart()
|
||||
return jsonify(result)
|
||||
except Exception as e:
|
||||
return jsonify({'success': False, 'error': str(e)}), 500
|
||||
|
||||
|
||||
@geoyoung_bp.route('/confirm', methods=['POST'])
|
||||
def api_geoyoung_confirm():
|
||||
"""주문 확정 API"""
|
||||
data = request.get_json() or {}
|
||||
memo = data.get('memo', '')
|
||||
|
||||
try:
|
||||
session = get_geo_session()
|
||||
result = session.submit_order(memo)
|
||||
return jsonify(result)
|
||||
except Exception as e:
|
||||
return jsonify({'success': False, 'error': str(e)}), 500
|
||||
|
||||
|
||||
@geoyoung_bp.route('/full-order', methods=['POST'])
|
||||
def api_geoyoung_full_order():
|
||||
"""전체 주문 API (검색 → 장바구니 → 확정)"""
|
||||
data = request.get_json()
|
||||
|
||||
if not data or not data.get('kd_code'):
|
||||
return jsonify({'success': False, 'error': 'kd_code required'}), 400
|
||||
|
||||
try:
|
||||
session = get_geo_session()
|
||||
result = session.full_order(
|
||||
kd_code=data['kd_code'],
|
||||
quantity=data.get('quantity', 1),
|
||||
specification=data.get('specification'),
|
||||
check_stock=data.get('check_stock', True),
|
||||
auto_confirm=data.get('auto_confirm', True),
|
||||
memo=data.get('memo', '')
|
||||
)
|
||||
return jsonify(result)
|
||||
except Exception as e:
|
||||
return jsonify({'success': False, 'error': str(e)}), 500
|
||||
|
||||
|
||||
@geoyoung_bp.route('/order', methods=['POST'])
|
||||
def api_geoyoung_order():
|
||||
"""지오영 주문 API (장바구니 추가)"""
|
||||
data = request.get_json()
|
||||
|
||||
if not data:
|
||||
return jsonify({'success': False, 'error': 'NO_DATA'}), 400
|
||||
|
||||
kd_code = data.get('kd_code', '').strip()
|
||||
quantity = data.get('quantity', 1)
|
||||
specification = data.get('specification')
|
||||
check_stock = data.get('check_stock', True)
|
||||
|
||||
if not kd_code:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': 'MISSING_PARAM',
|
||||
'message': 'kd_code가 필요합니다'
|
||||
}), 400
|
||||
|
||||
try:
|
||||
session = get_geo_session()
|
||||
result = session.quick_order(
|
||||
kd_code=kd_code,
|
||||
quantity=quantity,
|
||||
spec=specification,
|
||||
check_stock=check_stock
|
||||
)
|
||||
return jsonify(result)
|
||||
except Exception as e:
|
||||
logger.error(f"지오영 주문 오류: {e}")
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': 'ORDER_ERROR',
|
||||
'message': str(e)
|
||||
}), 500
|
||||
|
||||
|
||||
@geoyoung_bp.route('/order-batch', methods=['POST'])
|
||||
def api_geoyoung_order_batch():
|
||||
"""지오영 일괄 주문 API"""
|
||||
data = request.get_json()
|
||||
|
||||
if not data or not data.get('items'):
|
||||
return jsonify({'success': False, 'error': 'NO_ITEMS'}), 400
|
||||
|
||||
items = data.get('items', [])
|
||||
check_stock = data.get('check_stock', True)
|
||||
|
||||
session = get_geo_session()
|
||||
results = []
|
||||
success_count = 0
|
||||
failed_count = 0
|
||||
|
||||
for item in items:
|
||||
kd_code = item.get('kd_code', '').strip()
|
||||
quantity = item.get('quantity', 1)
|
||||
specification = item.get('specification')
|
||||
|
||||
if not kd_code:
|
||||
results.append({
|
||||
'kd_code': kd_code,
|
||||
'success': False,
|
||||
'error': 'MISSING_KD_CODE'
|
||||
})
|
||||
failed_count += 1
|
||||
continue
|
||||
|
||||
try:
|
||||
result = session.quick_order(
|
||||
kd_code=kd_code,
|
||||
quantity=quantity,
|
||||
spec=specification,
|
||||
check_stock=check_stock
|
||||
)
|
||||
result['kd_code'] = kd_code
|
||||
result['requested_qty'] = quantity
|
||||
results.append(result)
|
||||
|
||||
if result.get('success'):
|
||||
success_count += 1
|
||||
else:
|
||||
failed_count += 1
|
||||
|
||||
except Exception as e:
|
||||
results.append({
|
||||
'kd_code': kd_code,
|
||||
'success': False,
|
||||
'error': 'EXCEPTION',
|
||||
'message': str(e)
|
||||
})
|
||||
failed_count += 1
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'total': len(items),
|
||||
'success_count': success_count,
|
||||
'failed_count': failed_count,
|
||||
'results': results
|
||||
})
|
||||
|
||||
|
||||
# ========== 하위 호환성 ==========
|
||||
|
||||
# 기존 코드에서 직접 클래스 참조하는 경우를 위해
|
||||
GeoyoungSession = GeoYoungSession
|
||||
Reference in New Issue
Block a user