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, "获取会话摘要列表成功") @action(detail=False, methods=['get']) def search(self, request): """搜索会话""" keyword = request.query_params.get('keyword', '') if not keyword: return ApiResponse.error("缺少关键字参数", code=400) queryset = self.get_queryset() # 尝试判断关键字是否是日期格式 is_date = False parsed_date = None # 支持多种日期格式 date_formats = [ '%Y-%m-%d', '%Y/%m/%d', '%d-%m-%Y', '%d/%m/%Y', '%m-%d-%Y', '%m/%d/%Y' ] for fmt in date_formats: try: from datetime import datetime parsed_date = datetime.strptime(keyword, fmt).date() is_date = True break except ValueError: continue # 构建查询条件 if is_date: # 如果是完整日期格式,按日期搜索 queryset = queryset.filter(date_created=parsed_date) else: # 检查是否可能是年份 is_year = False year_value = None try: # 尝试将关键字作为年份处理 if len(keyword) == 4 and keyword.isdigit(): year_value = int(keyword) if 1900 <= year_value <= 2100: # 合理的年份范围 is_year = True # 按年份搜索 from django.db.models.functions import Extract queryset = queryset.annotate(year=Extract('date_created', 'year')).filter(year=year_value) except Exception: is_year = False # 如果不是年份,继续其他搜索逻辑 if not is_year: try: # 尝试将关键字转换为整数(用于ID或会话编号) keyword_int = int(keyword) queryset = queryset.filter( Q(id=keyword_int) | Q(session_number=keyword_int) ) except ValueError: # 如果转换失败,则按其他可能的字符串字段搜索 queryset = queryset.filter( Q(id__icontains=keyword) | Q(session_number__icontains=keyword) ) # 额外尝试对日期的部分进行模糊匹配 try: # 如果关键字可能是月份名称或数字 month_names = { '一月': 1, '二月': 2, '三月': 3, '四月': 4, '五月': 5, '六月': 6, '七月': 7, '八月': 8, '九月': 9, '十月': 10, '十一月': 11, '十二月': 12, 'january': 1, 'february': 2, 'march': 3, 'april': 4, 'may': 5, 'june': 6, 'july': 7, 'august': 8, 'september': 9, 'october': 10, 'november': 11, 'december': 12, 'jan': 1, 'feb': 2, 'mar': 3, 'apr': 4, 'jun': 6, 'jul': 7, 'aug': 8, 'sep': 9, 'oct': 10, 'nov': 11, 'dec': 12 } month_value = None # 检查是否是月份名称 lower_keyword = keyword.lower() if lower_keyword in month_names: month_value = month_names[lower_keyword] # 检查是否是数字月份 elif keyword.isdigit() and 1 <= int(keyword) <= 12: month_value = int(keyword) if month_value: # 按月份搜索 from django.db.models.functions import Extract month_queryset = self.get_queryset().annotate(month=Extract('date_created', 'month')).filter(month=month_value) # 合并查询结果 queryset = queryset | month_queryset except Exception: # 忽略任何错误,继续使用之前的查询集 pass # 如果没有找到结果 if not queryset.exists(): return ApiResponse.error(f"未找到匹配关键词 '{keyword}' 的会话", code=404) # 格式化响应数据 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, f"搜索会话成功,关键词: {keyword}") 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 import os from apps.daren_detail.models import CreatorProfile from apps.user.models import User logger = logging.getLogger(__name__) # 清除环境变量中的代理设置 os_environ_copy = os.environ.copy() if 'HTTP_PROXY' in os.environ: del os.environ['HTTP_PROXY'] if 'HTTPS_PROXY' in os.environ: del os.environ['HTTPS_PROXY'] if 'http_proxy' in os.environ: del os.environ['http_proxy'] if 'https_proxy' in os.environ: del os.environ['https_proxy'] logger.info("已清除代理环境变量") # 调用外部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}") # 创建一个没有代理的会话 session = requests.Session() session.proxies.clear() # 发送请求,禁用代理 response = session.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) # 恢复环境变量 os.environ.update(os_environ_copy) # 解析响应 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)