operations_project/apps/gmail/views.py

599 lines
23 KiB
Python
Raw Normal View History

2025-05-13 11:58:17 +08:00
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
2025-05-07 18:01:48 +08:00
2025-05-13 11:58:17 +08:00
# 配置日志记录器,用于记录视图操作的调试、警告和错误信息
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)