daren/apps/discovery/views.py
2025-05-23 19:25:35 +08:00

686 lines
35 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.

from django.shortcuts import render
from django.db.models import Q
from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.permissions import AllowAny
from django.utils import timezone
from rest_framework.permissions import IsAuthenticated
from apps.user.authentication import CustomTokenAuthentication
from .models import SearchSession, Creator
from .serializers import (
SearchSessionSerializer,
SearchSessionDetailSerializer,
CreatorSerializer,
CreatorDetailSerializer
)
from .pagination import StandardResultsSetPagination
class ApiResponse:
"""API统一响应格式"""
@staticmethod
def success(data=None, message="操作成功"):
return Response({
"code": 200,
"message": message,
"data": data
})
@staticmethod
def error(message="操作失败", code=400, data=None):
return Response({
"code": code,
"message": message,
"data": data
}, status=status.HTTP_200_OK) # 始终返回200状态码错误信息在内容中提供
class SearchSessionViewSet(viewsets.ModelViewSet):
"""搜索会话视图集"""
queryset = SearchSession.objects.all()
serializer_class = SearchSessionSerializer
authentication_classes = [CustomTokenAuthentication]
permission_classes = [IsAuthenticated]
pagination_class = StandardResultsSetPagination
def get_serializer_class(self):
if self.action == 'retrieve':
return SearchSessionDetailSerializer
return SearchSessionSerializer
def list(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset())
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(queryset, many=True)
return ApiResponse.success(serializer.data, "获取搜索会话列表成功")
def retrieve(self, request, *args, **kwargs):
instance = self.get_object()
serializer = self.get_serializer(instance)
return ApiResponse.success(serializer.data, "获取搜索会话详情成功")
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return ApiResponse.success(serializer.data, "创建搜索会话成功")
def update(self, request, *args, **kwargs):
partial = kwargs.pop('partial', False)
instance = self.get_object()
serializer = self.get_serializer(instance, data=request.data, partial=partial)
serializer.is_valid(raise_exception=True)
self.perform_update(serializer)
return ApiResponse.success(serializer.data, "更新搜索会话成功")
def destroy(self, request, *args, **kwargs):
instance = self.get_object()
self.perform_destroy(instance)
return ApiResponse.success(None, "删除搜索会话成功")
@action(detail=True, methods=['get'])
def results(self, request, pk=None):
"""获取指定会话的搜索结果"""
session = self.get_object()
creators = session.creators.all()
page = self.paginate_queryset(creators)
if page is not None:
serializer = CreatorSerializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = CreatorSerializer(creators, many=True)
return ApiResponse.success(serializer.data, "获取会话创作者列表成功")
@action(detail=False, methods=['get'])
def summary(self, request):
"""获取所有会话的摘要数据,用于展示在表格中"""
queryset = self.filter_queryset(self.get_queryset())
# 格式化响应数据
result = []
for session in queryset:
result.append({
'id': session.id,
'session_number': session.session_number,
'date': session.date_created,
'creator_count': session.creator_count,
'shoppable_creators': session.shoppable_creators,
'avg_followers': session.avg_followers,
'avg_gmv': session.avg_gmv,
'avg_video_views': session.avg_video_views
})
return ApiResponse.success(result, "获取会话摘要列表成功")
class CreatorDiscoveryViewSet(viewsets.ReadOnlyModelViewSet):
"""创作者发现视图集"""
queryset = Creator.objects.all()
serializer_class = CreatorSerializer
authentication_classes = [CustomTokenAuthentication]
permission_classes = [IsAuthenticated]
pagination_class = StandardResultsSetPagination
def get_serializer_class(self):
if self.action == 'retrieve':
return CreatorDetailSerializer
return CreatorSerializer
def list(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset())
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(queryset, many=True)
return ApiResponse.success(serializer.data, "获取创作者列表成功")
def retrieve(self, request, *args, **kwargs):
instance = self.get_object()
serializer = self.get_serializer(instance)
return ApiResponse.success(serializer.data, "获取创作者详情成功")
@action(detail=False, methods=['post'])
def search(self, request):
"""搜索创作者"""
query = request.data.get('query', '')
category = request.data.get('category', None)
ecommerce_level = request.data.get('ecommerce_level', None)
exposure_level = request.data.get('exposure_level', None)
hashtag = request.data.get('hashtag', None)
trend = request.data.get('trend', None)
profile = request.data.get('profile', None) # 新增profile参数
try:
# 导入CreatorProfile模型
from apps.daren_detail.models import CreatorProfile
import logging
logger = logging.getLogger(__name__)
# 获取或创建当天的session
today = timezone.now().date()
today_session = SearchSession.objects.filter(date_created=today).first()
if not today_session:
# 如果当天没有session创建一个
max_session_number = SearchSession.objects.all().order_by('-session_number').first()
session_number = 1
if max_session_number:
session_number = max_session_number.session_number + 1
today_session = SearchSession.objects.create(
session_number=session_number,
creator_count=0,
shoppable_creators=0,
avg_followers=0,
avg_gmv=0,
avg_video_views=0,
date_created=today
)
# 构建查询条件
query_filters = {}
if query:
query_filters['name__icontains'] = query
if category:
query_filters['category'] = category
if ecommerce_level:
if isinstance(ecommerce_level, str) and ecommerce_level.startswith('L') and len(ecommerce_level) > 1:
try:
ecommerce_level_num = int(ecommerce_level[1:])
query_filters['e_commerce_level'] = ecommerce_level_num
except ValueError:
pass
else:
query_filters['e_commerce_level'] = ecommerce_level
if exposure_level:
query_filters['exposure_level'] = exposure_level
if profile: # 增加对profile的过滤
query_filters['profile'] = profile
# 处理hashtag和trend的查询
creator_profiles = CreatorProfile.objects.filter(**query_filters)
# 如果指定了hashtag进行精确过滤
if hashtag:
creator_profiles = creator_profiles.filter(hashtags__contains=f"#{hashtag}")
# 如果没有找到匹配的数据,尝试创建一个演示数据
if not creator_profiles.exists():
# 创建一个演示数据,实际生产中应删除这段逻辑
demo_profile = CreatorProfile.objects.create(
name=f"Demo Creator with #{hashtag}",
followers=1000,
gmv=500.0,
items_sold=20,
avg_video_views=2000,
category="Fashion Accessories" if category is None else category,
e_commerce_level=3 if ecommerce_level is None else ecommerce_level,
exposure_level="KOL-2" if exposure_level is None else exposure_level,
hashtags=f"#{hashtag}#style#trending",
trends=f"summer {hashtag}",
profile=profile if profile else "tiktok"
)
creator_profiles = CreatorProfile.objects.filter(pk=demo_profile.pk)
logger.info(f"创建了测试数据: {demo_profile.name}")
# 如果指定了trend进行精确过滤
if trend:
creator_profiles = creator_profiles.filter(trends__contains=trend)
# 如果没有找到匹配的数据,尝试创建一个演示数据
if not creator_profiles.exists():
# 创建一个演示数据,实际生产中应删除这段逻辑
demo_profile = CreatorProfile.objects.create(
name=f"Demo Creator with {trend} trend",
followers=1200,
gmv=600.0,
items_sold=25,
avg_video_views=2500,
category="Beauty & Personal Care" if category is None else category,
e_commerce_level=4 if ecommerce_level is None else ecommerce_level,
exposure_level="KOL-1" if exposure_level is None else exposure_level,
hashtags=f"#beauty#{trend}#trending",
trends=f"{trend} season",
profile=profile if profile else "tiktok"
)
creator_profiles = CreatorProfile.objects.filter(pk=demo_profile.pk)
logger.info(f"创建了测试数据: {demo_profile.name}")
# 存储匹配的Creator ID列表
matched_creator_ids = []
# 对每个找到的CreatorProfile创建Creator记录
for profile_obj in creator_profiles:
# 检查是否已经在当前session中存在相同creator
existing_creator = Creator.objects.filter(
session=today_session,
name=profile_obj.name
).first()
if existing_creator:
# 如果已存在记录ID用于过滤
matched_creator_ids.append(existing_creator.id)
else:
# 确定电商等级字符串
ecommerce_level_str = "New tag"
if profile_obj.e_commerce_level is not None:
ecommerce_level_str = f"L{profile_obj.e_commerce_level}"
# 创建Creator记录
new_creator = Creator.objects.create(
session=today_session,
name=profile_obj.name,
avatar=profile_obj.avatar_url if profile_obj.avatar_url else None,
category=profile_obj.category if profile_obj.category else "Other",
ecommerce_level=ecommerce_level_str,
exposure_level=profile_obj.exposure_level if profile_obj.exposure_level else "New tag",
followers=float(profile_obj.followers) if profile_obj.followers else 0,
gmv=float(profile_obj.gmv) if profile_obj.gmv else 0,
items_sold=float(profile_obj.items_sold) if profile_obj.items_sold else 0,
avg_video_views=float(profile_obj.avg_video_views) if profile_obj.avg_video_views else 0,
has_ecommerce=profile_obj.e_commerce_level is not None,
tiktok_url=profile_obj.tiktok_link if profile_obj.tiktok_link else None,
hashtags=profile_obj.hashtags, # 确保复制hashtags
trends=profile_obj.trends, # 确保复制trends
profile=profile_obj.profile # 复制profile字段
)
matched_creator_ids.append(new_creator.id)
# 更新session统计信息
all_creators = today_session.creators.all()
total_creators = all_creators.count()
if total_creators > 0:
# 计算平均值
total_followers = sum(creator.followers for creator in all_creators)
total_gmv = sum(creator.gmv for creator in all_creators)
total_video_views = sum(creator.avg_video_views for creator in all_creators)
today_session.creator_count = total_creators
today_session.shoppable_creators = all_creators.filter(has_ecommerce=True).count()
today_session.avg_followers = round(total_followers / total_creators, 1)
today_session.avg_gmv = round(total_gmv / total_creators, 1)
today_session.avg_video_views = round(total_video_views / total_creators, 1)
today_session.save()
# 只获取与搜索匹配的结果而不是session中的所有creators
matched_creators = Creator.objects.filter(id__in=matched_creator_ids)
# 创建一个自定义响应
search_type = "搜索创作者"
if hashtag:
search_type = f"搜索标签 #{hashtag}"
elif trend:
search_type = f"搜索趋势 {trend}"
response_data = {
"id": today_session.id,
"session_number": today_session.session_number,
"creator_count": matched_creators.count(),
"shoppable_creators": matched_creators.filter(has_ecommerce=True).count(),
"avg_followers": round(sum(c.followers for c in matched_creators) / matched_creators.count(), 1) if matched_creators.count() > 0 else 0,
"avg_gmv": round(sum(c.gmv for c in matched_creators) / matched_creators.count(), 1) if matched_creators.count() > 0 else 0,
"avg_video_views": round(sum(c.avg_video_views for c in matched_creators) / matched_creators.count(), 1) if matched_creators.count() > 0 else 0,
"date_created": today_session.date_created,
"creators": CreatorSerializer(matched_creators, many=True).data
}
return ApiResponse.success(response_data, f"{search_type}成功")
except Exception as e:
import traceback
logger.error(f"搜索创作者时发生错误: {str(e)}")
logger.error(traceback.format_exc())
return ApiResponse.error(f"搜索创作者失败: {str(e)}")
@action(detail=False, methods=['post'])
def search_tags(self, request):
"""专门用于搜索标签和趋势的API"""
mode = request.data.get('mode', '') # 'hashtag' 或 'trend'
keyword = request.data.get('keyword', '')
profile = request.data.get('profile', None) # 新增profile参数
if not mode or not keyword:
return ApiResponse.error("缺少必要参数: mode和keyword", code=400)
try:
# 导入CreatorProfile模型
from apps.daren_detail.models import CreatorProfile
import logging
logger = logging.getLogger(__name__)
# 获取或创建当天的session
today = timezone.now().date()
today_session = SearchSession.objects.filter(date_created=today).first()
if not today_session:
# 如果当天没有session创建一个
max_session_number = SearchSession.objects.all().order_by('-session_number').first()
session_number = 1
if max_session_number:
session_number = max_session_number.session_number + 1
today_session = SearchSession.objects.create(
session_number=session_number,
creator_count=0,
shoppable_creators=0,
avg_followers=0,
avg_gmv=0,
avg_video_views=0,
date_created=today
)
# 构建查询条件
query_filters = {}
if profile: # 添加平台过滤
query_filters['profile'] = profile
# 根据模式选择搜索字段
if mode.lower() == 'hashtag':
creator_profiles = CreatorProfile.objects.filter(hashtags__contains=f"#{keyword}", **query_filters)
elif mode.lower() == 'trend':
creator_profiles = CreatorProfile.objects.filter(trends__contains=keyword, **query_filters)
else:
return ApiResponse.error(f"不支持的搜索模式: {mode},请使用 'hashtag''trend'", code=400)
# 日志记录查询结果
logger.info(f"搜索{mode.lower()}'{keyword}'找到{creator_profiles.count()}个结果")
for profile_obj in creator_profiles:
if mode.lower() == 'hashtag':
logger.info(f"Profile: {profile_obj.name}, Hashtags: {profile_obj.hashtags}")
else:
logger.info(f"Profile: {profile_obj.name}, Trends: {profile_obj.trends}")
# 存储匹配的Creator ID列表
matched_creator_ids = []
# 对每个找到的CreatorProfile创建Creator记录
created_count = 0
for profile_obj in creator_profiles:
# 检查是否已经在当前session中存在相同creator
existing_creator = Creator.objects.filter(
session=today_session,
name=profile_obj.name
).first()
if existing_creator:
# 如果已存在记录ID用于过滤
matched_creator_ids.append(existing_creator.id)
else:
# 确定电商等级字符串
ecommerce_level_str = "New tag"
if profile_obj.e_commerce_level is not None:
ecommerce_level_str = f"L{profile_obj.e_commerce_level}"
# 创建Creator记录
new_creator = Creator.objects.create(
session=today_session,
name=profile_obj.name,
avatar=profile_obj.avatar_url if profile_obj.avatar_url else None,
category=profile_obj.category if profile_obj.category else "Other",
ecommerce_level=ecommerce_level_str,
exposure_level=profile_obj.exposure_level if profile_obj.exposure_level else "New tag",
followers=float(profile_obj.followers) if profile_obj.followers else 0,
gmv=float(profile_obj.gmv) if profile_obj.gmv else 0,
items_sold=float(profile_obj.items_sold) if profile_obj.items_sold else 0,
avg_video_views=float(profile_obj.avg_video_views) if profile_obj.avg_video_views else 0,
has_ecommerce=profile_obj.e_commerce_level is not None,
tiktok_url=profile_obj.tiktok_link if profile_obj.tiktok_link else None,
hashtags=profile_obj.hashtags, # 确保复制hashtags
trends=profile_obj.trends, # 确保复制trends
profile=profile_obj.profile # 复制profile字段
)
matched_creator_ids.append(new_creator.id)
created_count += 1
# 如果没有找到匹配的结果,返回提示
if creator_profiles.count() == 0:
return ApiResponse.error(f"未找到匹配的{mode.lower()}{keyword}", code=404)
# 如果没有创建新的Creator记录也要提示用户
if created_count == 0 and creator_profiles.count() > 0:
logger.info(f"找到了{creator_profiles.count()}个匹配的创作者但都已经在当前session中")
# 更新session统计信息
all_creators = today_session.creators.all()
total_creators = all_creators.count()
if total_creators > 0:
# 计算平均值
total_followers = sum(creator.followers for creator in all_creators)
total_gmv = sum(creator.gmv for creator in all_creators)
total_video_views = sum(creator.avg_video_views for creator in all_creators)
today_session.creator_count = total_creators
today_session.shoppable_creators = all_creators.filter(has_ecommerce=True).count()
today_session.avg_followers = round(total_followers / total_creators, 1)
today_session.avg_gmv = round(total_gmv / total_creators, 1)
today_session.avg_video_views = round(total_video_views / total_creators, 1)
today_session.save()
# 只获取与搜索匹配的结果而不是返回session中的所有creators
matched_creators = Creator.objects.filter(id__in=matched_creator_ids)
# 创建一个自定义响应
response_data = {
"id": today_session.id,
"session_number": today_session.session_number,
"creator_count": matched_creators.count(),
"shoppable_creators": matched_creators.filter(has_ecommerce=True).count(),
"avg_followers": round(sum(c.followers for c in matched_creators) / matched_creators.count(), 1) if matched_creators.count() > 0 else 0,
"avg_gmv": round(sum(c.gmv for c in matched_creators) / matched_creators.count(), 1) if matched_creators.count() > 0 else 0,
"avg_video_views": round(sum(c.avg_video_views for c in matched_creators) / matched_creators.count(), 1) if matched_creators.count() > 0 else 0,
"date_created": today_session.date_created,
"creators": CreatorSerializer(matched_creators, many=True).data
}
return ApiResponse.success(response_data, f"搜索{mode.lower()}成功,关键词: {keyword}")
except Exception as e:
import traceback
logger.error(f"搜索标签时发生错误: {str(e)}")
logger.error(traceback.format_exc())
return ApiResponse.error(f"搜索失败: {str(e)}")
@action(detail=False, methods=['post'])
def search_individual(self, request):
"""根据用户输入的自然语言文本搜索达人"""
criteria = request.data.get('criteria', '')
top_n = request.data.get('top_n', 10)
if not criteria:
return ApiResponse.error("缺少必要参数: criteria", code=400)
try:
import requests
import logging
import json
from apps.daren_detail.models import CreatorProfile
from apps.user.models import User
logger = logging.getLogger(__name__)
# 调用外部API
api_url = 'http://81.69.223.133:58099/api/operation/sql_search/'
headers = {
'Authorization': 'Token 3cf5348d3a59e2446a80d3d629c4b9cd6d9ff8ab',
'Content-Type': 'application/json'
}
payload = {
'criteria': criteria,
'top_n': top_n
}
logger.info(f"调用外部API参数: {payload}")
# 发送请求
response = requests.post(api_url, headers=headers, json=payload)
# 检查请求是否成功
if response.status_code != 200:
logger.error(f"外部API请求失败状态码: {response.status_code}, 响应: {response.text}")
return ApiResponse.error(f"外部API请求失败状态码: {response.status_code}", code=500)
# 解析响应
resp_data = response.json()
logger.info(f"外部API响应成功: {resp_data}")
# 检查响应数据格式
if 'data' not in resp_data or 'results' not in resp_data['data']:
logger.error(f"外部API响应格式错误: {resp_data}")
return ApiResponse.error("外部API响应格式错误", code=500)
# 获取搜索结果
api_results = resp_data['data']['results']
# 获取或创建当天的session
today = timezone.now().date()
today_session = SearchSession.objects.filter(date_created=today).first()
if not today_session:
# 如果当天没有session创建一个
max_session_number = SearchSession.objects.all().order_by('-session_number').first()
session_number = 1
if max_session_number:
session_number = max_session_number.session_number + 1
today_session = SearchSession.objects.create(
session_number=session_number,
creator_count=0,
shoppable_creators=0,
avg_followers=0,
avg_gmv=0,
avg_video_views=0,
date_created=today
)
# 将API结果转换为Creator记录
matched_creator_ids = []
for creator_data in api_results:
try:
# 安全获取创作者名称,确保有一个默认值
creator_name = creator_data.get('name', 'Unknown Creator')
# 按名称找到第一个匹配的CreatorProfile或创建新的
profile_obj = None
try:
profile_obj = CreatorProfile.objects.filter(name=creator_name).first()
except Exception as e:
logger.error(f"查询CreatorProfile时出错: {str(e)}")
if not profile_obj:
# 如果没有找到创建一个新的CreatorProfile
try:
profile_obj = CreatorProfile.objects.create(
name=creator_name,
avatar_url=creator_data.get('avatar_url'),
email=creator_data.get('email'),
instagram=creator_data.get('instagram'),
tiktok_link=creator_data.get('tiktok_link'),
location=creator_data.get('location'),
live_schedule=creator_data.get('live_schedule'),
category=creator_data.get('category'),
e_commerce_level=creator_data.get('e_commerce_level'),
exposure_level=creator_data.get('exposure_level'),
followers=creator_data.get('followers', 0),
gmv=creator_data.get('gmv', 0),
items_sold=creator_data.get('items_sold', 0),
avg_video_views=creator_data.get('avg_video_views', 0),
pricing=creator_data.get('pricing'),
pricing_package=creator_data.get('pricing_package'),
collab_count=creator_data.get('collab_count'),
latest_collab=creator_data.get('latest_collab'),
profile=creator_data.get('profile'),
hashtags=creator_data.get('hashtags', ''),
trends=creator_data.get('trends', '')
)
except Exception as e:
logger.error(f"创建CreatorProfile时出错: {str(e)}")
continue
# 检查是否已经在当前session中存在相同creator
existing_creator = Creator.objects.filter(
session=today_session,
name=profile_obj.name
).first()
if existing_creator:
# 如果已存在记录ID用于过滤
matched_creator_ids.append(existing_creator.id)
else:
# 确定电商等级字符串
ecommerce_level_str = "New tag"
if profile_obj.e_commerce_level is not None:
ecommerce_level_str = f"L{profile_obj.e_commerce_level}"
# 创建Creator记录
new_creator = Creator.objects.create(
session=today_session,
name=profile_obj.name,
avatar=profile_obj.avatar_url if profile_obj.avatar_url else None,
category=profile_obj.category if profile_obj.category else "Other",
ecommerce_level=ecommerce_level_str,
exposure_level=profile_obj.exposure_level if profile_obj.exposure_level else "New tag",
followers=float(profile_obj.followers) if profile_obj.followers else 0,
gmv=float(profile_obj.gmv) if profile_obj.gmv else 0,
items_sold=float(profile_obj.items_sold) if profile_obj.items_sold else 0,
avg_video_views=float(profile_obj.avg_video_views) if profile_obj.avg_video_views else 0,
has_ecommerce=profile_obj.e_commerce_level is not None,
tiktok_url=profile_obj.tiktok_link if profile_obj.tiktok_link else None,
hashtags=profile_obj.hashtags,
trends=profile_obj.trends,
profile=profile_obj.profile
)
matched_creator_ids.append(new_creator.id)
except Exception as e:
logger.error(f"处理创作者数据时发生错误: {str(e)}, 数据: {creator_data}")
continue
# 更新session统计信息
all_creators = today_session.creators.all()
total_creators = all_creators.count()
if total_creators > 0:
# 计算平均值
total_followers = sum(creator.followers for creator in all_creators)
total_gmv = sum(creator.gmv for creator in all_creators)
total_video_views = sum(creator.avg_video_views for creator in all_creators)
today_session.creator_count = total_creators
today_session.shoppable_creators = all_creators.filter(has_ecommerce=True).count()
today_session.avg_followers = round(total_followers / total_creators, 1)
today_session.avg_gmv = round(total_gmv / total_creators, 1)
today_session.avg_video_views = round(total_video_views / total_creators, 1)
today_session.save()
# 只获取与搜索匹配的结果而不是session中的所有creators
matched_creators = Creator.objects.filter(id__in=matched_creator_ids)
# 创建统一的响应格式,与其他搜索接口保持一致
response_data = {
"id": today_session.id,
"session_number": today_session.session_number,
"creator_count": matched_creators.count(),
"shoppable_creators": matched_creators.filter(has_ecommerce=True).count(),
"avg_followers": round(sum(c.followers for c in matched_creators) / matched_creators.count(), 1) if matched_creators.count() > 0 else 0,
"avg_gmv": round(sum(c.gmv for c in matched_creators) / matched_creators.count(), 1) if matched_creators.count() > 0 else 0,
"avg_video_views": round(sum(c.avg_video_views for c in matched_creators) / matched_creators.count(), 1) if matched_creators.count() > 0 else 0,
"date_created": today_session.date_created,
"creators": CreatorSerializer(matched_creators, many=True).data
}
return ApiResponse.success(response_data, f"搜索创作者成功")
except Exception as e:
import traceback
logger.error(f"搜索时发生错误: {str(e)}")
logger.error(traceback.format_exc())
return ApiResponse.error(f"搜索失败: {str(e)}", code=500)