165 lines
6.8 KiB
Python
165 lines
6.8 KiB
Python
# app/services/motivation_service.py
|
|
"""
|
|
HealthSync Motivator Batch 독려 메시지 생성 서비스
|
|
"""
|
|
import asyncio
|
|
from typing import Dict, Any, List
|
|
from datetime import datetime
|
|
from app.utils.database_utils import db
|
|
from app.utils.claude_client import claude_client
|
|
from app.utils.logger import batch_logger
|
|
from app.config.prompts import get_encouragement_prompt
|
|
from app.repositories.queries import UserQueries, GoalQueries, ChatQueries
|
|
from app.models.chat_message import MessageType
|
|
|
|
|
|
class MotivationService:
|
|
"""독려 메시지 생성 비즈니스 로직 서비스"""
|
|
|
|
def __init__(self):
|
|
self.logger = batch_logger
|
|
|
|
async def process_all_users(self) -> Dict[str, int]:
|
|
"""모든 활성 사용자에 대해 독려 메시지 처리"""
|
|
try:
|
|
self.logger.info("🚀 독려 메시지 배치 처리 시작")
|
|
|
|
# 1. 활성 사용자 목록 조회
|
|
active_users = await db.execute_query(UserQueries.GET_ACTIVE_USERS)
|
|
self.logger.info(f"📊 활성 사용자 수: {len(active_users)}명")
|
|
|
|
# 처리 결과 카운터
|
|
results = {
|
|
"total_users": len(active_users),
|
|
"processed_users": 0,
|
|
"sent_messages": 0,
|
|
"skipped_users": 0,
|
|
"failed_users": 0
|
|
}
|
|
|
|
# 2. 각 사용자별 독려 메시지 처리
|
|
for user in active_users:
|
|
try:
|
|
user_id = user["member_serial_number"]
|
|
self.logger.info(f"👤 사용자 처리 시작: {user['name']} (ID: {user_id})")
|
|
|
|
# 사용자별 독려 메시지 처리
|
|
message_sent = await self._process_user_encouragement(user)
|
|
|
|
results["processed_users"] += 1
|
|
if message_sent:
|
|
results["sent_messages"] += 1
|
|
else:
|
|
results["skipped_users"] += 1
|
|
|
|
except Exception as e:
|
|
self.logger.error(f"❌ 사용자 처리 실패 - user_id: {user.get('member_serial_number')}, error: {str(e)}")
|
|
results["failed_users"] += 1
|
|
|
|
self.logger.info(f"✅ 독려 메시지 배치 처리 완료: {results}")
|
|
return results
|
|
|
|
except Exception as e:
|
|
self.logger.error(f"❌ 배치 처리 전체 실패: {str(e)}")
|
|
raise Exception(f"배치 처리 실패: {str(e)}")
|
|
|
|
async def _process_user_encouragement(self, user: Dict[str, Any]) -> bool:
|
|
"""개별 사용자 독려 메시지 처리"""
|
|
try:
|
|
user_id = user["member_serial_number"]
|
|
user_name = user["name"]
|
|
occupation = user.get("occupation", "정보 없음")
|
|
|
|
# # 1. 오늘 이미 독려 메시지를 받았는지 확인
|
|
# today_encouragement = await db.execute_query(
|
|
# ChatQueries.CHECK_TODAY_ENCOURAGEMENT,
|
|
# {"user_id": user_id}
|
|
# )
|
|
#
|
|
# if today_encouragement and today_encouragement[0]["count"] > 0:
|
|
# self.logger.info(f"⏭️ 이미 독려 메시지 발송됨 - user: {user_name}")
|
|
# return False
|
|
|
|
# 2. 미완료 미션 조회
|
|
incomplete_missions = await db.execute_query(
|
|
GoalQueries.GET_INCOMPLETE_MISSIONS,
|
|
{"user_id": user_id}
|
|
)
|
|
|
|
if not incomplete_missions:
|
|
self.logger.info(f"✅ 미완료 미션 없음 - user: {user_name}")
|
|
return False
|
|
|
|
# 3. 독려 메시지 생성
|
|
encouragement_message = await self._generate_encouragement_message(
|
|
occupation, incomplete_missions
|
|
)
|
|
|
|
# 4. 채팅 DB에 저장
|
|
await self._save_encouragement_message(user_id, encouragement_message)
|
|
|
|
self.logger.info(f"💌 독려 메시지 발송 완료 - user: {user_name}, "
|
|
f"미완료 미션: {len(incomplete_missions)}개")
|
|
return True
|
|
|
|
except Exception as e:
|
|
self.logger.error(f"❌ 사용자 독려 메시지 처리 실패 - user_id: {user_id}, error: {str(e)}")
|
|
raise Exception(f"사용자 독려 메시지 처리 실패: {str(e)}")
|
|
|
|
async def _generate_encouragement_message(self, occupation: str, incomplete_missions: List[Dict[str, Any]]) -> str:
|
|
"""Claude API를 통한 독려 메시지 생성"""
|
|
try:
|
|
# 미완료 미션 목록 문자열 생성
|
|
mission_list = []
|
|
for mission in incomplete_missions:
|
|
mission_list.append(f"- {mission['mission_name']} (목표: {mission['daily_target_count']}회)")
|
|
|
|
missions_text = "\n".join(mission_list)
|
|
|
|
# 프롬프트 생성
|
|
prompt_template = get_encouragement_prompt()
|
|
formatted_prompt = prompt_template.format(
|
|
occupation=occupation,
|
|
incomplete_missions=missions_text
|
|
)
|
|
|
|
# Claude API 호출
|
|
claude_response = await claude_client.call_claude_api(formatted_prompt)
|
|
|
|
# 응답 정제 (앞뒤 공백 제거, 따옴표 제거)
|
|
encouragement_message = claude_response.strip().strip('"').strip("'")
|
|
|
|
self.logger.info(f"💭 독려 메시지 생성 완료 - 길이: {len(encouragement_message)}자")
|
|
return encouragement_message
|
|
|
|
except Exception as e:
|
|
self.logger.error(f"❌ 독려 메시지 생성 실패: {str(e)}")
|
|
raise Exception(f"독려 메시지 생성 실패: {str(e)}")
|
|
|
|
async def _save_encouragement_message(self, user_id: int, message: str) -> None:
|
|
"""독려 메시지를 채팅 DB에 저장 (수정된 메서드)"""
|
|
try:
|
|
# ChatMessage 모델에 맞춰 데이터 구성
|
|
message_data = {
|
|
"member_serial_number": user_id,
|
|
"message_type": MessageType.ENCOURAGEMENT.value, # "encouragement"
|
|
"message_content": None, # 사용자 입력이 없으므로 None
|
|
"response_content": message, # AI가 생성한 독려 메시지
|
|
"created_at": datetime.now()
|
|
}
|
|
|
|
await db.execute_insert(
|
|
ChatQueries.INSERT_ENCOURAGEMENT_MESSAGE,
|
|
message_data
|
|
)
|
|
|
|
self.logger.info(f"💾 독려 메시지 DB 저장 완료 - user_id: {user_id}, "
|
|
f"message_type: {MessageType.ENCOURAGEMENT.value}")
|
|
|
|
except Exception as e:
|
|
self.logger.error(f"❌ 독려 메시지 DB 저장 실패 - user_id: {user_id}, error: {str(e)}")
|
|
raise Exception(f"독려 메시지 DB 저장 실패: {str(e)}")
|
|
|
|
|
|
# 전역 서비스 인스턴스
|
|
motivation_service = MotivationService() |