feat: marketing tip

This commit is contained in:
unknown 2025-06-16 14:43:45 +09:00
parent b5d896aadf
commit 55df795791
5 changed files with 603 additions and 0 deletions

View File

@ -0,0 +1,131 @@
"""
마케팅 생성 API 엔드포인트
Java 서비스와 연동되는 API
"""
from flask import Blueprint, request, jsonify
from datetime import datetime
import logging
from services.marketing_tip_service import MarketingTipService
from models.marketing_tip_models import MarketingTipGenerateRequest, MarketingTipResponse
logger = logging.getLogger(__name__)
# Blueprint 생성
marketing_tip_bp = Blueprint('marketing_tip', __name__)
# 서비스 인스턴스
marketing_tip_service = MarketingTipService()
@marketing_tip_bp.route('/api/v1/generate-marketing-tip', methods=['POST'])
def generate_marketing_tip():
"""
AI 마케팅 생성 API
Java 서비스에서 호출하는 엔드포인트
"""
try:
# 요청 데이터 검증
if not request.is_json:
return jsonify({
'tip': '',
'status': 'error',
'message': 'Content-Type이 application/json이어야 합니다.',
'generated_at': '',
'store_name': '',
'business_type': '',
'ai_model': ''
}), 400
data = request.get_json()
if not data:
return jsonify({
'tip': '',
'status': 'error',
'message': '요청 데이터가 없습니다.',
'generated_at': '',
'store_name': '',
'business_type': '',
'ai_model': ''
}), 400
# 필수 필드 검증
if 'store_name' not in data or not data['store_name']:
return jsonify({
'tip': '',
'status': 'error',
'message': '매장명(store_name)은 필수입니다.',
'generated_at': '',
'store_name': '',
'business_type': '',
'ai_model': ''
}), 400
if 'business_type' not in data or not data['business_type']:
return jsonify({
'tip': '',
'status': 'error',
'message': '업종(business_type)은 필수입니다.',
'generated_at': '',
'store_name': '',
'business_type': '',
'ai_model': ''
}), 400
logger.info(f"마케팅 팁 생성 요청: {data.get('store_name', 'Unknown')}")
# 요청 모델 생성
try:
request_model = MarketingTipGenerateRequest(**data)
except ValueError as e:
return jsonify({
'tip': '',
'status': 'error',
'message': f'요청 데이터 형식이 올바르지 않습니다: {str(e)}',
'generated_at': '',
'store_name': data.get('store_name', ''),
'business_type': data.get('business_type', ''),
'ai_model': ''
}), 400
# 매장 정보 구성
store_data = {
'store_name': request_model.store_name,
'business_type': request_model.business_type,
'location': request_model.location or '',
'seat_count': request_model.seat_count or 0
}
# 마케팅 팁 생성
result = marketing_tip_service.generate_marketing_tip(
store_data=store_data,
)
logger.info(f"마케팅 팁 생성 완료: {result.get('store_name', 'Unknown')}")
return jsonify(result), 200
except Exception as e:
logger.error(f"마케팅 팁 생성 API 오류: {str(e)}")
return jsonify({
'tip': '죄송합니다. 일시적인 오류로 마케팅 팁을 생성할 수 없습니다. 잠시 후 다시 시도해주세요.',
'status': 'error',
'message': f'서버 오류가 발생했습니다: {str(e)}',
'generated_at': '',
'store_name': data.get('store_name', '') if 'data' in locals() else '',
'business_type': data.get('business_type', '') if 'data' in locals() else '',
'ai_model': 'error'
}), 500
@marketing_tip_bp.route('/api/v1/health', methods=['GET'])
def health_check():
"""
헬스체크 API
"""
return jsonify({
'status': 'healthy',
'service': 'marketing-tip-api',
'timestamp': datetime.now().isoformat()
}), 200

View File

@ -13,6 +13,9 @@ from services.poster_service import PosterService
from services.sns_content_service import SnsContentService
from models.request_models import ContentRequest, PosterRequest, SnsContentGetRequest, PosterContentGetRequest
from services.poster_service_v2 import PosterServiceV2
from api.marketing_tip_api import marketing_tip_bp
def create_app():
@ -33,6 +36,9 @@ def create_app():
poster_service_v2 = PosterServiceV2()
sns_content_service = SnsContentService()
# Blueprint 등록
app.register_blueprint(marketing_tip_bp)
@app.route('/health', methods=['GET'])
def health_check():
"""헬스 체크 API"""

View File

@ -0,0 +1,93 @@
"""
마케팅 API 요청/응답 모델
"""
from pydantic import BaseModel, Field
from typing import Optional, Dict, Any, List
from datetime import datetime
class MenuInfo(BaseModel):
"""메뉴 정보 모델"""
menu_id: int = Field(..., description="메뉴 ID")
menu_name: str = Field(..., description="메뉴명")
category: str = Field(..., description="메뉴 카테고리")
price: int = Field(..., description="가격")
description: Optional[str] = Field(None, description="메뉴 설명")
class Config:
schema_extra = {
"example": {
"store_name": "더블샷 카페",
"business_type": "카페",
"location": "서울시 강남구 역삼동",
"seat_count": 30,
"menu_list": [
{
"menu_id": 1,
"menu_name": "아메리카노",
"category": "음료",
"price": 4000,
"description": "깊고 진한 맛의 아메리카노"
},
{
"menu_id": 2,
"menu_name": "카페라떼",
"category": "음료",
"price": 4500,
"description": "부드러운 우유 거품이 올라간 카페라떼"
},
{
"menu_id": 3,
"menu_name": "치즈케이크",
"category": "디저트",
"price": 6000,
"description": "진한 치즈 맛의 수제 케이크"
}
],
"additional_requirement": "젊은 고객층을 타겟으로 한 마케팅"
}
}
class MarketingTipGenerateRequest(BaseModel):
"""마케팅 팁 생성 요청 모델"""
store_name: str = Field(..., description="매장명")
business_type: str = Field(..., description="업종")
location: Optional[str] = Field(None, description="위치")
seat_count: Optional[int] = Field(None, description="좌석 수")
menu_list: Optional[List[MenuInfo]] = Field(default=[], description="메뉴 목록")
class Config:
schema_extra = {
"example": {
"store_name": "더블샷 카페",
"business_type": "카페",
"location": "서울시 강남구 역삼동",
"seat_count": 30,
}
}
class MarketingTipResponse(BaseModel):
"""마케팅 팁 응답 모델"""
tip: str = Field(..., description="생성된 마케팅 팁")
status: str = Field(..., description="응답 상태 (success, fallback, error)")
message: str = Field(..., description="응답 메시지")
generated_at: str = Field(..., description="생성 시간")
store_name: str = Field(..., description="매장명")
business_type: str = Field(..., description="업종")
ai_model: str = Field(..., description="사용된 AI 모델")
class Config:
schema_extra = {
"example": {
"tip": "☕ 더블샷 카페 여름 마케팅 전략\n\n💡 핵심 포인트:\n1. 여름 한정 시원한 음료 개발\n2. SNS 이벤트로 젊은 고객층 공략\n3. 더위 피할 수 있는 쾌적한 환경 어필",
"status": "success",
"message": "AI 마케팅 팁이 성공적으로 생성되었습니다.",
"generated_at": "2024-06-13T15:30:00",
"store_name": "더블샷 카페",
"business_type": "카페",
"ai_model": "claude"
}
}

View File

@ -0,0 +1,331 @@
"""
마케팅 생성 서비스
Java 서비스에서 요청받은 매장 정보를 기반으로 AI 마케팅 팁을 생성
"""
import os
import logging
from typing import Dict, Any, Optional
import anthropic
import openai
from datetime import datetime
# 로깅 설정
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class MarketingTipService:
"""마케팅 팁 생성 서비스 클래스"""
def __init__(self):
"""서비스 초기화"""
self.claude_api_key = os.getenv('CLAUDE_API_KEY')
self.openai_api_key = os.getenv('OPENAI_API_KEY')
# Claude 클라이언트 초기화
if self.claude_api_key:
self.claude_client = anthropic.Anthropic(api_key=self.claude_api_key)
else:
self.claude_client = None
logger.warning("Claude API 키가 설정되지 않았습니다.")
# OpenAI 클라이언트 초기화
if self.openai_api_key:
self.openai_client = openai.OpenAI(api_key=self.openai_api_key)
else:
self.openai_client = None
logger.warning("OpenAI API 키가 설정되지 않았습니다.")
def generate_marketing_tip(self, store_data: Dict[str, Any], additional_requirement: Optional[str] = None) -> Dict[str, Any]:
"""
매장 정보를 기반으로 AI 마케팅 생성
Args:
store_data: 매장 정보 (store_name, business_type, location )
Returns:
생성된 마케팅 팁과 메타데이터
"""
try:
logger.info(f"마케팅 팁 생성 시작: {store_data.get('store_name', 'Unknown')}")
# 1. 프롬프트 생성
prompt = self._create_marketing_prompt(store_data, additional_requirement)
# 2. AI 서비스 호출 (Claude 우선, 실패 시 OpenAI)
tip_content = self._call_ai_service(prompt)
# 3. 응답 데이터 구성
response = {
'tip': tip_content,
'status': 'success',
'message': 'AI 마케팅 팁이 성공적으로 생성되었습니다.',
'generated_at': datetime.now().isoformat(),
'store_name': store_data.get('store_name', ''),
'business_type': store_data.get('business_type', ''),
'ai_model': 'claude' if self.claude_client else 'openai'
}
logger.info(f"마케팅 팁 생성 완료: {store_data.get('store_name', 'Unknown')}")
logger.info(f"마케팅 팁 생성 완료: {response}")
return response
except Exception as e:
logger.error(f"마케팅 팁 생성 실패: {str(e)}")
# 실패 시 Fallback 팁 반환
fallback_tip = self._create_fallback_tip(store_data, additional_requirement)
return {
'tip': fallback_tip,
'status': 'fallback',
'message': 'AI 서비스 호출 실패로 기본 팁을 제공합니다.',
'generated_at': datetime.now().isoformat(),
'store_name': store_data.get('store_name', ''),
'business_type': store_data.get('business_type', ''),
'ai_model': 'fallback'
}
def _create_marketing_prompt(self, store_data: Dict[str, Any], additional_requirement: Optional[str]) -> str:
"""마케팅 팁 생성을 위한 프롬프트 생성"""
store_name = store_data.get('store_name', '매장')
business_type = store_data.get('business_type', '소상공인')
location = store_data.get('location', '')
seat_count = store_data.get('seat_count', 0)
menu_list = store_data.get('menu_list', [])
prompt = f"""
당신은 소상공인 마케팅 전문가입니다.
현재 유행하고 성공한 마케팅 예시를 검색하여 확인 , 참고하여 아래 내용을 작성해주세요.
당신의 임무는 매장 정보를 바탕으로, 적은 비용으로 효과를 있는 현실적이고 실행 가능한 마케팅 팁을 제안하는 것입니다.
지역성, 지역의 현재 날씨 확인하고, 현재 트렌드까지 고려해주세요.
소상공인을 위한 실용적인 마케팅 팁을 생성해주세요.
매장 정보:
- 매장명: {store_name}
- 업종: {business_type}
- 위치: {location}
- 좌석 : {seat_count}
"""
# 🔥 메뉴 정보 추가
if menu_list and len(menu_list) > 0:
prompt += f"\n메뉴 정보:\n"
for menu in menu_list:
menu_name = menu.get('menu_name', '')
category = menu.get('category', '')
price = menu.get('price', 0)
description = menu.get('description', '')
prompt += f"- {menu_name} ({category}): {price:,}원 - {description}\n"
prompt += """
아래 조건을 모두 충족하는 마케팅 팁을 하나 생성해주세요:
1. **실행 가능성**: 소상공인이 실제로 적용할 있는 현실적인 방법
2. **비용 효율성**: 적은 비용으로 높은 효과를 기대할 있는 전략
3. **구체성**: 실행 단계가 명확하고 구체적일
4. **시의성**: 현재 계절, 유행, 트렌드를 반영
5. **지역성**: 지역 특성 현재 날씨를 고려할
응답 형식 (300 내외, 간결하게):
html 형식으로 출력
핵심 마케팅 팁은 제목없이 한번 상단에 보여주세요
부제목과 내용은 분리해서 출력
아래의 부제목 앞에는 이모지 포함
- 핵심 마케팅 (1)
- 실행 방법 (1)
- 예상 비용과 기대 효과
- 주의사항 또는 유의점
- 참고했던 실제 성공한 마케팅
- 오늘의 응원의 문장 (간결하게 1)
심호흡하고, 단계별로 차근차근 생각해서 정확하고 실현 가능한 아이디어를 제시해주세요.
"""
return prompt
def _call_ai_service(self, prompt: str) -> str:
"""AI 서비스 호출"""
# Claude API 우선 시도
if self.claude_client:
try:
response = self.claude_client.messages.create(
model="claude-3-sonnet-20240229",
max_tokens=1000,
temperature=0.7,
messages=[
{
"role": "user",
"content": prompt
}
]
)
if response.content and len(response.content) > 0:
logger.info(f"마케팅 팁 생성 완료: {response.content}")
return response.content[0].text.strip()
except Exception as e:
logger.warning(f"Claude API 호출 실패: {str(e)}")
# OpenAI API 시도
if self.openai_client:
try:
response = self.openai_client.chat.completions.create(
model="gpt-3.5-turbo",
messages=[
{
"role": "system",
"content": "당신은 소상공인을 위한 마케팅 전문가입니다. 실용적이고 구체적인 마케팅 조언을 제공해주세요."
},
{
"role": "user",
"content": prompt
}
],
max_tokens=800,
temperature=0.7
)
if response.choices and len(response.choices) > 0:
return response.choices[0].message.content.strip()
except Exception as e:
logger.warning(f"OpenAI API 호출 실패: {str(e)}")
# 모든 AI 서비스 호출 실패
raise Exception("모든 AI 서비스 호출에 실패했습니다.")
def _create_fallback_tip(self, store_data: Dict[str, Any], additional_requirement: Optional[str]) -> str:
"""AI 서비스 실패 시 규칙 기반 Fallback 팁 생성"""
store_name = store_data.get('store_name', '매장')
business_type = store_data.get('business_type', '')
location = store_data.get('location', '')
menu_list = store_data.get('menu_list', [])
if menu_list and len(menu_list) > 0:
# 가장 비싼 메뉴 찾기 (시그니처 메뉴로 가정)
expensive_menu = max(menu_list, key=lambda x: x.get('price', 0), default=None)
# 카테고리별 메뉴 분석
categories = {}
for menu in menu_list:
category = menu.get('category', '기타')
if category not in categories:
categories[category] = []
categories[category].append(menu)
main_category = max(categories.keys(), key=lambda x: len(categories[x])) if categories else '메뉴'
if expensive_menu:
signature_menu = expensive_menu.get('menu_name', '시그니처 메뉴')
return f"""🎯 {store_name} 메뉴 기반 마케팅 전략
💡 핵심 전략:
- SNS를 활용한 홍보 강화
- 고객 리뷰 관리 적극 활용
- 지역 커뮤니티 참여로 인지도 향상
📱 실행 방법:
1. 인스타그램/네이버 블로그 정기 포스팅
2. 고객 만족도 조사 피드백 반영
3. 주변 상권과의 협력 이벤트 기획
💰 예상 효과: 매출 10-15% 증가 가능
주의사항: 꾸준한 실행과 고객 소통이 핵심"""
# 업종별 기본 팁
if '카페' in business_type or '커피' in business_type:
return f"""{store_name} 카페 마케팅 전략
💡 핵심 포인트:
1. 시그니처 음료 개발 SNS 홍보
2. 계절별 한정 메뉴로 재방문 유도
3. 인스타그램 포토존 설치
📱 실행 방법:
- 매주 신메뉴 또는 이벤트 인스타 포스팅
- 고객 사진 리포스트로 참여 유도
- 해시태그 #근처카페 #데이트코스 활용
💰 비용: 5-10만원 내외
📈 기대효과: 젊은 고객층 20% 증가"""
elif '음식점' in business_type or '식당' in business_type:
return f"""🍽️ {store_name} 음식점 마케팅 전략
💡 핵심 포인트:
1. 대표 메뉴 스토리텔링
2. 배달앱 리뷰 관리 강화
3. 단골 고객 혜택 프로그램
📱 실행 방법:
- 요리 과정 영상으로 신뢰도 구축
- 리뷰 적극 답변으로 고객 관리
- 방문 횟수별 할인 혜택 제공
💰 비용: 3-7만원 내외
📈 기대효과: 재방문율 25% 향상"""
elif '베이커리' in business_type or '빵집' in business_type:
return f"""🍞 {store_name} 베이커리 마케팅 전략
💡 핵심 포인트:
1. 구운 타이밍 알림 서비스
2. 계절 한정 출시
3. 포장 디자인으로 선물용 어필
📱 실행 방법:
- 네이버 톡톡으로 완성 시간 안내
- 명절/기념일 특별 한정 판매
- 예쁜 포장지로 브랜딩 강화
💰 비용: 5-8만원 내외
📈 기대효과: 단골 고객 30% 증가"""
# 지역별 특성 고려
if location:
location_tip = ""
if '강남' in location or '서초' in location:
location_tip = "\n🏢 강남권 특화: 직장인 대상 점심 세트메뉴 강화"
elif '홍대' in location or '신촌' in location:
location_tip = "\n🎓 대학가 특화: 학생 할인 및 그룹 이벤트 진행"
elif '강북' in location or '노원' in location:
location_tip = "\n🏘️ 주거지역 특화: 가족 단위 고객 대상 패키지 상품"
return f"""🎯 {store_name} 지역 맞춤 마케팅
💡 기본 전략:
- 온라인 리뷰 관리 강화
- 단골 고객 혜택 프로그램
- 지역 커뮤니티 참여{location_tip}
📱 실행 방법:
1. 구글/네이버 지도 정보 최신화
2. 동네 맘카페 홍보 참여
3. 주변 상권과 상생 이벤트
💰 비용: 3-5만원
📈 기대효과: 인지도 매출 향상"""
# 기본 범용 팁
return f"""🎯 {store_name} 기본 마케팅 전략
💡 핵심 3가지:
1. 온라인 존재감 강화 (SNS, 리뷰 관리)
2. 고객 소통 피드백 활용
3. 차별화된 서비스 제공
📱 실행 방법:
- 네이버 플레이스, 구글 정보 최신화
- 고객 불만 신속 해결로 신뢰 구축
- 작은 이벤트라도 꾸준히 진행
💰 비용: 거의 무료 (시간 투자 위주)
📈 기대효과: 꾸준한 성장과 단골 확보
핵심은 지속성입니다!"""

42
smarketing-ai/test.py Normal file
View File

@ -0,0 +1,42 @@
"""
마케팅 API 테스트 스크립트
"""
import requests
import json
def test_marketing_tip_api():
"""마케팅 팁 API 테스트"""
# 테스트 데이터
test_data = {
"store_name": "더블샷 카페",
"business_type": "카페",
"location": "서울시 강남구 역삼동",
"seat_count": 30,
}
# API 호출
url = "http://localhost:5001/api/v1/generate-marketing-tip"
headers = {
"Content-Type": "application/json",
"Authorization": "Bearer dummy-key"
}
try:
response = requests.post(url, json=test_data, headers=headers)
print(f"Status Code: {response.status_code}")
print(f"Response: {json.dumps(response.json(), ensure_ascii=False, indent=2)}")
if response.status_code == 200:
print("✅ 테스트 성공!")
else:
print("❌ 테스트 실패!")
except Exception as e:
print(f"❌ 테스트 오류: {str(e)}")
if __name__ == "__main__":
test_marketing_tip_api()