from rest_framework.views import APIView from rest_framework.response import Response from rest_framework.permissions import IsAuthenticated from rest_framework import status from .serializers import GmailCredentialSerializer from .services.gmail_service import GmailService from .models import GmailCredential, GmailConversation, GmailAttachment from django.shortcuts import get_object_or_404 import logging import os from django.conf import settings from django.core.files.storage import default_storage from django.core.files.base import ContentFile # 配置日志记录器,用于记录视图操作的调试、警告和错误信息 logger = logging.getLogger(__name__) class GmailAuthInitiateView(APIView): """ API 视图,用于启动 Gmail OAuth2 认证流程。 """ permission_classes = [IsAuthenticated] # 限制访问,仅允许已认证用户 def post(self, request): """ 处理 POST 请求,启动 Gmail OAuth2 认证并返回授权 URL。 Args: request: Django REST Framework 请求对象,包含客户端密钥 JSON 数据。 Returns: Response: 包含授权 URL 的 JSON 响应(成功时),或错误信息(失败时)。 Status Codes: 200: 成功生成授权 URL。 400: 请求数据无效。 500: 服务器内部错误(如认证服务失败)。 """ logger.debug(f"Received auth initiate request: {request.data}") serializer = GmailCredentialSerializer(data=request.data, context={'request': request}) if serializer.is_valid(): 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}") return Response({'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({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) # 记录无效请求数据并返回错误响应 logger.warning(f"Invalid request data: {serializer.errors}") return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) class GmailAuthCompleteView(APIView): """ API 视图,用于完成 Gmail OAuth2 认证流程。 """ permission_classes = [IsAuthenticated] # 限制访问,仅允许已认证用户 def post(self, request): """ 处理 POST 请求,使用授权代码完成 Gmail OAuth2 认证并保存凭证。 Args: request: Django REST Framework 请求对象,包含授权代码和客户端密钥 JSON。 Returns: Response: 包含已保存凭证数据的 JSON 响应(成功时),或错误信息(失败时)。 Status Codes: 201: 成功保存凭证。 400: 请求数据无效。 500: 服务器内部错误(如认证失败)。 """ logger.debug(f"Received auth complete request: {request.data}") serializer = GmailCredentialSerializer(data=request.data, context={'request': request}) if serializer.is_valid(): 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}") return Response(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({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) # 记录无效请求数据并返回错误响应 logger.warning(f"Invalid request data: {serializer.errors}") return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) class GmailCredentialListView(APIView): """ API 视图,用于列出用户的所有 Gmail 凭证。 """ permission_classes = [IsAuthenticated] # 限制访问,仅允许已认证用户 def get(self, request): """ 处理 GET 请求,返回用户的所有 Gmail 凭证列表。 Args: request: Django REST Framework 请求对象。 Returns: Response: 包含凭证列表的 JSON 响应。 Status Codes: 200: 成功返回凭证列表。 """ # 获取用户关联的所有 Gmail 凭证 credentials = request.user.gmail_credentials.all() # 序列化凭证数据 serializer = GmailCredentialSerializer(credentials, many=True, context={'request': request}) return Response(serializer.data, status=status.HTTP_200_OK) class GmailCredentialDetailView(APIView): """ API 视图,用于管理特定 Gmail 凭证的获取、更新和删除。 """ permission_classes = [IsAuthenticated] # 限制访问,仅允许已认证用户 def get(self, request, pk): """ 处理 GET 请求,获取特定 Gmail 凭证的详细信息。 Args: request: Django REST Framework 请求对象。 pk: 凭证的主键 ID。 Returns: Response: 包含凭证详细信息的 JSON 响应。 Status Codes: 200: 成功返回凭证信息。 404: 未找到指定凭证。 """ # 获取用户拥有的指定凭证,未找到则返回 404 credential = get_object_or_404(GmailCredential, pk=pk, user=request.user) serializer = GmailCredentialSerializer(credential, context={'request': request}) return Response(serializer.data, status=status.HTTP_200_OK) def patch(self, request, pk): """ 处理 PATCH 请求,更新特定 Gmail 凭证(如设置为默认凭证)。 Args: request: Django REST Framework 请求对象,包含更新数据。 pk: 凭证的主键 ID。 Returns: Response: 包含更新后凭证数据的 JSON 响应,或错误信息。 Status Codes: 200: 成功更新凭证。 400: 请求数据无效。 404: 未找到指定凭证。 """ # 获取用户拥有的指定凭证 credential = get_object_or_404(GmailCredential, pk=pk, user=request.user) serializer = GmailCredentialSerializer(credential, data=request.data, partial=True, context={'request': request}) if serializer.is_valid(): # 如果设置为默认凭证,清除其他凭证的默认状态 if serializer.validated_data.get('is_default', False): GmailCredential.objects.filter(user=request.user).exclude(id=credential.id).update(is_default=False) serializer.save() return Response(serializer.data, status=status.HTTP_200_OK) # 返回无效数据错误 return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) def delete(self, request, pk): """ 处理 DELETE 请求,删除特定 Gmail 凭证。 Args: request: Django REST Framework 请求对象。 pk: 凭证的主键 ID。 Returns: Response: 空响应,表示删除成功。 Status Codes: 204: 成功删除凭证。 404: 未找到指定凭证。 """ # 获取并删除用户拥有的指定凭证 credential = get_object_or_404(GmailCredential, pk=pk, user=request.user) credential.delete() return Response(status=status.HTTP_204_NO_CONTENT) class GmailConversationView(APIView): """ API视图,用于获取和保存Gmail对话。 """ permission_classes = [IsAuthenticated] # 限制访问,仅允许已认证用户 def post(self, request): """ 处理POST请求,获取Gmail对话并保存到聊天历史。 请求参数: user_email: 用户Gmail邮箱 influencer_email: 达人Gmail邮箱 kb_id: [可选] 知识库ID,不提供则使用默认知识库 返回: conversation_id: 创建的会话ID """ try: # 验证必填参数 user_email = request.data.get('user_email') influencer_email = request.data.get('influencer_email') if not user_email or not influencer_email: return Response({ 'code': 400, 'message': '缺少必填参数: user_email 或 influencer_email', 'data': None }, status=status.HTTP_400_BAD_REQUEST) # 可选参数 kb_id = request.data.get('kb_id') # 调用服务保存对话 conversation_id, error = GmailService.save_conversations_to_chat( request.user, user_email, influencer_email, kb_id ) if error: return Response({ 'code': 400, 'message': error, 'data': None }, status=status.HTTP_400_BAD_REQUEST) return Response({ 'code': 200, 'message': '获取Gmail对话成功', 'data': { 'conversation_id': conversation_id } }) except Exception as e: logger.error(f"获取Gmail对话失败: {str(e)}") return Response({ 'code': 500, 'message': f'获取Gmail对话失败: {str(e)}', 'data': None }, status=status.HTTP_500_INTERNAL_SERVER_ERROR) def get(self, request): """ 处理GET请求,获取用户的Gmail对话列表。 """ try: conversations = GmailConversation.objects.filter(user=request.user, is_active=True) data = [] for conversation in conversations: # 获取附件计数 attachments_count = GmailAttachment.objects.filter( conversation=conversation ).count() data.append({ 'id': str(conversation.id), 'conversation_id': conversation.conversation_id, 'user_email': conversation.user_email, 'influencer_email': conversation.influencer_email, 'title': conversation.title, 'last_sync_time': conversation.last_sync_time.strftime('%Y-%m-%d %H:%M:%S'), 'created_at': conversation.created_at.strftime('%Y-%m-%d %H:%M:%S'), 'attachments_count': attachments_count }) return Response({ 'code': 200, 'message': '获取对话列表成功', 'data': data }) except Exception as e: logger.error(f"获取对话列表失败: {str(e)}") return Response({ 'code': 500, 'message': f'获取对话列表失败: {str(e)}', 'data': None }, status=status.HTTP_500_INTERNAL_SERVER_ERROR) class GmailAttachmentListView(APIView): """ API视图,用于获取Gmail附件列表。 """ permission_classes = [IsAuthenticated] # 限制访问,仅允许已认证用户 def get(self, request, conversation_id=None): """ 处理GET请求,获取指定对话的附件列表。 """ try: if conversation_id: # 获取指定对话的附件 conversation = get_object_or_404(GmailConversation, conversation_id=conversation_id, user=request.user) attachments = GmailAttachment.objects.filter(conversation=conversation) else: # 获取用户的所有附件 conversations = GmailConversation.objects.filter(user=request.user, is_active=True) attachments = GmailAttachment.objects.filter(conversation__in=conversations) data = [] for attachment in attachments: data.append({ 'id': str(attachment.id), 'conversation_id': attachment.conversation.conversation_id, 'filename': attachment.filename, 'content_type': attachment.content_type, 'size': attachment.size, 'sender_email': attachment.sender_email, 'created_at': attachment.created_at.strftime('%Y-%m-%d %H:%M:%S'), 'url': attachment.get_absolute_url() }) return Response({ 'code': 200, 'message': '获取附件列表成功', 'data': data }) except Exception as e: logger.error(f"获取附件列表失败: {str(e)}") return Response({ 'code': 500, 'message': f'获取附件列表失败: {str(e)}', 'data': None }, status=status.HTTP_500_INTERNAL_SERVER_ERROR) class GmailPubSubView(APIView): """ API视图,用于设置Gmail的Pub/Sub实时通知。 """ permission_classes = [IsAuthenticated] # 限制访问,仅允许已认证用户 def post(self, request): """ 处理POST请求,为用户的Gmail账户设置Pub/Sub推送通知。 Args: request: Django REST Framework请求对象,包含Gmail邮箱信息。 Returns: Response: 设置结果的JSON响应。 Status Codes: 200: 成功设置Pub/Sub通知。 400: 请求数据无效。 404: 未找到指定Gmail凭证。 500: 服务器内部错误。 """ try: # 获取请求参数 email = request.data.get('email') if not email: return Response({'error': '必须提供Gmail邮箱地址'}, status=status.HTTP_400_BAD_REQUEST) # 检查用户是否有此Gmail账户的凭证 credential = GmailCredential.objects.filter(user=request.user, email=email).first() if not credential: return Response({'error': f'未找到{email}的授权信息'}, status=status.HTTP_404_NOT_FOUND) # 设置Pub/Sub通知 success, error = GmailService.setup_gmail_push_notification(request.user, email) if success: return Response({'message': f'已成功为{email}设置实时通知'}, status=status.HTTP_200_OK) else: return Response({'error': error}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) except Exception as e: logger.error(f"设置Gmail Pub/Sub通知失败: {str(e)}") return Response({'error': f'设置Gmail实时通知失败: {str(e)}'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) def get(self, request): """ 处理GET请求,获取用户所有已设置Pub/Sub通知的Gmail账户。 这个方法目前仅返回用户的所有Gmail凭证,未来可以扩展为返回推送通知的详细状态。 Args: request: Django REST Framework请求对象。 Returns: Response: 包含Gmail账户列表的JSON响应。 Status Codes: 200: 成功返回账户列表。 """ # 获取用户所有Gmail凭证 credentials = request.user.gmail_credentials.filter(is_valid=True) # 构建响应数据 accounts = [] for cred in credentials: accounts.append({ 'id': cred.id, 'email': cred.email, 'is_default': cred.is_default }) return Response({'accounts': accounts}, status=status.HTTP_200_OK) class GmailNotificationStartView(APIView): """ API视图,用于启动Gmail Pub/Sub监听器。 通常由系统管理员或后台任务调用,而非普通用户。 """ permission_classes = [IsAuthenticated] # 可根据需要更改为更严格的权限 def post(self, request): """ 处理POST请求,启动Gmail Pub/Sub监听器。 Args: request: Django REST Framework请求对象。 Returns: Response: 启动结果的JSON响应。 Status Codes: 200: 成功启动监听器。 500: 服务器内部错误。 """ try: # 可选:指定要监听的用户ID user_id = request.data.get('user_id') # 在后台线程中启动监听器 thread = GmailService.start_pubsub_listener_thread(user_id) return Response({'message': '已成功启动Gmail实时通知监听器'}, status=status.HTTP_200_OK) except Exception as e: logger.error(f"启动Gmail Pub/Sub监听器失败: {str(e)}") return Response({'error': f'启动Gmail实时通知监听器失败: {str(e)}'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) class GmailSendEmailView(APIView): """ API视图,用于发送Gmail邮件(支持附件)。 """ permission_classes = [IsAuthenticated] # 限制访问,仅允许已认证用户 def post(self, request): """ 处理POST请求,发送Gmail邮件。 请求应包含以下字段: - email: 发件人Gmail邮箱 - to: 收件人邮箱 - subject: 邮件主题 - body: 邮件正文 - attachments: 附件文件IDs列表 (可选) Args: request: Django REST Framework请求对象。 Returns: Response: 发送结果的JSON响应。 Status Codes: 200: 成功发送邮件。 400: 请求数据无效。 404: 未找到Gmail凭证。 500: 服务器内部错误。 """ try: # 获取请求参数 user_email = request.data.get('email') to_email = request.data.get('to') subject = request.data.get('subject') body = request.data.get('body') attachment_ids = request.data.get('attachments', []) # 验证必填字段 if not all([user_email, to_email, subject]): return Response({ 'error': '缺少必要参数,请提供email、to和subject字段' }, status=status.HTTP_400_BAD_REQUEST) # 检查是否有此Gmail账户的凭证 credential = GmailCredential.objects.filter( user=request.user, email=user_email, is_valid=True ).first() if not credential: return Response({ 'error': f'未找到{user_email}的有效授权信息' }, status=status.HTTP_404_NOT_FOUND) # 处理附件 attachments = [] if attachment_ids and isinstance(attachment_ids, list): for file_id in attachment_ids: # 查找已上传的文件 file_obj = request.FILES.get(f'file_{file_id}') if file_obj: # 保存临时文件 tmp_path = os.path.join(settings.MEDIA_ROOT, 'tmp', f'{file_id}_{file_obj.name}') os.makedirs(os.path.dirname(tmp_path), exist_ok=True) with open(tmp_path, 'wb+') as destination: for chunk in file_obj.chunks(): destination.write(chunk) attachments.append({ 'path': tmp_path, 'filename': file_obj.name }) else: # 检查是否为已有的Gmail附件ID try: attachment = GmailAttachment.objects.get(id=file_id) if attachment.conversation.user_id == request.user.id: attachments.append({ 'path': attachment.file_path, 'filename': attachment.filename }) except (GmailAttachment.DoesNotExist, ValueError): logger.warning(f"无法找到附件: {file_id}") # 发送邮件 success, result = GmailService.send_email( request.user, user_email, to_email, subject, body or '', attachments ) if success: return Response({ 'message': '邮件发送成功', 'message_id': result }, status=status.HTTP_200_OK) else: return Response({ 'error': result }, status=status.HTTP_500_INTERNAL_SERVER_ERROR) except Exception as e: logger.error(f"发送Gmail邮件失败: {str(e)}") return Response({ 'error': f'发送Gmail邮件失败: {str(e)}' }, status=status.HTTP_500_INTERNAL_SERVER_ERROR) def get(self, request): """ 处理GET请求,获取用户可用于发送邮件的Gmail账户列表。 Args: request: Django REST Framework请求对象。 Returns: Response: 包含Gmail账户列表的JSON响应。 Status Codes: 200: 成功返回账户列表。 """ # 获取用户所有可用Gmail凭证 credentials = request.user.gmail_credentials.filter(is_valid=True) # 构建响应数据 accounts = [] for cred in credentials: accounts.append({ 'id': cred.id, 'email': cred.email, 'is_default': cred.is_default }) return Response({'accounts': accounts}, status=status.HTTP_200_OK)