feat: 회원 상세 모달 구현 (마일리지 + POS 이력)

- /api/members/history/<phone>: 통합 이력 조회 API
- 마일리지 적립/사용 내역 (SQLite)
- POS 구매 이력 (MSSQL - 전화번호→고객코드 매핑)
- 세련된 UI: 탭 전환, 거래 카드, 구매 카드
- 상세에서 바로 메시지 발송 가능
This commit is contained in:
thug0bin
2026-02-27 15:08:09 +09:00
parent a7e96e5efa
commit 7843ca8fcf
2 changed files with 503 additions and 4 deletions

View File

@@ -3004,6 +3004,146 @@ def api_member_detail(cuscode):
return jsonify({'success': False, 'error': str(e)}), 500
@app.route('/api/members/history/<phone>')
def api_member_history(phone):
"""
회원 구매 이력 통합 조회 API
- 마일리지 적립/사용 내역 (SQLite)
- POS 구매 이력 (MSSQL)
"""
try:
# 전화번호 정규화
phone = phone.replace('-', '').replace(' ', '')
result = {
'success': True,
'phone': phone,
'mileage': None,
'purchases': []
}
# 1. 마일리지 내역 조회 (SQLite)
try:
sqlite_conn = db_manager.get_sqlite_connection()
cursor = sqlite_conn.cursor()
# 사용자 정보 조회
cursor.execute("""
SELECT id, nickname, phone, mileage_balance, created_at
FROM users WHERE phone = ?
""", (phone,))
user = cursor.fetchone()
if user:
user_id = user['id']
# 적립/사용 내역 조회
cursor.execute("""
SELECT
ml.points, ml.balance_after, ml.reason,
ml.description, ml.transaction_id, ml.created_at
FROM mileage_ledger ml
WHERE ml.user_id = ?
ORDER BY ml.created_at DESC
LIMIT 50
""", (user_id,))
transactions = cursor.fetchall()
result['mileage'] = {
'user_id': user_id,
'name': user['nickname'] or '',
'phone': user['phone'],
'balance': user['mileage_balance'] or 0,
'member_since': user['created_at'],
'transactions': [{
'points': t['points'],
'balance_after': t['balance_after'],
'reason': t['reason'],
'description': t['description'],
'transaction_id': t['transaction_id'],
'created_at': t['created_at']
} for t in transactions]
}
except Exception as e:
logging.warning(f"마일리지 조회 실패: {e}")
# 2. POS 구매 이력 조회 (MSSQL)
try:
base_session = db_manager.get_session('PM_BASE')
pres_session = db_manager.get_session('PM_PRES')
drug_session = db_manager.get_session('PM_DRUG')
# 전화번호로 고객코드 조회
cuscode_query = text("""
SELECT TOP 1 CUSCODE, PANAME
FROM CD_PERSON
WHERE REPLACE(REPLACE(PHONE, '-', ''), ' ', '') = :phone
OR REPLACE(REPLACE(TEL_NO, '-', ''), ' ', '') = :phone
OR REPLACE(REPLACE(PHONE2, '-', ''), ' ', '') = :phone
""")
cus_row = base_session.execute(cuscode_query, {'phone': phone}).fetchone()
if cus_row:
cuscode = cus_row.CUSCODE
result['pos_customer'] = {
'cuscode': cuscode,
'name': cus_row.PANAME
}
# 구매 이력 조회 (최근 30일)
purchase_query = text("""
SELECT
M.SL_NO_order as order_no,
M.SL_DT_appl as order_date,
M.SL_MY_total as total_amount,
M.SL_MY_discount as discount
FROM SALE_MAIN M
WHERE M.SL_CD_custom = :cuscode
AND M.SL_DT_appl >= CONVERT(VARCHAR(8), DATEADD(DAY, -30, GETDATE()), 112)
ORDER BY M.SL_DT_appl DESC, M.SL_NO_order DESC
""")
orders = pres_session.execute(purchase_query, {'cuscode': cuscode}).fetchall()
purchases = []
for order in orders[:20]: # 최대 20건
# 주문 상세 (품목)
items_query = text("""
SELECT
S.DrugCode,
G.GoodsName,
S.QUAN as quantity,
S.SL_TOTAL_PRICE as price
FROM SALE_SUB S
LEFT JOIN PM_DRUG.dbo.CD_GOODS G ON S.DrugCode = G.DrugCode
WHERE S.SL_NO_order = :order_no
""")
items = pres_session.execute(items_query, {'order_no': order.order_no}).fetchall()
purchases.append({
'order_no': order.order_no,
'date': order.order_date,
'total': float(order.total_amount) if order.total_amount else 0,
'discount': float(order.discount) if order.discount else 0,
'items': [{
'drug_code': item.DrugCode,
'name': item.GoodsName or '알 수 없음',
'quantity': float(item.quantity) if item.quantity else 1,
'price': float(item.price) if item.price else 0
} for item in items]
})
result['purchases'] = purchases
except Exception as e:
logging.warning(f"POS 구매 이력 조회 실패: {e}")
return jsonify(result)
except Exception as e:
logging.error(f"회원 이력 조회 오류: {e}")
return jsonify({'success': False, 'error': str(e)}), 500
# =============================================================================
# 알림톡/SMS 발송 API
# =============================================================================