daren/apps/discovery/views.py

804 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.

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
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)