daren/apps/feishu/views.py

896 lines
40 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import json
import traceback
import logging
import uuid
from django.db import transaction
from django.utils import timezone
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from django.http import Http404
from .models import FeishuTableMapping, CreatorConversationTracker
from .services.bitable_service import BitableService
from .services.data_sync_service import DataSyncService
from .services.gmail_extraction_service import GmailExtractionService
from .services.auto_gmail_conversation_service import AutoGmailConversationService
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
from apps.user.authentication import CustomTokenAuthentication
logger = logging.getLogger(__name__)
class FeishuTableRecordsView(APIView):
"""
查询飞书多维表格数据的视图
"""
def post(self, request, *args, **kwargs):
try:
# 获取前端传来的数据
data = request.data
table_url = data.get('table_url')
access_token = data.get('access_token')
filter_exp = data.get('filter')
sort = data.get('sort')
page_size = data.get('page_size', 20)
page_token = data.get('page_token')
if not table_url or not access_token:
return Response(
{"error": "请提供多维表格URL和access_token"},
status=status.HTTP_400_BAD_REQUEST
)
try:
# 从URL中提取app_token和table_id
app_token, table_id = BitableService.extract_params_from_url(table_url)
# 先获取一些样本数据,检查我们能否访问多维表格
sample_data = BitableService.search_records(
app_token=app_token,
table_id=table_id,
access_token=access_token,
filter_exp=filter_exp,
sort=sort,
page_size=page_size,
page_token=page_token
)
return Response(sample_data, status=status.HTTP_200_OK)
except ValueError as ve:
return Response(
{"error": str(ve), "details": "URL格式可能不正确"},
status=status.HTTP_400_BAD_REQUEST
)
except Exception as e:
error_details = traceback.format_exc()
return Response(
{
"error": f"查询飞书多维表格失败: {str(e)}",
"details": error_details[:500] # 限制错误详情长度
},
status=status.HTTP_500_INTERNAL_SERVER_ERROR
)
class FeishuDataSyncView(APIView):
"""
将飞书多维表格数据同步到数据库的视图
"""
def post(self, request, *args, **kwargs):
try:
# 获取前端传来的数据
data = request.data
table_url = data.get('table_url')
access_token = data.get('access_token')
table_name = data.get('table_name') # 可选,自定义表名
primary_key = data.get('primary_key') # 可选,指定主键
if not table_url or not access_token:
return Response(
{"error": "请提供多维表格URL和access_token"},
status=status.HTTP_400_BAD_REQUEST
)
# 提取参数
try:
app_token, table_id = BitableService.extract_params_from_url(table_url)
# 先获取一些样本数据,检查我们能否访问多维表格
sample_data = BitableService.search_records(
app_token=app_token,
table_id=table_id,
access_token=access_token,
page_size=5
)
# 执行数据同步
result = DataSyncService.sync_data_to_db(
table_url=table_url,
access_token=access_token,
table_name=table_name,
primary_key=primary_key
)
# 添加样本数据到结果中
if result.get('success'):
result['sample_data'] = sample_data.get('items', [])[:3] # 只返回最多3条样本数据
return Response(result, status=status.HTTP_200_OK)
else:
return Response(result, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
except ValueError as ve:
return Response(
{"error": str(ve), "details": "URL格式可能不正确"},
status=status.HTTP_400_BAD_REQUEST
)
except Exception as e:
import traceback
error_details = traceback.format_exc()
return Response(
{
"error": f"数据同步失败: {str(e)}",
"details": error_details[:500] # 限制错误详情长度
},
status=status.HTTP_500_INTERNAL_SERVER_ERROR
)
class FeishuTableMappingListView(APIView):
"""
获取已映射表格列表的视图
"""
def get(self, request, format=None):
"""获取所有映射的表格列表"""
mappings = FeishuTableMapping.objects.all().order_by('-last_sync_time')
result = []
for mapping in mappings:
result.append({
'id': mapping.id,
'app_token': mapping.app_token,
'table_id': mapping.table_id,
'table_url': mapping.table_url,
'table_name': mapping.table_name,
'feishu_table_name': mapping.feishu_table_name,
'last_sync_time': mapping.last_sync_time.strftime('%Y-%m-%d %H:%M:%S') if mapping.last_sync_time else None,
'total_records': mapping.total_records
})
return Response(result)
class FeishuTableMappingDetailView(APIView):
"""
单个表格映射的操作视图
"""
def get_object(self, pk):
try:
return FeishuTableMapping.objects.get(pk=pk)
except FeishuTableMapping.DoesNotExist:
raise Http404
def get(self, request, pk, format=None):
"""获取单个映射详情"""
mapping = self.get_object(pk)
return Response({
'id': mapping.id,
'app_token': mapping.app_token,
'table_id': mapping.table_id,
'table_url': mapping.table_url,
'table_name': mapping.table_name,
'feishu_table_name': mapping.feishu_table_name,
'last_sync_time': mapping.last_sync_time.strftime('%Y-%m-%d %H:%M:%S') if mapping.last_sync_time else None,
'total_records': mapping.total_records
})
def post(self, request, pk, format=None):
"""同步单个表格数据"""
mapping = self.get_object(pk)
# 获取access_token
access_token = request.data.get('access_token')
if not access_token:
return Response(
{"error": "请提供access_token"},
status=status.HTTP_400_BAD_REQUEST
)
# 执行数据同步
result = DataSyncService.sync_data_to_db(
table_url=mapping.table_url,
access_token=access_token,
table_name=mapping.table_name,
auto_sync=True
)
if result.get('success'):
return Response(result, status=status.HTTP_200_OK)
else:
return Response(result, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
def delete(self, request, pk, format=None):
"""删除映射关系(不删除数据表)"""
mapping = self.get_object(pk)
mapping.delete()
return Response({"message": "映射关系已删除"}, status=status.HTTP_204_NO_CONTENT)
class GmailExtractionView(APIView):
"""
从飞书多维表格中提取Gmail邮箱并创建对话
"""
@transaction.atomic
def post(self, request, *args, **kwargs):
try:
# 获取请求数据
data = request.data
table_url = data.get('table_url')
access_token = data.get('access_token')
email_field_name = data.get('email_field_name')
user_email = data.get('user_email')
kb_id = data.get('kb_id')
# 验证必填字段
if not table_url or not access_token or not email_field_name or not user_email:
return Response(
{"error": "请提供必要参数: table_url, access_token, email_field_name, user_email"},
status=status.HTTP_400_BAD_REQUEST
)
# 提取Gmail邮箱
gmail_emails, error = GmailExtractionService.find_duplicate_emails(
db_table_name=None, # 不需要检查数据库表
feishu_table_url=table_url,
access_token=access_token,
email_field_name=email_field_name,
user=request.user
)
if error:
return Response({"error": error}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
if not gmail_emails:
return Response({"message": "未找到Gmail邮箱"}, status=status.HTTP_200_OK)
# 创建对话
success_count, error = GmailExtractionService.create_conversations_for_emails(
user=request.user,
user_email=user_email,
emails=gmail_emails,
kb_id=kb_id
)
return Response({
"success": True,
"message": f"成功创建 {success_count} 个Gmail对话",
"total_emails": len(gmail_emails),
"gmail_emails": gmail_emails[:20] if len(gmail_emails) > 20 else gmail_emails,
"error": error
}, status=status.HTTP_200_OK)
except Exception as e:
error_details = traceback.format_exc()
return Response(
{
"error": f"提取Gmail邮箱并创建对话失败: {str(e)}",
"details": error_details[:500]
},
status=status.HTTP_500_INTERNAL_SERVER_ERROR
)
class AutoGmailConversationView(APIView):
"""
自动Gmail对话API支持自动发送消息并实时接收和回复达人消息
"""
permission_classes = [IsAuthenticated]
authentication_classes = [CustomTokenAuthentication]
def post(self, request, *args, **kwargs):
"""
创建自动对话,根据需要发送第一条打招呼消息
请求参数:
- user_email: 用户的Gmail邮箱已授权
- influencer_email: 达人Gmail邮箱
- goal_description: 对话目标描述
"""
try:
# 获取请求数据
data = request.data
user_email = data.get('user_email')
influencer_email = data.get('influencer_email')
goal_description = data.get('goal_description')
# 验证必填参数
if not user_email or not influencer_email or not goal_description:
return Response({
'code': 400,
'message': '缺少必要参数: user_email, influencer_email, goal_description',
'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.name}, 邮箱={user_email}, 达人={influencer_email}")
# 查找现有对话
existing_conversation = GmailConversation.objects.filter(
user=request.user,
user_email=user_email,
influencer_email=influencer_email
).first()
conversation_id = None
is_new_conversation = False
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
)
logger.info(f"目标{'创建' if is_new_goal else '更新'}成功: {goal.id if goal else 'None'}")
# 检查是否需要发送打招呼消息
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(
user=request.user,
user_email=user_email
)
if not notification_result and notification_error:
logger.warning(f"设置Gmail推送通知失败: {notification_error},但对话创建/更新成功")
# 生成对话摘要(如果有足够的消息)
summary = get_conversation_summary(conversation_id)
# 返回结果
return Response({
'code': 201 if is_new_conversation else 200,
'message': '自动对话创建成功' if is_new_conversation else '自动对话更新成功',
'data': {
'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,
'summary': summary
}
}, status=status.HTTP_201_CREATED if is_new_conversation else status.HTTP_200_OK)
except Exception as e:
logger.error(f"创建/更新自动对话失败: {str(e)}")
error_details = traceback.format_exc()
return Response({
'code': 500,
'message': f'创建/更新自动对话失败: {str(e)}',
'data': {
'details': error_details[:500]
}
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
def get(self, request, *args, **kwargs):
"""
获取用户所有自动对话及其状态
"""
try:
# 获取用户所有对话和目标
conversations = AutoGmailConversationService.get_all_user_conversations_with_goals(request.user)
return Response({
'code': 200,
'message': '获取自动对话列表成功',
'data': {
'conversations': conversations
}
})
except Exception as e:
logger.error(f"获取自动对话列表失败: {str(e)}")
error_details = traceback.format_exc()
return Response({
'code': 500,
'message': f'获取自动对话列表失败: {str(e)}',
'data': {
'details': error_details[:500]
}
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
class BatchGmailConversationView(APIView):
"""
批量Gmail自动对话API支持根据达人库批量发送邮件并跟踪回复状态
"""
permission_classes = [IsAuthenticated]
authentication_classes = [CustomTokenAuthentication]
def post(self, request, *args, **kwargs):
"""
批量创建自动对话,根据达人库中的邮箱发送打招呼消息
请求参数:
- user_email: 用户的Gmail邮箱已授权
- creator_ids: 创作者ID列表可选不提供则使用creator_pool_id
- creator_pool_id: 创作者库ID可选不提供则使用creator_ids
- goal_description: 对话目标描述
- batch_size: 批量处理大小默认10
"""
try:
# 获取请求数据
data = request.data
user_email = data.get('user_email')
creator_ids = data.get('creator_ids', [])
creator_pool_id = data.get('creator_pool_id')
goal_description = data.get('goal_description')
batch_size = int(data.get('batch_size', 10))
# 验证必填参数
if not user_email or not goal_description:
return Response({
'code': 400,
'message': '缺少必要参数: user_email, goal_description',
'data': None
}, status=status.HTTP_400_BAD_REQUEST)
if not creator_ids and not creator_pool_id:
return Response({
'code': 400,
'message': '请提供creator_ids或creator_pool_id其中之一',
'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)
# 获取创作者列表
from apps.daren_detail.models import CreatorProfile, PrivateCreatorPool, PrivateCreatorRelation
creators = []
if creator_ids:
creators = CreatorProfile.objects.filter(id__in=creator_ids)
elif creator_pool_id:
# 从私有池获取创作者
relations = PrivateCreatorRelation.objects.filter(
private_pool_id=creator_pool_id,
status='active'
).select_related('creator')
creators = [relation.creator for relation in relations]
if not creators:
return Response({
'code': 400,
'message': '未找到有效的创作者',
'data': None
}, status=status.HTTP_400_BAD_REQUEST)
# 筛选有邮箱的创作者
valid_creators = [creator for creator in creators if creator.email]
if not valid_creators:
return Response({
'code': 400,
'message': '所选创作者中没有有效的邮箱地址',
'data': None
}, status=status.HTTP_400_BAD_REQUEST)
logger.info(f"开始批量创建Gmail自动对话: 用户={request.user.name}, 邮箱={user_email}, 创作者数量={len(valid_creators)}")
# 创建结果统计
result_stats = {
'total': len(valid_creators),
'success': 0,
'failed': 0,
'skipped': 0,
'details': []
}
# 准备固定的打招呼消息
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"""
# 按批次处理
for i in range(0, len(valid_creators), batch_size):
batch_creators = valid_creators[i:i+batch_size]
for creator in batch_creators:
# 跳过没有邮箱的创作者
if not creator.email:
result_stats['skipped'] += 1
result_stats['details'].append({
'creator_id': str(creator.id),
'creator_name': creator.name,
'status': 'skipped',
'reason': '缺少邮箱地址'
})
continue
try:
# 检查是否已存在对话
existing_tracker = CreatorConversationTracker.objects.filter(
creator_profile=creator,
user=request.user,
user_email=user_email
).first()
if existing_tracker:
# 已存在追踪记录,跳过
result_stats['skipped'] += 1
result_stats['details'].append({
'creator_id': str(creator.id),
'creator_name': creator.name,
'status': 'skipped',
'reason': f'已存在对话 {existing_tracker.conversation_id}'
})
continue
# 查找现有对话
existing_conversation = GmailConversation.objects.filter(
user=request.user,
user_email=user_email,
influencer_email=creator.email
).first()
conversation_id = None
is_new_conversation = False
if existing_conversation:
# 使用现有对话
conversation = existing_conversation
conversation_id = conversation.conversation_id
# 激活对话
conversation.is_active = True
conversation.save()
logger.info(f"找到并激活现有对话: {conversation_id}, 创作者={creator.name}")
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=creator.email,
conversation_id=conversation_id,
title=f"{creator.name} ({creator.email}) 的Gmail对话",
is_active=True,
has_sent_greeting=False,
metadata={
'auto_conversation': True,
'batch_process': True,
'creator_id': str(creator.id),
'created_at': timezone.now().isoformat()
}
)
is_new_conversation = True
logger.info(f"创建新的自动对话: {conversation_id}, 创作者={creator.name}")
# 使用goal_service创建或更新目标
goal, is_new_goal = get_or_create_goal(
user=request.user,
conversation_id=conversation_id,
goal_description=goal_description
)
# 创建跟踪记录
tracker = CreatorConversationTracker.objects.create(
creator_profile=creator,
conversation_id=conversation_id,
user=request.user,
user_email=user_email,
influencer_email=creator.email
)
# 检查是否需要发送打招呼消息
if not conversation.has_sent_greeting:
# 发送打招呼消息
subject = "Paid Collaboration Opportunity with TikTok's #1 Fragrance Brand"
logger.info(f"开始向 {creator.email} 发送打招呼消息")
# 使用GmailService发送邮件
success, message_id = GmailService.send_email(
user=request.user,
user_email=user_email,
to_email=creator.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': creator.email,
'date': timezone.now().isoformat(),
'subject': subject,
'greeting': True,
'source': 'gmail',
'creator_id': str(creator.id)
}
)
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'])
result_stats['success'] += 1
result_stats['details'].append({
'creator_id': str(creator.id),
'creator_name': creator.name,
'status': 'success',
'conversation_id': conversation_id,
'email': creator.email
})
else:
# 发送失败
logger.error(f"发送打招呼消息失败: {message_id}, 创作者={creator.name}")
result_stats['failed'] += 1
result_stats['details'].append({
'creator_id': str(creator.id),
'creator_name': creator.name,
'status': 'failed',
'reason': f"发送邮件失败: {message_id}"
})
else:
# 已发送过打招呼消息
logger.info(f"对话 {conversation_id} 已经发送过打招呼消息,不再重复发送")
result_stats['skipped'] += 1
result_stats['details'].append({
'creator_id': str(creator.id),
'creator_name': creator.name,
'status': 'skipped',
'reason': '已发送过打招呼消息',
'conversation_id': conversation_id
})
except Exception as creator_error:
logger.error(f"处理创作者 {creator.name} 时出错: {str(creator_error)}")
result_stats['failed'] += 1
result_stats['details'].append({
'creator_id': str(creator.id),
'creator_name': creator.name,
'status': 'error',
'reason': str(creator_error)
})
# 设置Gmail推送通知
notification_result, notification_error = GmailService.setup_gmail_push_notification(
user=request.user,
user_email=user_email
)
if not notification_result and notification_error:
logger.warning(f"设置Gmail推送通知失败: {notification_error},但批量对话创建成功")
# 返回结果
return Response({
'code': 200,
'message': f"批量自动对话处理完成: 成功 {result_stats['success']}, 失败 {result_stats['failed']}, 跳过 {result_stats['skipped']}",
'data': {
'stats': result_stats,
'push_notification': notification_result
}
}, status=status.HTTP_200_OK)
except Exception as e:
logger.error(f"批量创建自动对话失败: {str(e)}")
error_details = traceback.format_exc()
return Response({
'code': 500,
'message': f'批量创建自动对话失败: {str(e)}',
'data': {
'details': error_details[:500]
}
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
def get(self, request, *args, **kwargs):
"""
获取所有批量创建的对话跟踪状态
"""
try:
# 获取用户所有跟踪记录
trackers = CreatorConversationTracker.objects.filter(user=request.user).select_related('creator_profile')
# 统计数据
stats = {
'total': trackers.count(),
'replied': trackers.filter(has_replied=True).count(),
'goal_achieved': trackers.filter(goal_achieved=True).count()
}
# 整理返回数据
tracker_data = []
for tracker in trackers:
tracker_data.append({
'id': str(tracker.id),
'creator_id': str(tracker.creator_profile.id),
'creator_name': tracker.creator_profile.name,
'creator_email': tracker.influencer_email,
'conversation_id': tracker.conversation_id,
'has_replied': tracker.has_replied,
'goal_achieved': tracker.goal_achieved,
'created_at': tracker.created_at.strftime('%Y-%m-%d %H:%M:%S'),
'updated_at': tracker.updated_at.strftime('%Y-%m-%d %H:%M:%S')
})
return Response({
'code': 200,
'message': '获取跟踪状态成功',
'data': {
'stats': stats,
'trackers': tracker_data
}
})
except Exception as e:
logger.error(f"获取跟踪状态失败: {str(e)}")
error_details = traceback.format_exc()
return Response({
'code': 500,
'message': f'获取跟踪状态失败: {str(e)}',
'data': {
'details': error_details[:500]
}
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)