diff --git a/user_management/migrations/0002_alter_chathistory_metadata_and_more.py b/user_management/migrations/0002_alter_chathistory_metadata_and_more.py new file mode 100644 index 00000000..7fd6f00e --- /dev/null +++ b/user_management/migrations/0002_alter_chathistory_metadata_and_more.py @@ -0,0 +1,22 @@ +# Generated by Django 5.1.5 on 2025-03-21 04:35 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('user_management', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='chathistory', + name='metadata', + field=models.JSONField(blank=True, default=dict, help_text="\n {\n 'model_id': 'xxx',\n 'dataset_id_list': ['id1', 'id2', ...],\n 'dataset_external_id_list': ['ext1', 'ext2', ...],\n 'primary_knowledge_base': 'id1'\n }\n "), + ), + migrations.AddIndex( + model_name='chathistory', + index=models.Index(fields=['conversation_id', 'is_deleted'], name='chat_histor_convers_89bc43_idx'), + ), + ] diff --git a/user_management/models.py b/user_management/models.py index 91f35cab..eb3ce915 100644 --- a/user_management/models.py +++ b/user_management/models.py @@ -1,3 +1,4 @@ +from itertools import count from django.contrib.auth.models import AbstractUser from django.db import models from django.utils import timezone @@ -286,13 +287,23 @@ class ChatHistory(models.Model): ] user = models.ForeignKey(User, on_delete=models.CASCADE) + # 保留与主知识库的关联 knowledge_base = models.ForeignKey('KnowledgeBase', on_delete=models.CASCADE) + # 用于标识知识库组合的对话 conversation_id = models.CharField(max_length=100, db_index=True) parent_id = models.CharField(max_length=100, null=True, blank=True) role = models.CharField(max_length=20, choices=ROLE_CHOICES) content = models.TextField() tokens = models.IntegerField(default=0, help_text="消息token数") - metadata = models.JSONField(default=dict, blank=True) + # 扩展metadata字段,用于存储知识库组合信息 + metadata = models.JSONField(default=dict, blank=True, help_text=""" + { + 'model_id': 'xxx', + 'dataset_id_list': ['id1', 'id2', ...], + 'dataset_external_id_list': ['ext1', 'ext2', ...], + 'primary_knowledge_base': 'id1' + } + """) created_at = models.DateTimeField(auto_now_add=True) is_deleted = models.BooleanField(default=False) @@ -302,6 +313,8 @@ class ChatHistory(models.Model): indexes = [ models.Index(fields=['conversation_id', 'created_at']), models.Index(fields=['user', 'created_at']), + # 添加新的索引以支持知识库组合查询 + models.Index(fields=['conversation_id', 'is_deleted']), ] def __str__(self): @@ -315,11 +328,69 @@ class ChatHistory(models.Model): is_deleted=False ).order_by('created_at') + @classmethod + def get_conversations_by_knowledge_bases(cls, dataset_ids, user): + """根据知识库组合获取对话历史""" + # 对知识库ID列表排序以确保一致性 + sorted_kb_ids = sorted(dataset_ids) + conversation_id = str(uuid.uuid5( + uuid.NAMESPACE_DNS, + '-'.join(sorted_kb_ids) + )) + + return cls.objects.filter( + conversation_id=conversation_id, + user=user, + is_deleted=False + ).order_by('created_at') + + @classmethod + def get_knowledge_base_combinations(cls, user): + """获取用户的所有知识库组合""" + return cls.objects.filter( + user=user, + is_deleted=False + ).values('conversation_id').annotate( + last_message=max('created_at'), + message_count=count('id') + ).values( + 'conversation_id', + 'last_message', + 'message_count', + 'metadata' + ).order_by('-last_message') + + def get_knowledge_bases(self): + """获取此消息关联的所有知识库""" + if self.metadata and 'dataset_id_list' in self.metadata: + return KnowledgeBase.objects.filter( + id__in=self.metadata['dataset_id_list'] + ) + return KnowledgeBase.objects.filter(id=self.knowledge_base.id) + def soft_delete(self): """软删除消息""" self.is_deleted = True self.save() + def to_dict(self): + """转换为字典格式""" + return { + 'id': str(self.id), + 'conversation_id': self.conversation_id, + 'role': self.role, + 'content': self.content, + 'created_at': self.created_at.strftime('%Y-%m-%d %H:%M:%S'), + 'metadata': self.metadata, + 'knowledge_bases': [ + { + 'id': str(kb.id), + 'name': kb.name, + 'type': kb.type + } for kb in self.get_knowledge_bases() + ] + } + class UserProfile(models.Model): """用户档案模型""" user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile') diff --git a/user_management/views.py b/user_management/views.py index 8cfcc1dd..6ee9d9f6 100644 --- a/user_management/views.py +++ b/user_management/views.py @@ -26,7 +26,7 @@ import os from rest_framework.test import APIRequestFactory from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.fields import GenericForeignKey -from django.http import Http404 +from django.http import Http404, HttpResponse from django.db import IntegrityError from channels.exceptions import ChannelFull from django.conf import settings @@ -93,79 +93,73 @@ class ChatHistoryViewSet(viewsets.ModelViewSet): ) def list(self, request): - """获取聊天记录列表""" + """获取对话列表概览""" try: # 获取查询参数 - dataset_id = request.query_params.get('dataset_id') page = int(request.query_params.get('page', 1)) page_size = int(request.query_params.get('page_size', 10)) - query = self.get_queryset() + # 获取所有对话的概览 + latest_chats = self.get_queryset().values( + 'conversation_id' + ).annotate( + latest_id=Max('id'), + message_count=Count('id'), + last_message=Max('created_at') + ).order_by('-last_message') - if dataset_id: - # 获取特定知识库的完整对话历史 - records = query.filter( - knowledge_base__id=dataset_id - ).order_by('created_at') + # 计算分页 + total = latest_chats.count() + start = (page - 1) * page_size + end = start + page_size + chats = latest_chats[start:end] - conversation = { - 'dataset_id': dataset_id, - 'dataset_name': records.first().knowledge_base.name if records.exists() else None, - 'messages': [{ - 'id': record.id, - 'role': record.role, - 'content': record.content, - 'created_at': record.created_at.strftime('%Y-%m-%d %H:%M:%S') - } for record in records] + results = [] + for chat in chats: + # 获取最新消息记录 + latest_record = ChatHistory.objects.get(id=chat['latest_id']) + + # 从metadata中获取完整的知识库信息 + dataset_info = [] + if latest_record.metadata: + dataset_id_list = latest_record.metadata.get('dataset_id_list', []) + dataset_names = latest_record.metadata.get('dataset_names', []) + + # 如果有知识库ID列表 + if dataset_id_list: + # 如果同时有名称列表且长度匹配 + if dataset_names and len(dataset_names) == len(dataset_id_list): + dataset_info = [{ + 'id': str(id), + 'name': name + } for id, name in zip(dataset_id_list, dataset_names)] + else: + # 如果没有名称列表,则只返回ID + datasets = KnowledgeBase.objects.filter(id__in=dataset_id_list) + dataset_info = [{ + 'id': str(ds.id), + 'name': ds.name + } for ds in datasets] + + results.append({ + 'conversation_id': chat['conversation_id'], + 'message_count': chat['message_count'], + 'last_message': latest_record.content, + 'last_time': chat['last_message'].strftime('%Y-%m-%d %H:%M:%S'), + 'dataset_id_list': [ds['id'] for ds in dataset_info], # 添加完整的知识库ID列表 + 'datasets': dataset_info # 包含ID和名称的完整信息 + }) + + return Response({ + 'code': 200, + 'message': '获取成功', + 'data': { + 'total': total, + 'page': page, + 'page_size': page_size, + 'results': results } - - return Response({ - 'code': 200, - 'message': '获取成功', - 'data': conversation - }) - else: - # 获取所有对话的概览 - latest_chats = query.values( - 'conversation_id', - 'knowledge_base__id', - 'knowledge_base__name' - ).annotate( - latest_id=Max('id'), - message_count=Count('id'), - last_message=Max('created_at') - ).order_by('-last_message') - - # 计算分页 - total = latest_chats.count() - start = (page - 1) * page_size - end = start + page_size - - # 获取分页数据 - chats = latest_chats[start:end] - - results = [] - for chat in chats: - latest_record = ChatHistory.objects.get(id=chat['latest_id']) - results.append({ - 'conversation_id': chat['conversation_id'], - 'dataset_id': str(chat['knowledge_base__id']), - 'dataset_name': chat['knowledge_base__name'], - 'message_count': chat['message_count'], - 'last_message': latest_record.content, - 'last_time': chat['last_message'].strftime('%Y-%m-%d %H:%M:%S') - }) - - return Response({ - 'code': 200, - 'message': '获取成功', - 'data': { - 'total': total, - 'page': page, - 'page_size': page_size, - 'results': results - } - }) + }) except Exception as e: logger.error(f"获取聊天记录失败: {str(e)}") @@ -176,6 +170,119 @@ class ChatHistoryViewSet(viewsets.ModelViewSet): 'data': None }, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + @action(detail=False, methods=['get']) + def conversation_detail(self, request): + """获取特定对话的详细信息""" + try: + conversation_id = request.query_params.get('conversation_id') + if not conversation_id: + return Response({ + 'code': 400, + 'message': '缺少conversation_id参数', + 'data': None + }, status=status.HTTP_400_BAD_REQUEST) + + # 获取对话历史 + messages = self.get_queryset().filter( + conversation_id=conversation_id + ).order_by('created_at') + + if not messages.exists(): + return Response({ + 'code': 404, + 'message': '对话不存在', + 'data': None + }, status=status.HTTP_404_NOT_FOUND) + + # 获取知识库信息 + first_message = messages.first() + dataset_info = [] + if first_message and first_message.metadata: + if 'dataset_id_list' in first_message.metadata: + datasets = KnowledgeBase.objects.filter( + id__in=first_message.metadata['dataset_id_list'] + ) + dataset_info = [{ + 'id': str(ds.id), + 'name': ds.name, + 'type': ds.type + } for ds in datasets] + + return Response({ + 'code': 200, + 'message': '获取成功', + 'data': { + 'conversation_id': conversation_id, + 'datasets': dataset_info, + 'messages': [{ + 'id': str(msg.id), + 'role': msg.role, + 'content': msg.content, + 'created_at': msg.created_at.strftime('%Y-%m-%d %H:%M:%S') + } for msg in messages] + } + }) + + 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) + + @action(detail=False, methods=['get']) + def available_datasets(self, request): + """获取用户可访问的知识库列表""" + try: + user = request.user + + # 获取用户有权限访问的知识库 + accessible_datasets = [] + + # 1. 获取用户通过权限表直接授权的知识库 + permission_datasets = KnowledgeBase.objects.filter( + id__in=KBPermissionModel.objects.filter( + user=user, + can_read=True, + status='active' + ).values_list('knowledge_base_id', flat=True) + ) + + # 2. 获取用户根据角色可以访问的知识库 + role_datasets = KnowledgeBase.objects.filter( + Q(type='member', department=user.department) | # 成员级知识库 + Q(type='leader', department=user.department) | # 部门级知识库,组长可访问 + Q(type='admin') # 管理级知识库,管理员可访问 + ).exclude( + Q(type='private') & ~Q(user_id=str(user.id)) # 排除不属于自己的私人知识库 + ) + + # 3. 合并并去重 + accessible_datasets = list(set(list(permission_datasets) + list(role_datasets))) + + return Response({ + 'code': 200, + 'message': '获取成功', + 'data': [{ + 'id': str(ds.id), + 'name': ds.name, + 'type': ds.type, + 'department': ds.department, + 'description': ds.desc + } for ds in accessible_datasets] + }) + + 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) + def create(self, request): """创建聊天记录""" try: @@ -192,9 +299,23 @@ class ChatHistoryViewSet(viewsets.ModelViewSet): # 检查知识库ID:支持dataset_id或dataset_id_list格式 dataset_ids = [] if 'dataset_id' in data: - dataset_ids.append(data['dataset_id']) - elif 'dataset_id_list' in data and isinstance(data['dataset_id_list'], list): - dataset_ids = data['dataset_id_list'] + dataset_id = data['dataset_id'] + # 直接使用标准UUID格式 + dataset_ids.append(str(dataset_id)) + elif 'dataset_id_list' in data and isinstance(data['dataset_id_list'], (list, str)): + # 处理可能的字符串格式 + if isinstance(data['dataset_id_list'], str): + try: + # 尝试解析JSON字符串 + dataset_list = json.loads(data['dataset_id_list']) + if isinstance(dataset_list, list): + dataset_ids = [str(id) for id in dataset_list] + except json.JSONDecodeError: + # 如果解析失败,可能是单个ID + dataset_ids = [str(data['dataset_id_list'])] + else: + # 如果已经是列表,直接使用标准UUID格式 + dataset_ids = [str(id) for id in data['dataset_id_list']] else: return Response({ 'code': 400, @@ -212,9 +333,9 @@ class ChatHistoryViewSet(viewsets.ModelViewSet): # 验证所有知识库并收集external_ids external_id_list = [] user = request.user - primary_knowledge_base = None # 主知识库,用于关联聊天记录 + knowledge_bases = [] # 存储所有知识库对象 - for idx, kb_id in enumerate(dataset_ids): + for kb_id in dataset_ids: try: knowledge_base = KnowledgeBase.objects.filter(id=kb_id).first() if not knowledge_base: @@ -224,9 +345,7 @@ class ChatHistoryViewSet(viewsets.ModelViewSet): 'data': None }, status=status.HTTP_404_NOT_FOUND) - # 保存第一个知识库作为主知识库 - if idx == 0: - primary_knowledge_base = knowledge_base + knowledge_bases.append(knowledge_base) # 检查知识库权限 can_read = False @@ -242,7 +361,6 @@ class ChatHistoryViewSet(viewsets.ModelViewSet): if permission: can_read = True else: - # 使用_can_read方法判断 can_read = self._can_read( type=knowledge_base.type, user=user, @@ -279,7 +397,20 @@ class ChatHistoryViewSet(viewsets.ModelViewSet): }, status=status.HTTP_400_BAD_REQUEST) # 获取或创建对话ID - conversation_id = data.get('conversation_id', str(uuid.uuid4())) + conversation_id = data.get('conversation_id') + + # 如果没有提供 conversation_id,根据知识库组合生成新的ID + if not conversation_id: + # 对知识库ID列表排序以确保相同组合生成相同的hash + sorted_kb_ids = sorted(dataset_ids) + # 使用知识库ID组合生成唯一的conversation_id + conversation_id = str(uuid.uuid5( + uuid.NAMESPACE_DNS, + '-'.join(sorted_kb_ids) + )) + logger.info(f"为知识库组合 {sorted_kb_ids} 生成新的conversation_id: {conversation_id}") + else: + logger.info(f"使用现有conversation_id: {conversation_id}") # 调用外部API获取答案 (传递多个knowledge base的external_id) answer = self._get_answer_from_external_api( @@ -294,41 +425,44 @@ class ChatHistoryViewSet(viewsets.ModelViewSet): 'data': None }, status=status.HTTP_500_INTERNAL_SERVER_ERROR) - # 创建用户问题记录 (关联到主知识库) + # 准备完整的metadata + metadata = { + 'model_id': data.get('model_id', '58c5deb4-f2e2-11ef-9a1b-0242ac120009'), + 'dataset_id_list': [str(id) for id in dataset_ids], + 'dataset_external_id_list': [str(id) for id in external_id_list], + 'dataset_names': [kb.name for kb in knowledge_bases] # 添加知识库名称列表 + } + + # 创建用户问题记录 question_record = ChatHistory.objects.create( user=request.user, - knowledge_base=primary_knowledge_base, - conversation_id=conversation_id, + knowledge_base=knowledge_bases[0], # 仍然需要一个主知识库,使用第一个 + conversation_id=str(conversation_id), role='user', content=data['question'], - metadata={ - 'model_id': data.get('model_id', '58c5deb4-f2e2-11ef-9a1b-0242ac120009'), - 'dataset_id_list': dataset_ids - } + metadata=metadata ) # 创建AI回答记录 answer_record = ChatHistory.objects.create( user=request.user, - knowledge_base=primary_knowledge_base, - conversation_id=conversation_id, + knowledge_base=knowledge_bases[0], # 仍然需要一个主知识库,使用第一个 + conversation_id=str(conversation_id), parent_id=str(question_record.id), role='assistant', content=answer, - metadata={ - 'model_id': data.get('model_id', '58c5deb4-f2e2-11ef-9a1b-0242ac120009'), - 'dataset_id_list': dataset_ids - } + metadata=metadata ) + # 返回完整的响应 return Response({ 'code': 200, 'message': '创建成功', 'data': { - 'id': answer_record.id, - 'conversation_id': conversation_id, - 'dataset_id': str(primary_knowledge_base.id), - 'dataset_name': primary_knowledge_base.name, + 'id': str(answer_record.id), + 'conversation_id': str(conversation_id), + 'dataset_id_list': [str(id) for id in dataset_ids], + 'dataset_names': [kb.name for kb in knowledge_bases], # 返回所有知识库名称 'role': 'assistant', 'content': answer_record.content, 'created_at': answer_record.created_at.strftime('%Y-%m-%d %H:%M:%S') @@ -342,7 +476,7 @@ class ChatHistoryViewSet(viewsets.ModelViewSet): 'code': 500, 'message': f'创建聊天记录失败: {str(e)}', 'data': None - }, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + }, status.HTTP_500_INTERNAL_SERVER_ERROR) def _get_answer_from_external_api(self, dataset_external_id_list, question): """调用外部API获取AI回答""" @@ -354,7 +488,7 @@ class ChatHistoryViewSet(viewsets.ModelViewSet): # 第一个API调用创建聊天 chat_request_data = { - "id": "d333dee2-b3c2-11ef-af2c-a4bb6dafa942", + "id": "65031f4d-c86d-430e-8089-d8ff2731a837", "model_id": "58c5deb4-f2e2-11ef-9a1b-0242ac120009", "dataset_id_list": dataset_external_ids, "multiple_rounds_dialogue": False, @@ -617,6 +751,210 @@ class ChatHistoryViewSet(viewsets.ModelViewSet): 'message': f'搜索失败: {str(e)}', 'data': None }, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + + @action(detail=False, methods=['get']) + def export(self, request): + """导出聊天记录为Excel文件""" + try: + # 获取查询参数 + conversation_id = request.query_params.get('conversation_id') + dataset_id = request.query_params.get('dataset_id') + history_days = request.query_params.get('history_days', '7') # 默认导出最近7天 + + # 至少需要一个筛选条件 + if not conversation_id and not dataset_id: + return Response({ + 'code': 400, + 'message': '需要提供conversation_id或dataset_id参数', + 'data': None + }, status=status.HTTP_400_BAD_REQUEST) + + # 验证权限 + user = request.user + if dataset_id: + knowledge_base = KnowledgeBase.objects.filter(id=dataset_id).first() + if not knowledge_base: + return Response({ + 'code': 404, + 'message': '知识库不存在', + 'data': None + }, status=status.HTTP_404_NOT_FOUND) + + # 检查是否有读取权限 + can_read = False + permission = KBPermissionModel.objects.filter( + knowledge_base=knowledge_base, + user=user, + can_read=True, + status='active' + ).first() + + if permission: + can_read = True + else: + can_read = self._can_read( + type=knowledge_base.type, + user=user, + department=knowledge_base.department, + group=knowledge_base.group, + creator_id=knowledge_base.user_id + ) + + if not can_read: + return Response({ + 'code': 403, + 'message': '无权访问该知识库', + 'data': None + }, status=status.HTTP_403_FORBIDDEN) + + # 查询确认有聊天记录存在 + query = self.get_queryset() + if conversation_id: + records = query.filter(conversation_id=conversation_id) + elif dataset_id: + records = query.filter(knowledge_base__id=dataset_id) + + if not records.exists(): + return Response({ + 'code': 404, + 'message': '未找到相关对话记录', + 'data': None + }, status=status.HTTP_404_NOT_FOUND) + + # 调用外部API导出Excel文件 - 使用GET请求 + application_id = "65031f4d-c86d-430e-8089-d8ff2731a837" # 固定值 + export_url = f"{settings.API_BASE_URL}/api/application/{application_id}/chat/export?history_day={history_days}" + + logger.info(f"发送导出请求:{export_url}") + + export_response = requests.get( + url=export_url, + timeout=60, + stream=True # 使用流式传输处理大文件 + ) + + # 检查响应状态 + if export_response.status_code != 200: + logger.error(f"导出API调用失败: {export_response.status_code}, {export_response.text}") + return Response({ + 'code': 500, + 'message': '导出失败,外部服务返回错误', + 'data': None + }, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + + # 创建响应对象并设置文件下载头 + response = HttpResponse( + content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' + ) + response['Content-Disposition'] = 'attachment; filename="data.xlsx"' + + # 将API响应内容写入响应对象 + for chunk in export_response.iter_content(chunk_size=8192): + if chunk: + response.write(chunk) + + logger.info("导出成功完成") + return response + + 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) + + @action(detail=False, methods=['get']) + def chat_list(self, request): + """获取对话列表""" + try: + # 获取查询参数 + history_days = request.query_params.get('history_days', '7') # 默认7天 + + # 构建API请求 + application_id = "65031f4d-c86d-430e-8089-d8ff2731a837" + api_url = f"{settings.API_BASE_URL}/api/application/{application_id}/chat" + + # 添加查询参数 + params = { + 'history_day': history_days + } + + logger.info(f"发送获取对话列表请求:{api_url}") + + # 调用外部API + response = requests.get( + url=api_url, + params=params, + timeout=30 + ) + + if response.status_code != 200: + logger.error(f"获取对话列表失败: {response.status_code}, {response.text}") + return Response({ + 'code': 500, + 'message': '获取对话列表失败,外部服务返回错误', + 'data': None + }, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + + # 解析响应数据 + try: + result = response.json() + + if result.get('code') != 200: + logger.error(f"外部API返回错误: {result}") + return Response({ + 'code': result.get('code', 500), + 'message': result.get('message', '获取对话列表失败'), + 'data': None + }, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + + # 处理返回的数据 + chat_list = result.get('data', []) + + # 格式化返回数据 + formatted_chats = [] + for chat in chat_list: + formatted_chat = { + 'id': chat['id'], + 'chat_id': chat['chat_id'], + 'abstract': chat['abstract'], + 'message_count': chat['chat_record_count'], + 'created_at': datetime.fromisoformat(chat['create_time'].replace('Z', '+00:00')).strftime('%Y-%m-%d %H:%M:%S'), + 'updated_at': datetime.fromisoformat(chat['update_time'].replace('Z', '+00:00')).strftime('%Y-%m-%d %H:%M:%S'), + 'star_count': chat['star_num'], + 'trample_count': chat['trample_num'], + 'mark_sum': chat['mark_sum'], + 'is_deleted': chat['is_deleted'] + } + formatted_chats.append(formatted_chat) + + return Response({ + 'code': 200, + 'message': '获取成功', + 'data': { + 'total': len(formatted_chats), + 'results': formatted_chats + } + }) + + except json.JSONDecodeError as e: + logger.error(f"解析响应数据失败: {str(e)}") + return Response({ + 'code': 500, + 'message': '解析响应数据失败', + 'data': None + }, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + + 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) def _highlight_keyword(self, text, keyword): """高亮关键词"""