diff --git a/apps/feishu/views.py b/apps/feishu/views.py index 49261e8..53006c9 100644 --- a/apps/feishu/views.py +++ b/apps/feishu/views.py @@ -17,6 +17,9 @@ from rest_framework.permissions import IsAuthenticated from apps.gmail.models import GmailCredential, GmailConversation, AutoReplyConfig from apps.gmail.services.gmail_service import GmailService from apps.gmail.serializers import AutoReplyConfigSerializer +from apps.gmail.services.goal_service import get_or_create_goal, get_conversation_summary +from apps.chat.models import ChatHistory +from apps.knowledge_base.models import KnowledgeBase logger = logging.getLogger(__name__) @@ -295,7 +298,7 @@ class AutoGmailConversationView(APIView): def post(self, request, *args, **kwargs): """ - 创建自动对话,发送第一条打招呼消息 + 创建自动对话,根据需要发送第一条打招呼消息 请求参数: - user_email: 用户的Gmail邮箱(已授权) @@ -317,42 +320,140 @@ class AutoGmailConversationView(APIView): 'data': None }, status=status.HTTP_400_BAD_REQUEST) - # 获取或创建活跃对话 - # 先查找现有对话 + # 验证Gmail凭证 + credential = GmailCredential.objects.filter(user=request.user, email=user_email).first() + if not credential: + return Response({ + 'code': 400, + 'message': f"未找到{user_email}的Gmail授权", + 'data': None + }, status=status.HTTP_400_BAD_REQUEST) + + logger.info(f"开始创建/更新Gmail自动对话: 用户={request.user.username}, 邮箱={user_email}, 达人={influencer_email}") + + # 查找现有对话 existing_conversation = GmailConversation.objects.filter( user=request.user, user_email=user_email, influencer_email=influencer_email ).first() - if existing_conversation: - # 激活现有对话 - existing_conversation.is_active = True - existing_conversation.save() - logger.info(f"找到并激活现有对话: {existing_conversation.conversation_id}") + conversation_id = None + is_new_conversation = False - # 调用服务创建自动对话 - success, result, goal = AutoGmailConversationService.create_auto_conversation( - user=request.user, - user_email=user_email, - influencer_email=influencer_email, - greeting_message="", # 使用空字符串,实际消息在服务中已固定 + if existing_conversation: + # 使用现有对话 + conversation = existing_conversation + conversation_id = conversation.conversation_id + # 激活对话 + conversation.is_active = True + + conversation.save() + logger.info(f"找到并激活现有对话: {conversation_id}, has_sent_greeting={conversation.has_sent_greeting}") + else: + # 创建新对话 + conversation_id = f"gmail_{request.user.id}_{str(uuid.uuid4())[:8]}" + conversation = GmailConversation.objects.create( + user=request.user, + user_email=user_email, + influencer_email=influencer_email, + conversation_id=conversation_id, + title=f"与 {influencer_email} 的Gmail对话", + is_active=True, + has_sent_greeting=False, # 新创建的对话尚未发送打招呼消息 + metadata={ + 'auto_conversation': True, + 'created_at': timezone.now().isoformat() + } + ) + is_new_conversation = True + logger.info(f"创建新的自动对话: {conversation_id}") + + # 使用goal_service创建或更新目标 + goal, is_new_goal = get_or_create_goal( + user=request.user, + conversation_id=conversation_id, goal_description=goal_description ) - if not success: - return Response({ - 'code': 400, - 'message': result, - 'data': None - }, status=status.HTTP_400_BAD_REQUEST) + logger.info(f"目标{'创建' if is_new_goal else '更新'}成功: {goal.id if goal else 'None'}") - # 确保对话是活跃的 - conversation = GmailConversation.objects.filter(conversation_id=result).first() - if conversation and not conversation.is_active: - conversation.is_active = True - conversation.save() - logger.info(f"已将对话 {conversation.conversation_id} 设置为活跃") + # 检查是否需要发送打招呼消息 + if not conversation.has_sent_greeting: + logger.info(f"准备发送打招呼消息: {conversation_id}") + # 固定的打招呼消息 + greeting_message = """Paid Collaboration Opportunity with TikTok's #1 Fragrance Brand 🌸 +Hi, +I'm Vira from OOIN Media, and I'm reaching out on behalf of a top-performing fragrance brand Sttes on TikTok Shop—currently ranked #1 in the perfume category. +This brand has already launched several viral products and is now looking to partner with select creators like you through paid collaborations to continue driving awareness and sales. +We'd love to explore a partnership and would appreciate it if you could share: +Your rate for a single TikTok video +Whether you offer bundle pricing for multiple videos +Any additional details or formats you offer (e.g. story integration, livestream add-ons, etc.) +The product has strong market traction, proven conversions, and a competitive commission structure if you're also open to affiliate partnerships. +Looking forward to the opportunity to work together and hearing your rates! +Warm regards, +Vira +OOIN Media""" + + # 发送打招呼消息 + subject = "Paid Collaboration Opportunity with TikTok's #1 Fragrance Brand" + logger.info(f"开始向 {influencer_email} 发送打招呼消息") + + # 使用GmailService发送邮件 + success, message_id = GmailService.send_email( + user=request.user, + user_email=user_email, + to_email=influencer_email, + subject=subject, + body=greeting_message + ) + + if success: + # 将打招呼消息保存到聊天历史 - 使用更完整的方式 + try: + # 查找或创建默认知识库 + knowledge_base = KnowledgeBase.objects.filter(user_id=request.user.id, type='private').first() + if knowledge_base: + # 创建聊天消息 + ChatHistory.objects.create( + user=request.user, + knowledge_base=knowledge_base, + conversation_id=conversation_id, + message_id=f"greeting_{message_id}", + title=conversation.title, + role="user", # 用户发出的消息 + content=greeting_message, + metadata={ + 'gmail_message_id': message_id, + 'from': user_email, + 'to': influencer_email, + 'date': timezone.now().isoformat(), + 'subject': subject, + 'greeting': True, + 'source': 'gmail' + } + ) + logger.info(f"打招呼消息已保存到聊天历史: {message_id}") + else: + logger.warning("未找到默认知识库,打招呼消息未保存到聊天历史") + except Exception as chat_error: + logger.error(f"保存打招呼消息到聊天历史失败: {str(chat_error)}") + # 这里我们继续执行,不影响主流程 + + # 更新对话的has_sent_greeting字段 + conversation.has_sent_greeting = True + conversation.save(update_fields=['has_sent_greeting', 'updated_at']) + logger.info(f"对话 {conversation_id} 已发送打招呼消息,并更新了has_sent_greeting=True") + else: + logger.error(f"发送打招呼消息失败: {message_id}") + return Response({ + 'code': 500, + 'message': f"发送打招呼消息失败: {message_id}", + 'data': None + }, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + else: + logger.info(f"对话 {conversation_id} 已经发送过打招呼消息,不再重复发送") # 设置Gmail推送通知 notification_result, notification_error = GmailService.setup_gmail_push_notification( @@ -361,29 +462,34 @@ class AutoGmailConversationView(APIView): ) if not notification_result and notification_error: - logger.warning(f"设置Gmail推送通知失败: {notification_error},但对话创建成功") + logger.warning(f"设置Gmail推送通知失败: {notification_error},但对话创建/更新成功") + + # 生成对话摘要(如果有足够的消息) + summary = get_conversation_summary(conversation_id) # 返回结果 return Response({ - 'code': 201, - 'message': '自动对话创建成功,已发送打招呼消息', + 'code': 201 if is_new_conversation else 200, + 'message': '自动对话创建成功' if is_new_conversation else '自动对话更新成功', 'data': { - 'conversation_id': result, + 'conversation_id': conversation_id, 'goal_id': str(goal.id) if goal else None, 'user_email': user_email, 'influencer_email': influencer_email, 'is_active': True, + 'has_sent_greeting': conversation.has_sent_greeting, 'goal_description': goal_description, - 'push_notification': notification_result + 'push_notification': notification_result, + 'summary': summary } - }, status=status.HTTP_201_CREATED) + }, status=status.HTTP_201_CREATED if is_new_conversation else status.HTTP_200_OK) except Exception as e: - logger.error(f"创建自动对话失败: {str(e)}") + logger.error(f"创建/更新自动对话失败: {str(e)}") error_details = traceback.format_exc() return Response({ 'code': 500, - 'message': f'创建自动对话失败: {str(e)}', + 'message': f'创建/更新自动对话失败: {str(e)}', 'data': { 'details': error_details[:500] } diff --git a/apps/gmail/views.py b/apps/gmail/views.py index 66ebb98..7b61027 100644 --- a/apps/gmail/views.py +++ b/apps/gmail/views.py @@ -40,8 +40,12 @@ class GmailAuthInitiateView(APIView): """ 处理 POST 请求,启动 Gmail OAuth2 认证并返回授权 URL。 + 支持两种方式提供客户端密钥: + 1. 在请求体中提供client_secret_json字段 + 2. 上传名为client_secret_file的JSON文件 + Args: - request: Django REST Framework 请求对象,包含客户端密钥 JSON 数据。 + request: Django REST Framework 请求对象,包含客户端密钥 JSON 数据或文件。 Returns: Response: 包含授权 URL 的 JSON 响应(成功时),或错误信息(失败时)。 @@ -52,34 +56,78 @@ class GmailAuthInitiateView(APIView): 500: 服务器内部错误(如认证服务失败)。 """ logger.debug(f"Received auth initiate request: {request.data}") - serializer = GmailCredentialSerializer(data=request.data, context={'request': request}) - if serializer.is_valid(): + + # 检查是否是文件上传方式 + client_secret_json = None + if 'client_secret_file' in request.FILES: try: - # 从请求数据中提取客户端密钥 JSON - client_secret_json = serializer.validated_data['client_secret_json'] - # 调用 GmailService 生成授权 URL - auth_url = GmailService.initiate_authentication(request.user, client_secret_json) - logger.info(f"Generated auth URL for user {request.user.id}") + # 读取上传的JSON文件内容 + client_secret_file = request.FILES['client_secret_file'] + client_secret_json = json.loads(client_secret_file.read().decode('utf-8')) + logger.info(f"从上传文件读取到客户端密钥JSON") + except json.JSONDecodeError as e: + logger.error(f"解析客户端密钥JSON文件失败: {str(e)}") return Response({ - 'code': 200, - 'message': '成功生成授权URL', - 'data': {'auth_url': auth_url} - }, status=status.HTTP_200_OK) + 'code': 400, + 'message': f'无效的JSON文件格式: {str(e)}', + 'data': None + }, status=status.HTTP_400_BAD_REQUEST) except Exception as e: - # 记录错误并返回服务器错误响应 - logger.error(f"Error initiating authentication for user {request.user.id}: {str(e)}") + logger.error(f"处理上传文件失败: {str(e)}") return Response({ 'code': 500, - 'message': f'认证初始化错误:{str(e)}', + 'message': f'处理上传文件失败: {str(e)}', 'data': None }, status=status.HTTP_500_INTERNAL_SERVER_ERROR) - # 记录无效请求数据并返回错误响应 - logger.warning(f"Invalid request data: {serializer.errors}") - return Response({ - 'code': 400, - 'message': '请求数据无效', - 'data': serializer.errors - }, status=status.HTTP_400_BAD_REQUEST) + + # 如果不是文件上传,则尝试从请求数据中提取JSON + if not client_secret_json: + serializer = GmailCredentialSerializer(data=request.data, context={'request': request}) + if serializer.is_valid(): + try: + # 从请求数据中提取客户端密钥 JSON + client_secret_json = serializer.validated_data['client_secret_json'] + except Exception as e: + logger.error(f"未提供客户端密钥JSON: {str(e)}") + return Response({ + 'code': 400, + 'message': '请提供client_secret_json字段或上传client_secret_file文件', + 'data': None + }, status=status.HTTP_400_BAD_REQUEST) + else: + # 记录无效请求数据并返回错误响应 + logger.warning(f"Invalid request data: {serializer.errors}") + return Response({ + 'code': 400, + 'message': '请求数据无效,请提供client_secret_json字段或上传client_secret_file文件', + 'data': serializer.errors + }, status=status.HTTP_400_BAD_REQUEST) + + # 如果此时仍然没有client_secret_json,返回错误 + if not client_secret_json: + return Response({ + 'code': 400, + 'message': '请提供client_secret_json字段或上传client_secret_file文件', + 'data': None + }, status=status.HTTP_400_BAD_REQUEST) + + try: + # 调用 GmailService 生成授权 URL + auth_url = GmailService.initiate_authentication(request.user, client_secret_json) + logger.info(f"Generated auth URL for user {request.user.id}") + return Response({ + 'code': 200, + 'message': '成功生成授权URL', + 'data': {'auth_url': auth_url} + }, status=status.HTTP_200_OK) + except Exception as e: + # 记录错误并返回服务器错误响应 + logger.error(f"Error initiating authentication for user {request.user.id}: {str(e)}") + return Response({ + 'code': 500, + 'message': f'认证初始化错误:{str(e)}', + 'data': None + }, status=status.HTTP_500_INTERNAL_SERVER_ERROR) class GmailAuthCompleteView(APIView): @@ -92,8 +140,12 @@ class GmailAuthCompleteView(APIView): """ 处理 POST 请求,使用授权代码完成 Gmail OAuth2 认证并保存凭证。 + 支持两种方式提供客户端密钥: + 1. 在请求体中提供client_secret_json字段 + 2. 上传名为client_secret_file的JSON文件 + Args: - request: Django REST Framework 请求对象,包含授权代码和客户端密钥 JSON。 + request: Django REST Framework 请求对象,包含授权代码和客户端密钥 JSON 或文件。 Returns: Response: 包含已保存凭证数据的 JSON 响应(成功时),或错误信息(失败时)。 @@ -104,37 +156,89 @@ class GmailAuthCompleteView(APIView): 500: 服务器内部错误(如认证失败)。 """ logger.debug(f"Received auth complete request: {request.data}") - serializer = GmailCredentialSerializer(data=request.data, context={'request': request}) - if serializer.is_valid(): + + # 检查是否是文件上传方式 + client_secret_json = None + if 'client_secret_file' in request.FILES: try: - # 提取授权代码和客户端密钥 JSON - auth_code = serializer.validated_data['auth_code'] - client_secret_json = serializer.validated_data['client_secret_json'] - # 完成认证并保存凭证 - credential = GmailService.complete_authentication(request.user, auth_code, client_secret_json) - # 序列化凭证数据以返回 - serializer = GmailCredentialSerializer(credential, context={'request': request}) - logger.info(f"Authentication completed for user {request.user.id}, email: {credential.email}") + # 读取上传的JSON文件内容 + client_secret_file = request.FILES['client_secret_file'] + client_secret_json = json.loads(client_secret_file.read().decode('utf-8')) + logger.info(f"从上传文件读取到客户端密钥JSON") + except json.JSONDecodeError as e: + logger.error(f"解析客户端密钥JSON文件失败: {str(e)}") return Response({ - 'code': 201, - 'message': '认证完成并成功保存凭证', - 'data': serializer.data - }, status=status.HTTP_201_CREATED) + 'code': 400, + 'message': f'无效的JSON文件格式: {str(e)}', + 'data': None + }, status=status.HTTP_400_BAD_REQUEST) except Exception as e: - # 记录错误并返回服务器错误响应 - logger.error(f"Error completing authentication for user {request.user.id}: {str(e)}") + logger.error(f"处理上传文件失败: {str(e)}") return Response({ 'code': 500, - 'message': f'完成认证时发生错误:{str(e)}', + 'message': f'处理上传文件失败: {str(e)}', 'data': None }, status=status.HTTP_500_INTERNAL_SERVER_ERROR) - # 记录无效请求数据并返回错误响应 - logger.warning(f"Invalid request data: {serializer.errors}") - return Response({ - 'code': 400, - 'message': '请求数据无效', - 'data': serializer.errors - }, status=status.HTTP_400_BAD_REQUEST) + + # 获取授权码,无论是哪种方式都需要 + auth_code = request.data.get('auth_code') + if not auth_code: + return Response({ + 'code': 400, + 'message': '必须提供授权码', + 'data': None + }, status=status.HTTP_400_BAD_REQUEST) + + # 如果不是文件上传,则尝试从请求数据中提取JSON + if not client_secret_json: + serializer = GmailCredentialSerializer(data=request.data, context={'request': request}) + if serializer.is_valid(): + try: + # 从请求数据中提取客户端密钥 JSON + client_secret_json = serializer.validated_data['client_secret_json'] + except Exception as e: + logger.error(f"未提供客户端密钥JSON: {str(e)}") + return Response({ + 'code': 400, + 'message': '请提供client_secret_json字段或上传client_secret_file文件', + 'data': None + }, status=status.HTTP_400_BAD_REQUEST) + else: + # 记录无效请求数据并返回错误响应 + logger.warning(f"Invalid request data: {serializer.errors}") + return Response({ + 'code': 400, + 'message': '请求数据无效,请提供client_secret_json字段或上传client_secret_file文件', + 'data': serializer.errors + }, status=status.HTTP_400_BAD_REQUEST) + + # 如果此时仍然没有client_secret_json,返回错误 + if not client_secret_json: + return Response({ + 'code': 400, + 'message': '请提供client_secret_json字段或上传client_secret_file文件', + 'data': None + }, status=status.HTTP_400_BAD_REQUEST) + + try: + # 完成认证并保存凭证 + credential = GmailService.complete_authentication(request.user, auth_code, client_secret_json) + # 序列化凭证数据以返回 + return_serializer = GmailCredentialSerializer(credential, context={'request': request}) + logger.info(f"Authentication completed for user {request.user.id}, email: {credential.email}") + return Response({ + 'code': 201, + 'message': '认证完成并成功保存凭证', + 'data': return_serializer.data + }, status=status.HTTP_201_CREATED) + except Exception as e: + # 记录错误并返回服务器错误响应 + logger.error(f"Error completing authentication for user {request.user.id}: {str(e)}") + return Response({ + 'code': 500, + 'message': f'完成认证时发生错误:{str(e)}', + 'data': None + }, status=status.HTTP_500_INTERNAL_SERVER_ERROR) class GmailCredentialViewSet(viewsets.ModelViewSet): @@ -212,36 +316,21 @@ class GmailCredentialViewSet(viewsets.ModelViewSet): client_secret_json = serializer.validated_data.get('client_secret_json') if not auth_code: # 初始化OAuth - auth_url = GmailService.start_oauth(client_secret_json) + auth_url = GmailService.initiate_authentication(request.user, client_secret_json) return Response({ 'code': 200, 'message': '授权URL生成成功', 'data': {'auth_url': auth_url} }) else: # 完成OAuth - email, credentials = GmailService.complete_oauth(client_secret_json, auth_code) - if not email: - return Response({ - 'code': 400, - 'message': f'授权失败: {credentials}', - 'data': None - }, status=status.HTTP_400_BAD_REQUEST) - - # 保存凭证 - serializer.save( - user=request.user, - email=email, - credentials=credentials, - is_valid=True - ) - + credential = GmailService.complete_authentication(request.user, auth_code, client_secret_json) return Response({ 'code': 201, 'message': '授权成功', 'data': { - 'id': serializer.instance.id, - 'email': email, - 'is_valid': True + 'id': credential.id, + 'email': credential.email, + 'is_valid': credential.is_valid } }, status=status.HTTP_201_CREATED) @@ -365,6 +454,68 @@ class GmailCredentialViewSet(viewsets.ModelViewSet): 'data': None }, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + @action(detail=False, methods=['post']) + def upload_client_secret(self, request): + """ + 通过上传客户端密钥JSON文件来创建Gmail凭证 + + 请求参数: + - client_secret_file: Google Cloud项目的客户端密钥JSON文件 + - auth_code: [可选] 授权码,用于完成OAuth流程 + """ + try: + # 检查是否上传了文件 + if 'client_secret_file' not in request.FILES: + return Response({ + 'code': 400, + 'message': '未找到客户端密钥文件,请使用client_secret_file字段上传', + 'data': None + }, status=status.HTTP_400_BAD_REQUEST) + + # 读取上传的JSON文件内容 + client_secret_file = request.FILES['client_secret_file'] + client_secret_json = json.loads(client_secret_file.read().decode('utf-8')) + + # 获取可选的授权码 + auth_code = request.data.get('auth_code', '') + + if not auth_code: # 初始化OAuth + # 调用GmailService生成授权URL + auth_url = GmailService.initiate_authentication(request.user, client_secret_json) + return Response({ + 'code': 200, + 'message': '授权URL生成成功', + 'data': {'auth_url': auth_url} + }) + else: # 完成OAuth + # 完成认证并保存凭证 + credential = GmailService.complete_authentication(request.user, auth_code, client_secret_json) + return Response({ + 'code': 201, + 'message': '授权成功', + 'data': { + 'id': credential.id, + 'email': credential.email, + 'is_valid': credential.is_valid + } + }, status=status.HTTP_201_CREATED) + + except json.JSONDecodeError as e: + logger.error(f"解析客户端密钥JSON文件失败: {str(e)}") + return Response({ + 'code': 400, + 'message': f'无效的JSON文件格式: {str(e)}', + 'data': None + }, status=status.HTTP_400_BAD_REQUEST) + except Exception as e: + logger.error(f"上传客户端密钥文件失败: {str(e)}") + logger.error(traceback.format_exc()) + return Response({ + 'code': 500, + 'message': f'处理客户端密钥文件失败: {str(e)}', + 'data': None + }, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + class GmailConversationView(APIView): """ @@ -846,12 +997,120 @@ class GmailWebhookView(APIView): # 获取并保存最新邮件 print("[Gmail Webhook] 开始获取最新邮件...") - # 处理新邮件,使用静态方法而不是实例化类 try: # 使用GmailService的静态方法处理新邮件 processed_emails = GmailService.process_new_emails(user, credential, history_id) print(f"[Gmail Webhook] 新邮件处理完成,共 {len(processed_emails)} 封") + # 处理活跃对话的自动回复 + if processed_emails: + print(f"[Gmail Webhook] 开始处理自动回复...") + + # 获取刚处理的邮件的对应的对话信息 + from apps.gmail.models import GmailConversation, UserGoal + from apps.chat.models import ChatHistory + + # 遍历每个处理过的邮件ID + for email_id in processed_emails: + try: + # 通过邮件ID查找对应的聊天记录 + chat_msg = ChatHistory.objects.filter( + metadata__gmail_message_id=email_id + ).order_by('-created_at').first() + + if not chat_msg: + print(f"[Gmail Webhook] 邮件 {email_id} 未找到对应的聊天记录,跳过自动回复") + continue + + conversation_id = chat_msg.conversation_id + + # 检查对话是否是活跃的 + conversation = GmailConversation.objects.filter( + conversation_id=conversation_id, + is_active=True + ).first() + + if not conversation: + print(f"[Gmail Webhook] 对话 {conversation_id} 不是活跃的,跳过自动回复") + continue + + print(f"[Gmail Webhook] 发现活跃对话 {conversation_id},准备自动回复") + + # 检查邮件角色,只有达人发送的邮件才自动回复 + if chat_msg.role != 'assistant': + print(f"[Gmail Webhook] 邮件 {email_id} 不是达人发送的,跳过自动回复") + continue + + # 查找对话的目标 + goal = UserGoal.objects.filter( + conversation=conversation, + is_active=True + ).first() + + if not goal: + print(f"[Gmail Webhook] 对话 {conversation_id} 没有活跃目标,跳过自动回复") + continue + + # 获取对话摘要 + from apps.gmail.services.goal_service import get_conversation_summary, get_last_message, generate_recommended_reply + + conversation_summary = get_conversation_summary(conversation_id) + if not conversation_summary: + conversation_summary = "无对话摘要" + + # 获取最后一条达人消息 + last_message = get_last_message(conversation_id) + if not last_message: + print(f"[Gmail Webhook] 对话 {conversation_id} 没有达人消息,跳过自动回复") + continue + + # 生成推荐回复 + reply_content, error = generate_recommended_reply( + user=user, + goal_description=goal.description, + conversation_summary=conversation_summary, + last_message=last_message + ) + + if error: + print(f"[Gmail Webhook] 生成推荐回复失败: {error}") + continue + + if not reply_content: + print(f"[Gmail Webhook] 生成的推荐回复为空") + continue + + # 构建回复的主题 + subject = chat_msg.metadata.get('subject', '') + reply_subject = f"回复: {subject}" if not subject.startswith('回复:') else subject + + # 准备发送自动回复 + print(f"[Gmail Webhook] 准备发送自动回复: 从{conversation.user_email}到{conversation.influencer_email}") + + success, reply_message_id = GmailService.send_email( + user=user, + user_email=conversation.user_email, + to_email=conversation.influencer_email, + subject=reply_subject, + body=reply_content + ) + + if success: + print(f"[Gmail Webhook] 已成功发送自动回复: {reply_message_id}") + + # 更新目标状态 + goal.last_activity_time = timezone.now() + if goal.status == 'pending': + goal.status = 'in_progress' + goal.save(update_fields=['last_activity_time', 'status', 'updated_at']) + else: + print(f"[Gmail Webhook] 发送自动回复失败: {reply_message_id}") + + except Exception as reply_error: + print(f"[Gmail Webhook] 处理自动回复过程中出错: {str(reply_error)}") + import traceback + print(traceback.format_exc()) + # 更新凭证的历史ID(移到处理完成后再更新) if history_id: credential.last_history_id = history_id @@ -861,10 +1120,12 @@ class GmailWebhookView(APIView): except Exception as e: print(f"[Gmail Webhook] 获取最新邮件失败: {str(e)}") logger.error(f"获取最新邮件失败: {str(e)}") + import traceback + print(traceback.format_exc()) else: print(f"[Gmail Webhook] 警告: 未找到对应的Gmail凭证: {email_address}") logger.warning(f"收到推送通知,但未找到对应的Gmail凭证: {email_address}") - + except Exception as e: print(f"[Gmail Webhook] 解析推送数据失败: {str(e)}") logger.error(f"解析推送数据失败: {str(e)}")