diff --git a/backend/app.py b/backend/app.py index fe5965e..ccabb91 100644 --- a/backend/app.py +++ b/backend/app.py @@ -3,13 +3,21 @@ Flask 웹 서버 - QR 마일리지 적립 간편 적립: 전화번호 + 이름만 입력 """ +import sys +import os + +# Windows 콘솔 UTF-8 강제 (한글 깨짐 방지) +if sys.platform == 'win32': + import io + sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8') + sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8') + os.environ.setdefault('PYTHONIOENCODING', 'utf-8') + from flask import Flask, request, render_template, jsonify, redirect, url_for, session import hashlib import base64 import secrets from datetime import datetime, timezone, timedelta -import sys -import os import logging from sqlalchemy import text from dotenv import load_dotenv @@ -2060,6 +2068,56 @@ def admin_alimtalk(): return render_template('admin_alimtalk.html', local_logs=local_logs, stats=stats) +@app.route('/admin/ai-crm') +def admin_ai_crm(): + """AI 업셀링 CRM 대시보드""" + conn = db_manager.get_sqlite_connection() + cursor = conn.cursor() + + # 추천 목록 (최근 50건) + 사용자 정보 JOIN + cursor.execute(""" + SELECT r.*, u.nickname, u.phone as user_phone + FROM ai_recommendations r + LEFT JOIN users u ON r.user_id = u.id + ORDER BY r.created_at DESC + LIMIT 50 + """) + recs = [dict(row) for row in cursor.fetchall()] + + # trigger_products JSON 파싱 + for rec in recs: + tp = rec.get('trigger_products') + if tp: + try: + rec['trigger_list'] = json.loads(tp) + except Exception: + rec['trigger_list'] = [tp] + else: + rec['trigger_list'] = [] + + # 통계 + cursor.execute(""" + SELECT + COUNT(*) as total, + SUM(CASE WHEN status = 'active' AND (expires_at IS NULL OR expires_at > datetime('now')) THEN 1 ELSE 0 END) as active_count, + SUM(CASE WHEN status = 'dismissed' THEN 1 ELSE 0 END) as dismissed_count, + SUM(CASE WHEN displayed_count > 0 THEN 1 ELSE 0 END) as displayed_count + FROM ai_recommendations + """) + stats = dict(cursor.fetchone()) + + # 오늘 생성 건수 + cursor.execute(""" + SELECT COUNT(*) as today_count + FROM ai_recommendations + WHERE date(created_at) = date('now') + """) + stats['today_count'] = cursor.fetchone()['today_count'] + + now = datetime.now().strftime('%Y-%m-%d %H:%M:%S') + return render_template('admin_ai_crm.html', recs=recs, stats=stats, now=now) + + @app.route('/api/admin/alimtalk/nhn-history') def api_admin_alimtalk_nhn_history(): """NHN Cloud 실제 발송 내역 API""" diff --git a/backend/services/clawdbot_client.py b/backend/services/clawdbot_client.py index 524b773..cd732d3 100644 --- a/backend/services/clawdbot_client.py +++ b/backend/services/clawdbot_client.py @@ -4,6 +4,17 @@ Clawdbot Gateway Python 클라이언트 추가 API 비용 없음 (Claude Max 구독 재활용) """ +import sys +import os + +# Windows 콘솔 UTF-8 강제 (한글 깨짐 방지) +if sys.platform == 'win32': + import io + if hasattr(sys.stdout, 'buffer'): + sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8') + sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8') + os.environ.setdefault('PYTHONIOENCODING', 'utf-8') + import json import uuid import asyncio diff --git a/backend/templates/admin.html b/backend/templates/admin.html index 7aae828..ebeffb2 100644 --- a/backend/templates/admin.html +++ b/backend/templates/admin.html @@ -398,7 +398,10 @@
📊 관리자 대시보드
청춘약국 마일리지 관리
- 📨 알림톡 로그 +
+ 🤖 AI CRM + 📨 알림톡 로그 +
diff --git a/backend/templates/admin_ai_crm.html b/backend/templates/admin_ai_crm.html new file mode 100644 index 0000000..9c47654 --- /dev/null +++ b/backend/templates/admin_ai_crm.html @@ -0,0 +1,412 @@ + + + + + + AI 업셀링 CRM - 청춘약국 + + + + + + +
+
+ ← 관리자 홈 + 알림톡 로그 → +
+

AI 업셀링 CRM

+

구매 기반 맞춤 추천 생성 현황 · Clawdbot Gateway

+
+ +
+ +
+
+
전체 생성
+
{{ stats.total or 0 }}
+
+
+
Active
+
{{ stats.active_count or 0 }}
+
+
+
고객 반응 (닫기)
+
{{ stats.dismissed_count or 0 }}
+
+
+
오늘 생성
+
{{ stats.today_count or 0 }}
+
+
+ + +
+
추천 생성 로그
+
최근 50건 · 클릭하여 상세 보기
+
+ + {% if recs %} +
+ + + + + + + + + + + + + + {% for rec in recs %} + + + + + + + + + + + + + + {% endfor %} + +
생성일시고객트리거 품목추천 제품AI 메시지상태노출
+ {{ rec.created_at[5:16] if rec.created_at else '-' }} + +
{{ rec.nickname or '알 수 없음' }}
+ {% if rec.user_phone %} +
{{ rec.user_phone[:3] }}-****-{{ rec.user_phone[-4:] }}
+ {% endif %} +
+ {% if rec.trigger_list %} + {% for item in rec.trigger_list %} + {{ item }} + {% endfor %} + {% else %} + - + {% endif %} + + {{ rec.recommended_product }} + +
{{ rec.recommendation_message }}
+
+ {% if rec.status == 'active' and (not rec.expires_at or rec.expires_at > now) %} + Active + {% elif rec.status == 'dismissed' %} + Dismissed + {% else %} + Expired + {% endif %} + + {{ rec.displayed_count or 0 }} +
+
+
+
+
추천 이유
+
{{ rec.recommendation_reason or '-' }}
+
+
+
거래 ID
+
{{ rec.transaction_id or '-' }}
+
+
+
노출 일시
+
{{ rec.displayed_at or '미노출' }}
+
+
+
닫기 일시
+
{{ rec.dismissed_at or '-' }}
+
+
+
만료 일시
+
{{ rec.expires_at or '없음' }}
+
+
+
노출 횟수
+
{{ rec.displayed_count or 0 }}회
+
+
+ {% if rec.ai_raw_response %} +
+
AI 원본 응답
+
{{ rec.ai_raw_response }}
+
+ {% endif %} +
+
+
+ {% else %} +
+
+
🤖
+
아직 생성된 AI 추천이 없습니다
+
+
+ {% endif %} +
+ + + + diff --git a/backend/templates/my_page.html b/backend/templates/my_page.html index db7a170..d155367 100644 --- a/backend/templates/my_page.html +++ b/backend/templates/my_page.html @@ -396,16 +396,21 @@