daren_project/user_management/feishu_chat_views.py
2025-04-17 16:14:00 +08:00

434 lines
16 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 logging
import traceback
from rest_framework import status
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from user_management.models import (
GmailTalentMapping, ChatHistory, ConversationSummary
)
from user_management.gmail_integration import GmailIntegration
from feishu.feishu_ai_chat import (
fetch_table_records, find_duplicate_email_creators,
process_duplicate_emails, auto_chat_session,
check_goal_achieved
)
logger = logging.getLogger(__name__)
@api_view(['POST'])
@permission_classes([IsAuthenticated])
def process_feishu_table(request):
"""
从飞书多维表格读取数据,处理重复邮箱
请求参数:
table_id: 表格ID
view_id: 视图ID
app_token: 飞书应用TOKEN (可选)
access_token: 用户访问令牌 (可选)
app_id: 应用ID (可选)
app_secret: 应用密钥 (可选)
goal_template: 目标内容模板 (可选)
auto_chat: 是否自动执行AI对话 (可选)
turns: 自动对话轮次 (可选)
"""
try:
# 检查用户权限 - 只允许组长使用
if request.user.role != 'leader':
return Response(
{"error": "只有组长角色的用户可以使用此功能"},
status=status.HTTP_403_FORBIDDEN
)
# 获取参数
table_id = request.data.get("table_id", "tbl3oikG3F8YYtVA") # 默认表格ID
view_id = request.data.get("view_id", "vewSOIsmxc") # 默认视图ID
app_token = request.data.get("app_token", "XYE6bMQUOaZ5y5svj4vcWohGnmg")
access_token = request.data.get("access_token", "u-fK0HvbXVte.G2xzYs5oxV6k1nHu1glvFgG00l0Ma24VD")
app_id = request.data.get("app_id", "cli_a5c97daacb9e500d")
app_secret = request.data.get("app_secret", "fdVeOCLXmuIHZVmSV0VbJh9wd0Kq1o5y")
goal_template = request.data.get(
"goal_template",
"与达人{handle}(邮箱:{email})建立联系并了解其账号情况,评估合作潜力,处理合作需求,最终目标是达成合作并签约。"
)
auto_chat = request.data.get("auto_chat", False)
turns = request.data.get("turns", 5)
logger.info(f"处理飞书表格数据: table_id={table_id}, view_id={view_id}, app_id={app_id}")
# 从飞书表格获取记录
records = fetch_table_records(
app_token,
table_id,
view_id,
access_token,
app_id,
app_secret
)
if not records:
logger.warning("未获取到任何记录可能是表格ID或视图ID不正确或无权限访问")
# 尝试使用SDK中的search方法直接获取
try:
import lark_oapi as lark
from lark_oapi.api.bitable.v1 import (
SearchAppTableRecordRequest,
SearchAppTableRecordRequestBody
)
# 创建client
client = lark.Client.builder() \
.enable_set_token(True) \
.log_level(lark.LogLevel.DEBUG) \
.build()
# 构造请求对象
request = SearchAppTableRecordRequest.builder() \
.app_token(app_token) \
.table_id(table_id) \
.page_size(20) \
.request_body(SearchAppTableRecordRequestBody.builder().build()) \
.build()
# 发起请求
option = lark.RequestOption.builder().user_access_token(access_token).build()
response = client.bitable.v1.app_table_record.search(request, option)
if not response.success():
logger.error(f"直接搜索请求失败: {response.code}, {response.msg}")
return Response(
{"message": f"未获取到任何记录,错误: {response.msg}"},
status=status.HTTP_404_NOT_FOUND
)
# 获取记录
records = response.data.items
if not records:
return Response(
{"message": "未获取到任何记录"},
status=status.HTTP_404_NOT_FOUND
)
except Exception as e:
logger.error(f"尝试直接搜索时出错: {str(e)}")
logger.error(traceback.format_exc())
return Response(
{"message": f"未获取到任何记录,错误: {str(e)}"},
status=status.HTTP_404_NOT_FOUND
)
# 查找重复邮箱的创作者
duplicate_emails = find_duplicate_email_creators(records)
if not duplicate_emails:
return Response(
{"message": "未发现重复邮箱"},
status=status.HTTP_200_OK
)
# 处理重复邮箱记录
results = process_duplicate_emails(duplicate_emails, goal_template)
# 如果需要自动对话
chat_results = []
if auto_chat and results['success'] > 0:
# 为每个成功创建的记录执行自动对话
for detail in results['details']:
if detail['status'] == 'success':
email = detail['email']
chat_result = auto_chat_session(request.user, email, max_turns=turns)
chat_results.append({
'email': email,
'result': chat_result
})
# 返回处理结果
return Response({
'status': 'success',
'records_count': len(records),
'duplicate_emails_count': len(duplicate_emails),
'processing_results': results,
'chat_results': chat_results
}, status=status.HTTP_200_OK)
except Exception as e:
logger.error(f"处理飞书表格时出错: {str(e)}")
logger.error(traceback.format_exc())
return Response(
{"error": str(e)},
status=status.HTTP_500_INTERNAL_SERVER_ERROR
)
@api_view(['POST'])
@permission_classes([IsAuthenticated])
def run_auto_chat(request):
"""
为指定邮箱执行自动对话
请求参数:
email: 达人邮箱
force_send: 是否强制发送新邮件(即使没有新回复)(可选)
subject: 邮件主题(可选仅当force_send=true时使用)
content: 邮件内容(可选仅当force_send=true时使用)
"""
try:
# 检查用户权限 - 只允许组长使用
if request.user.role != 'leader':
return Response(
{"error": "只有组长角色的用户可以使用此功能"},
status=status.HTTP_403_FORBIDDEN
)
# 获取参数
email = request.data.get("email")
force_send = request.data.get("force_send", False)
subject = request.data.get("subject")
content = request.data.get("content")
# 验证必要参数
if not email:
return Response(
{"error": "缺少参数email"},
status=status.HTTP_400_BAD_REQUEST
)
# 如果强制发送且没有提供内容
if force_send and not content:
return Response(
{"error": "当force_send=true时必须提供content参数"},
status=status.HTTP_400_BAD_REQUEST
)
# 首先尝试同步最新邮件
try:
# 创建Gmail集成实例
gmail_integration = GmailIntegration(request.user)
# 同步最新邮件
logger.info(f"正在同步与 {email} 的最新邮件...")
sync_result = gmail_integration.sync_talent_emails(email)
if sync_result.get('status') == 'success':
logger.info(f"成功同步邮件: {sync_result.get('message', 'No message')}")
else:
logger.warning(f"同步邮件警告: {sync_result.get('message', 'Unknown warning')}")
except Exception as e:
logger.error(f"同步邮件出错: {str(e)}")
logger.error(traceback.format_exc())
# 仅记录错误,不中断流程
# 如果是强制发送模式
if force_send:
try:
# 获取知识库映射
mapping = GmailTalentMapping.objects.filter(
user=request.user,
talent_email=email,
is_active=True
).first()
if not mapping:
return Response(
{"error": f"找不到与邮箱 {email} 的映射关系"},
status=status.HTTP_404_NOT_FOUND
)
# 直接发送邮件
mail_subject = subject if subject else "关于合作的洽谈"
mail_result = gmail_integration.send_email(
to_email=email,
subject=mail_subject,
body=content,
conversation_id=mapping.conversation_id
)
if mail_result['status'] != 'success':
return Response(
{"error": f"邮件发送失败: {mail_result.get('message', 'Unknown error')}"},
status=status.HTTP_500_INTERNAL_SERVER_ERROR
)
# 保存发送的内容到对话历史
ChatHistory.objects.create(
user=request.user,
knowledge_base=mapping.knowledge_base,
conversation_id=mapping.conversation_id,
role='assistant',
content=content
)
return Response({
'status': 'success',
'message': f"已强制发送邮件到 {email}",
'email_sent': True,
'conversation_id': mapping.conversation_id
}, status=status.HTTP_200_OK)
except Exception as e:
logger.error(f"强制发送邮件时出错: {str(e)}")
logger.error(traceback.format_exc())
return Response(
{"error": str(e)},
status=status.HTTP_500_INTERNAL_SERVER_ERROR
)
# 执行自动对话
result = auto_chat_session(request.user, email)
# 返回结果
return Response(result, status=status.HTTP_200_OK)
except Exception as e:
logger.error(f"执行自动对话时出错: {str(e)}")
logger.error(traceback.format_exc())
return Response(
{"error": str(e)},
status=status.HTTP_500_INTERNAL_SERVER_ERROR
)
@api_view(['GET', 'POST'])
@permission_classes([IsAuthenticated])
def feishu_user_goal(request):
"""
设置或获取用户总目标
GET 请求:
获取当前用户总目标
POST 请求参数:
email: 达人邮箱
goal: 目标内容
"""
try:
# 检查用户权限 - 只允许组长使用
if request.user.role != 'leader':
return Response(
{"error": "只有组长角色的用户可以使用此功能"},
status=status.HTTP_403_FORBIDDEN
)
if request.method == 'GET':
# 创建Gmail集成实例
gmail_integration = GmailIntegration(request.user)
# 获取总目标
result = gmail_integration.manage_user_goal()
return Response(result, status=status.HTTP_200_OK)
elif request.method == 'POST':
# 获取参数
email = request.data.get("email")
goal = request.data.get("goal")
# 验证必要参数
if not email:
return Response(
{"error": "缺少参数email"},
status=status.HTTP_400_BAD_REQUEST
)
if not goal:
return Response(
{"error": "缺少参数goal"},
status=status.HTTP_400_BAD_REQUEST
)
# 设置用户总目标
gmail_integration = GmailIntegration(request.user)
result = gmail_integration.manage_user_goal(goal)
return Response(result, status=status.HTTP_200_OK)
except Exception as e:
logger.error(f"管理用户总目标时出错: {str(e)}")
logger.error(traceback.format_exc())
return Response(
{"error": str(e)},
status=status.HTTP_500_INTERNAL_SERVER_ERROR
)
@api_view(['GET'])
@permission_classes([IsAuthenticated])
def check_goal_status(request):
"""
检查目标完成状态
请求参数:
email: 达人邮箱
"""
try:
# 检查用户权限 - 只允许组长使用
if request.user.role != 'leader':
return Response(
{"error": "只有组长角色的用户可以使用此功能"},
status=status.HTTP_403_FORBIDDEN
)
# 获取参数
email = request.query_params.get("email")
# 验证必要参数
if not email:
return Response(
{"error": "缺少参数email"},
status=status.HTTP_400_BAD_REQUEST
)
# 查找Gmail映射关系
mapping = GmailTalentMapping.objects.filter(
user=request.user,
talent_email=email,
is_active=True
).first()
if not mapping:
return Response(
{"error": f"找不到与邮箱 {email} 的映射关系"},
status=status.HTTP_404_NOT_FOUND
)
# 获取对话历史中最后的AI回复
last_ai_message = ChatHistory.objects.filter(
user=request.user,
knowledge_base=mapping.knowledge_base,
conversation_id=mapping.conversation_id,
role='assistant',
is_deleted=False
).order_by('-created_at').first()
if not last_ai_message:
return Response(
{"error": f"找不到与邮箱 {email} 的对话历史"},
status=status.HTTP_404_NOT_FOUND
)
# 检查目标是否已达成
goal_achieved = check_goal_achieved(last_ai_message.content)
# 获取对话总结
summary = ConversationSummary.objects.filter(
user=request.user,
talent_email=email,
is_active=True
).order_by('-updated_at').first()
result = {
'status': 'success',
'email': email,
'goal_achieved': goal_achieved,
'last_message_time': last_ai_message.created_at.strftime('%Y-%m-%d %H:%M:%S'),
'last_message': last_ai_message.content,
'summary': summary.summary if summary else None
}
return Response(result, status=status.HTTP_200_OK)
except Exception as e:
logger.error(f"检查目标状态时出错: {str(e)}")
logger.error(traceback.format_exc())
return Response(
{"error": str(e)},
status=status.HTTP_500_INTERNAL_SERVER_ERROR
)