# 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()