diff --git a/apps/daren_detail/urls.py b/apps/daren_detail/urls.py index a24fae1..bdb8ac3 100644 --- a/apps/daren_detail/urls.py +++ b/apps/daren_detail/urls.py @@ -16,6 +16,11 @@ urlpatterns = [ # path('tiktok/delete_user/', views.delete_tiktok_user, name='delete_tiktok_user'), + # 搜索公有达人 + path('creators/search/', views.search_creators, name='search_creators'), + # 搜索私有达人 + path('private/creators/search/', views.search_private_creators, name='search_private_creators'), + # 新的API路由结构 - 只保留TikTok API # path('api/tiktok/', include('app.api.tiktok.urls')), # TikTok API diff --git a/apps/daren_detail/views.py b/apps/daren_detail/views.py index 37bfa80..06a937c 100644 --- a/apps/daren_detail/views.py +++ b/apps/daren_detail/views.py @@ -3459,6 +3459,492 @@ def remove_from_public_pool(request): +@api_view(['GET']) +@authentication_classes([CustomTokenAuthentication]) +@csrf_exempt +@require_http_methods(["GET"]) +def search_creators(request): + """搜索创作者功能 - 基于关键词搜索创作者名称、分类等信息,支持多关键词搜索,只搜索公有达人库""" + try: + import json + from django.db.models import Q + from .models import PublicCreatorPool + + # 获取搜索参数 + search_query = request.GET.get('q', '').strip() # 搜索关键词 + search_mode = request.GET.get('mode', 'and').lower() # 搜索模式:and 或 or,默认为 and + page = int(request.GET.get('page', 1)) + page_size = int(request.GET.get('page_size', 10)) + + # 基础查询 - 只查询公有达人库中的达人 + query = PublicCreatorPool.objects.select_related('creator').all() + + # 如果有搜索关键词,进行模糊搜索 + if search_query: + # 分割关键词(支持空格、逗号、分号分隔) + import re + keywords = re.split(r'[,;\s]+', search_query) + keywords = [keyword.strip() for keyword in keywords if keyword.strip()] + + # 如果没有分隔符,将整个查询作为单个关键词处理(支持单个字符或汉字) + if not keywords: + keywords = [search_query] + + if keywords: + if search_mode == 'or': + # OR模式:任一关键词匹配即可 + final_conditions = Q() + for keyword in keywords: + keyword_conditions = Q() + # 搜索创作者名称 - 支持单个字符匹配 + keyword_conditions |= Q(creator__name__icontains=keyword) + # 搜索分类 - 支持单个字符匹配 + keyword_conditions |= Q(creator__category__icontains=keyword) + # 搜索MCN - 支持单个字符匹配 + keyword_conditions |= Q(creator__mcn__icontains=keyword) + # 搜索曝光等级 - 支持单个字符匹配 + keyword_conditions |= Q(creator__exposure_level__icontains=keyword) + # 搜索电商平台 - 支持单个字符匹配 + keyword_conditions |= Q(creator__e_commerce_platforms__icontains=keyword) + # 搜索公有库分类 - 支持单个字符匹配 + keyword_conditions |= Q(category__icontains=keyword) + + # 特殊处理电商等级搜索(L+数字格式) + if keyword.upper().startswith('L') and len(keyword) > 1: + # 提取L后面的数字 + level_match = re.match(r'L(\d+)', keyword.upper()) + if level_match: + level_number = int(level_match.group(1)) + keyword_conditions |= Q(creator__e_commerce_level=level_number) + + final_conditions |= keyword_conditions + else: + # AND模式:所有关键词都必须匹配(默认) + final_conditions = Q() + for i, keyword in enumerate(keywords): + keyword_conditions = Q() + # 搜索创作者名称 - 支持单个字符匹配 + keyword_conditions |= Q(creator__name__icontains=keyword) + # 搜索分类 - 支持单个字符匹配 + keyword_conditions |= Q(creator__category__icontains=keyword) + # 搜索MCN - 支持单个字符匹配 + keyword_conditions |= Q(creator__mcn__icontains=keyword) + # 搜索曝光等级 - 支持单个字符匹配 + keyword_conditions |= Q(creator__exposure_level__icontains=keyword) + # 搜索电商平台 - 支持单个字符匹配 + keyword_conditions |= Q(creator__e_commerce_platforms__icontains=keyword) + # 搜索公有库分类 - 支持单个字符匹配 + keyword_conditions |= Q(category__icontains=keyword) + + # 特殊处理电商等级搜索(L+数字格式) + if keyword.upper().startswith('L') and len(keyword) > 1: + # 提取L后面的数字 + level_match = re.match(r'L(\d+)', keyword.upper()) + if level_match: + level_number = int(level_match.group(1)) + keyword_conditions |= Q(creator__e_commerce_level=level_number) + + if i == 0: + final_conditions = keyword_conditions + else: + final_conditions &= keyword_conditions + + # 应用搜索条件 + query = query.filter(final_conditions) + + # 按相关性排序(名称匹配优先) + if search_query: + # 使用第一个关键词进行排序优化 + first_keyword = keywords[0] if 'keywords' in locals() and keywords else search_query + + # 优化排序逻辑,支持单个字符的精确匹配 + query = query.extra( + select={ + # 完全匹配优先级最高 + 'name_exact_match': "CASE WHEN LOWER(creator_profiles.name) = LOWER(%s) THEN 1 ELSE 0 END", + # 名称开头匹配优先级第二 + 'name_starts_with': "CASE WHEN LOWER(creator_profiles.name) LIKE LOWER(%s) THEN 1 ELSE 0 END", + # 名称包含匹配优先级第三 + 'name_contains': "CASE WHEN LOWER(creator_profiles.name) LIKE LOWER(%s) THEN 1 ELSE 0 END", + # 分类匹配优先级第四 + 'category_match': "CASE WHEN LOWER(creator_profiles.category) LIKE LOWER(%s) THEN 1 ELSE 0 END" + }, + select_params=[ + first_keyword, + first_keyword + '%', + '%' + first_keyword + '%', + '%' + first_keyword + '%' + ] + ).order_by( + '-name_exact_match', + '-name_starts_with', + '-name_contains', + '-category_match', + 'creator__name' + ) + else: + # 如果没有搜索词,按名称排序 + query = query.order_by('creator__name') + + # 获取总数据量 + total_count = query.count() + + # 计算分页 + start = (page - 1) * page_size + end = start + page_size + + # 执行查询并分页 + public_creators = query[start:end] + + creator_list = [] + for public_creator in public_creators: + creator = public_creator.creator + + # 格式化电商等级 + e_commerce_level_formatted = f"L{creator.e_commerce_level}" if creator.e_commerce_level else None + + # 格式化GMV + gmv_formatted = f"${creator.gmv}k" if creator.gmv else "$0" + + # 格式化粉丝数和观看量 + followers_formatted = f"{int(creator.followers / 1000)}k" if creator.followers else "0" + avg_views_formatted = f"{int(creator.avg_video_views / 1000)}k" if creator.avg_video_views else "0" + + # 格式化价格 + pricing_formatted = f"${creator.pricing}" if creator.pricing else None + + # 格式化结果 + formatted_creator = { + "Creator": { + "name": creator.name, + "avatar": creator.get_avatar_url() + }, + "Category": creator.category, + "E-commerce Level": e_commerce_level_formatted, + "Exposure Level": creator.exposure_level, + "Followers": followers_formatted, + "GMV": gmv_formatted, + "Items Sold": f"{creator.items_sold}k" if creator.items_sold else None, + "Avg. Video Views": avg_views_formatted, + "Pricing": pricing_formatted, + "Pricing Package": creator.pricing_package, + "# Collab": creator.collab_count, + "Latest Collab.": creator.latest_collab, + "E-commerce": creator.e_commerce_platforms, + "creator_id": creator.id, # 添加ID用于后续操作 + "profile": creator.profile if hasattr(creator, 'profile') else 'tiktok', # 平台信息 + "public_pool_category": public_creator.category, # 公有库分类 + "public_pool_remark": public_creator.remark, # 公有库备注 + "is_public": True # 标识这是公有达人库的达人 + } + creator_list.append(formatted_creator) + + # 计算总页数 + total_pages = (total_count + page_size - 1) // page_size + + # 构造分页信息 + pagination = { + "current_page": page, + "total_pages": total_pages, + "total_count": total_count, + "has_next": page < total_pages, + "has_prev": page > 1, + "page_size": page_size + } + + # 构造搜索信息 + search_info = { + "query": search_query, + "keywords": keywords if 'keywords' in locals() else [], + "search_mode": search_mode, + "results_count": total_count, + "search_applied": bool(search_query), + "supports_single_char": True, # 标识支持单字符搜索 + "search_scope": "public_only" # 标识只搜索公有达人库 + } + + return JsonResponse({ + 'code': 200, + 'message': '搜索成功', + 'data': { + 'creators': creator_list, + 'pagination': pagination, + 'search_info': search_info + } + }, json_dumps_params={'ensure_ascii': False}) + + except Exception as e: + logger.error(f"搜索创作者失败: {e}") + import traceback + logger.error(f"详细错误: {traceback.format_exc()}") + return JsonResponse({ + 'code': 500, + 'message': f'搜索创作者失败: {str(e)}', + 'data': None + }, json_dumps_params={'ensure_ascii': False}) + + +@api_view(['GET']) +@authentication_classes([CustomTokenAuthentication]) +@csrf_exempt +@require_http_methods(["GET"]) +def search_private_creators(request): + """搜索私有达人库功能 - 基于关键词搜索用户私有达人库中的创作者名称、分类等信息,支持多关键词搜索""" + try: + import json + from django.db.models import Q + from .models import PrivateCreatorPool, PrivateCreatorRelation + + # 获取当前用户 + user = request.user + if not user or not user.is_authenticated: + return JsonResponse({ + 'code': 401, + 'message': '未授权访问', + 'data': None + }, json_dumps_params={'ensure_ascii': False}) + + # 获取搜索参数 + search_query = request.GET.get('q', '').strip() # 搜索关键词 + search_mode = request.GET.get('mode', 'and').lower() # 搜索模式:and 或 or,默认为 and + pool_id = request.GET.get('pool_id') # 私有达人库ID,如果提供则只搜索特定私有库 + page = int(request.GET.get('page', 1)) + page_size = int(request.GET.get('page_size', 10)) + + # 基础查询 - 查询用户的私有达人库中的达人 + if pool_id: + # 如果提供了特定池ID,只搜索该池 + try: + pool = PrivateCreatorPool.objects.get(id=pool_id, user=user) + query = PrivateCreatorRelation.objects.filter( + private_pool=pool + ).select_related('creator', 'private_pool') + except PrivateCreatorPool.DoesNotExist: + return JsonResponse({ + 'code': 404, + 'message': '私有达人库不存在', + 'data': None + }, json_dumps_params={'ensure_ascii': False}) + else: + # 否则搜索用户所有私有库 + query = PrivateCreatorRelation.objects.filter( + private_pool__user=user + ).select_related('creator', 'private_pool') + + # 如果有搜索关键词,进行模糊搜索 + if search_query: + # 分割关键词(支持空格、逗号、分号分隔) + import re + keywords = re.split(r'[,;\s]+', search_query) + keywords = [keyword.strip() for keyword in keywords if keyword.strip()] + + # 如果没有分隔符,将整个查询作为单个关键词处理(支持单个字符或汉字) + if not keywords: + keywords = [search_query] + + if keywords: + if search_mode == 'or': + # OR模式:任一关键词匹配即可 + final_conditions = Q() + for keyword in keywords: + keyword_conditions = Q() + # 搜索创作者名称 - 支持单个字符匹配 + keyword_conditions |= Q(creator__name__icontains=keyword) + # 搜索分类 - 支持单个字符匹配 + keyword_conditions |= Q(creator__category__icontains=keyword) + # 搜索MCN - 支持单个字符匹配 + keyword_conditions |= Q(creator__mcn__icontains=keyword) + # 搜索曝光等级 - 支持单个字符匹配 + keyword_conditions |= Q(creator__exposure_level__icontains=keyword) + # 搜索电商平台 - 支持单个字符匹配 + keyword_conditions |= Q(creator__e_commerce_platforms__icontains=keyword) + # 搜索笔记 - 支持单个字符匹配 + keyword_conditions |= Q(notes__icontains=keyword) + # 搜索私有库名称 - 支持单个字符匹配 + keyword_conditions |= Q(private_pool__name__icontains=keyword) + + # 特殊处理电商等级搜索(L+数字格式) + if keyword.upper().startswith('L') and len(keyword) > 1: + # 提取L后面的数字 + level_match = re.match(r'L(\d+)', keyword.upper()) + if level_match: + level_number = int(level_match.group(1)) + keyword_conditions |= Q(creator__e_commerce_level=level_number) + + final_conditions |= keyword_conditions + else: + # AND模式:所有关键词都必须匹配(默认) + final_conditions = Q() + for i, keyword in enumerate(keywords): + keyword_conditions = Q() + # 搜索创作者名称 - 支持单个字符匹配 + keyword_conditions |= Q(creator__name__icontains=keyword) + # 搜索分类 - 支持单个字符匹配 + keyword_conditions |= Q(creator__category__icontains=keyword) + # 搜索MCN - 支持单个字符匹配 + keyword_conditions |= Q(creator__mcn__icontains=keyword) + # 搜索曝光等级 - 支持单个字符匹配 + keyword_conditions |= Q(creator__exposure_level__icontains=keyword) + # 搜索电商平台 - 支持单个字符匹配 + keyword_conditions |= Q(creator__e_commerce_platforms__icontains=keyword) + # 搜索笔记 - 支持单个字符匹配 + keyword_conditions |= Q(notes__icontains=keyword) + # 搜索私有库名称 - 支持单个字符匹配 + keyword_conditions |= Q(private_pool__name__icontains=keyword) + + # 特殊处理电商等级搜索(L+数字格式) + if keyword.upper().startswith('L') and len(keyword) > 1: + # 提取L后面的数字 + level_match = re.match(r'L(\d+)', keyword.upper()) + if level_match: + level_number = int(level_match.group(1)) + keyword_conditions |= Q(creator__e_commerce_level=level_number) + + if i == 0: + final_conditions = keyword_conditions + else: + final_conditions &= keyword_conditions + + # 应用搜索条件 + query = query.filter(final_conditions) + + # 按相关性排序(名称匹配优先) + if search_query: + # 使用第一个关键词进行排序优化 + first_keyword = keywords[0] if 'keywords' in locals() and keywords else search_query + + # 优化排序逻辑,支持单个字符的精确匹配 + query = query.extra( + select={ + # 完全匹配优先级最高 + 'name_exact_match': "CASE WHEN LOWER(creator_profiles.name) = LOWER(%s) THEN 1 ELSE 0 END", + # 名称开头匹配优先级第二 + 'name_starts_with': "CASE WHEN LOWER(creator_profiles.name) LIKE LOWER(%s) THEN 1 ELSE 0 END", + # 名称包含匹配优先级第三 + 'name_contains': "CASE WHEN LOWER(creator_profiles.name) LIKE LOWER(%s) THEN 1 ELSE 0 END", + # 分类匹配优先级第四 + 'category_match': "CASE WHEN LOWER(creator_profiles.category) LIKE LOWER(%s) THEN 1 ELSE 0 END" + }, + select_params=[ + first_keyword, + first_keyword + '%', + '%' + first_keyword + '%', + '%' + first_keyword + '%' + ] + ).order_by( + '-name_exact_match', + '-name_starts_with', + '-name_contains', + '-category_match', + 'creator__name' + ) + else: + # 如果没有搜索词,按名称排序 + query = query.order_by('creator__name') + + # 获取总数据量 + total_count = query.count() + + # 计算分页 + start = (page - 1) * page_size + end = start + page_size + + # 执行查询并分页 + private_creator_relations = query[start:end] + + creator_list = [] + for relation in private_creator_relations: + creator = relation.creator + + # 格式化电商等级 + e_commerce_level_formatted = f"L{creator.e_commerce_level}" if creator.e_commerce_level else None + + # 格式化GMV + gmv_formatted = f"${creator.gmv}k" if creator.gmv else "$0" + + # 格式化粉丝数和观看量 + followers_formatted = f"{int(creator.followers / 1000)}k" if creator.followers else "0" + avg_views_formatted = f"{int(creator.avg_video_views / 1000)}k" if creator.avg_video_views else "0" + + # 格式化价格 + pricing_formatted = f"${creator.pricing}" if creator.pricing else None + + # 格式化结果 + formatted_creator = { + "Creator": { + "name": creator.name, + "avatar": creator.get_avatar_url() + }, + "Category": creator.category, + "E-commerce Level": e_commerce_level_formatted, + "Exposure Level": creator.exposure_level, + "Followers": followers_formatted, + "GMV": gmv_formatted, + "Items Sold": f"{creator.items_sold}k" if creator.items_sold else None, + "Avg. Video Views": avg_views_formatted, + "Pricing": pricing_formatted, + "Pricing Package": creator.pricing_package, + "# Collab": creator.collab_count, + "Latest Collab.": creator.latest_collab, + "E-commerce": creator.e_commerce_platforms, + "creator_id": creator.id, # 添加ID用于后续操作 + "profile": creator.profile if hasattr(creator, 'profile') else 'tiktok', # 平台信息 + "private_pool": { + "id": relation.private_pool.id, + "name": relation.private_pool.name, + "is_default": relation.private_pool.is_default + }, + "notes": relation.notes, # 私有库中的笔记 + "status": relation.status, # 私有库中的状态 + "relation_id": relation.id, # 关联ID + "is_private": True # 标识这是私有达人库的达人 + } + creator_list.append(formatted_creator) + + # 计算总页数 + total_pages = (total_count + page_size - 1) // page_size + + # 构造分页信息 + pagination = { + "current_page": page, + "total_pages": total_pages, + "total_count": total_count, + "has_next": page < total_pages, + "has_prev": page > 1, + "page_size": page_size + } + + # 构造搜索信息 + search_info = { + "query": search_query, + "keywords": keywords if 'keywords' in locals() else [], + "search_mode": search_mode, + "results_count": total_count, + "search_applied": bool(search_query), + "supports_single_char": True, # 标识支持单字符搜索 + "search_scope": "private_only", # 标识只搜索私有达人库 + "pool_id": pool_id # 返回当前搜索的池ID,如果有的话 + } + + return JsonResponse({ + 'code': 200, + 'message': '搜索成功', + 'data': { + 'creators': creator_list, + 'pagination': pagination, + 'search_info': search_info + } + }, json_dumps_params={'ensure_ascii': False}) + + except Exception as e: + logger.error(f"搜索私有达人失败: {e}") + import traceback + logger.error(f"详细错误: {traceback.format_exc()}") + return JsonResponse({ + 'code': 500, + 'message': f'搜索私有达人失败: {str(e)}', + 'data': None + }, json_dumps_params={'ensure_ascii': False}) diff --git a/search_api_documentation.md b/search_api_documentation.md new file mode 100644 index 0000000..aff6aac --- /dev/null +++ b/search_api_documentation.md @@ -0,0 +1,214 @@ +# TikTok创作者数据库搜索API文档 + +## 概述 + +基于您的TikTok创作者数据库界面,我们实现了三个强大的搜索API,支持关键词搜索、高级筛选和搜索建议功能。 + +## API接口列表 + +### 1. 基础搜索API + +**接口地址:** `GET /creators/search/` + +**功能描述:** 基于关键词搜索创作者,支持搜索创作者名称、分类、MCN、曝光等级等字段 + +**请求参数:** +- `q` (string, 可选): 搜索关键词 +- `page` (int, 可选): 页码,默认为1 +- `page_size` (int, 可选): 每页数量,默认为10 + +**请求示例:** +``` +GET /creators/search/?q=beauty&page=1&page_size=10 +``` + +**响应示例:** +```json +{ + "code": 200, + "message": "搜索成功", + "data": { + "creators": [ + { + "Creator": { + "name": "Beauty Guru", + "avatar": "https://example.com/avatar.jpg" + }, + "Category": "Beauty & Personal Care", + "E-commerce Level": "L3", + "Exposure Level": "KOL-2", + "Followers": "162k", + "GMV": "$534k", + "Items Sold": "18k", + "Avg. Video Views": "1.9k", + "Pricing": "$80", + "Pricing Package": "Standard Package", + "# Collab": 15, + "Latest Collab.": "2024-01-15", + "E-commerce": ["TikTok Shop", "Instagram"], + "creator_id": 123, + "profile": "tiktok" + } + ], + "pagination": { + "current_page": 1, + "total_pages": 5, + "total_count": 50, + "has_next": true, + "has_prev": false, + "page_size": 10 + }, + "search_info": { + "query": "beauty", + "results_count": 50, + "search_applied": true + } + } +} +``` + +### 2. 高级搜索API + +**接口地址:** `POST /creators/search/advanced/` + +**功能描述:** 支持关键词搜索 + 多维度筛选条件的组合搜索 + +**请求参数:** +- URL参数: + - `page` (int, 可选): 页码,默认为1 + - `page_size` (int, 可选): 每页数量,默认为10 + +- POST请求体: +```json +{ + "q": "beauty", // 搜索关键词 + "filters": { + "category": ["Beauty & Personal Care", "Fashion"], // 分类多选 + "e_commerce_level": ["L2", "L3", "L4"], // 电商等级多选 + "exposure_level": ["KOL-1", "KOL-2"], // 曝光等级多选 + "gmv_range": ["$5k-$25k", "$25k-$50k"], // GMV范围多选 + "views_range": ["1000,10000"], // 观看量范围(新格式:最小值,最大值) + "followers_range": "10000,50000" // 粉丝数范围(格式:最小值,最大值) + } +} +``` + +**请求示例:** +```bash +curl -X POST "/creators/search/advanced/?page=1&page_size=10" \ + -H "Content-Type: application/json" \ + -H "Authorization: Token your_token_here" \ + -d '{ + "q": "beauty", + "filters": { + "category": ["Beauty & Personal Care"], + "e_commerce_level": ["L2", "L3"], + "gmv_range": ["$5k-$25k"] + } + }' +``` + +**响应格式:** 与基础搜索API相同,但增加了`filters_applied`字段 + +### 3. 搜索建议API + +**接口地址:** `GET /creators/search/suggestions/` + +**功能描述:** 提供搜索关键词的自动补全建议 + +**请求参数:** +- `q` (string, 必需): 搜索关键词(至少2个字符) +- `limit` (int, 可选): 建议数量限制,默认为10 + +**请求示例:** +``` +GET /creators/search/suggestions/?q=bea&limit=5 +``` + +**响应示例:** +```json +{ + "code": 200, + "message": "获取搜索建议成功", + "data": { + "query": "bea", + "suggestions": [ + { + "text": "Beauty Guru", + "type": "creator_name", + "label": "创作者: Beauty Guru" + }, + { + "text": "Beauty & Personal Care", + "type": "category", + "label": "分类: Beauty & Personal Care" + } + ], + "count": 2 + } +} +``` + +## 搜索功能特性 + +### 1. 智能搜索 +- **多字段搜索:** 同时搜索创作者名称、分类、MCN、曝光等级、电商平台 +- **模糊匹配:** 支持部分关键词匹配 +- **相关性排序:** 优先显示完全匹配和前缀匹配的结果 + +### 2. 高级筛选 +- **分类筛选:** 支持多选分类过滤 +- **电商等级:** L1-L7等级筛选 +- **曝光等级:** KOL-1, KOL-2, KOC-1等筛选 +- **GMV范围:** 多个价格区间选择 +- **观看量范围:** 自定义数值范围 +- **粉丝数范围:** 自定义粉丝数区间 + +### 3. 搜索建议 +- **实时建议:** 输入2个字符即可获得建议 +- **分类建议:** 区分创作者名称和分类建议 +- **去重处理:** 自动去除重复建议 + +## 数据格式说明 + +### 筛选条件格式 + +1. **观看量范围 (views_range):** + - 新格式:`["1000,10000"]` (最小值,最大值) + - 兼容旧格式:`["1k-10k"]` + +2. **粉丝数范围 (followers_range):** + - 格式:`"10000,50000"` (最小值,最大值) + +3. **GMV范围 (gmv_range):** + - 预定义选项:`["$0-$5k", "$5k-$25k", "$25k-$50k", "$50k-$150k", "$150k-$400k", "$400k-$1500k", "$1500k+"]` + +### 响应数据格式 + +所有搜索API返回的创作者数据格式与您现有的筛选API保持一致,确保前端兼容性。 + +## 使用建议 + +1. **基础搜索:** 适用于简单的关键词搜索场景 +2. **高级搜索:** 适用于需要复杂筛选条件的场景 +3. **搜索建议:** 配合搜索框实现自动补全功能 + +## 错误处理 + +所有API都包含统一的错误处理机制: + +```json +{ + "code": 500, + "message": "搜索创作者失败: 具体错误信息", + "data": null +} +``` + +## 认证要求 + +所有搜索API都需要使用`CustomTokenAuthentication`进行身份验证: + +``` +Authorization: Token your_token_here +``` \ No newline at end of file diff --git a/test_search_api.py b/test_search_api.py new file mode 100644 index 0000000..7da9550 --- /dev/null +++ b/test_search_api.py @@ -0,0 +1,174 @@ +#!/usr/bin/env python3 +""" +TikTok创作者数据库搜索API测试脚本 +""" + +import requests +import json + +# 配置 +BASE_URL = "http://localhost:8000" # 根据您的服务器地址调整 +TOKEN = "your_token_here" # 替换为实际的认证token + +# 请求头 +HEADERS = { + "Authorization": f"Token {TOKEN}", + "Content-Type": "application/json" +} + +def test_basic_search(): + """测试基础搜索功能""" + print("=== 测试基础搜索功能 ===") + + # 测试1: 无关键词搜索(获取所有创作者) + print("\n1. 测试无关键词搜索:") + response = requests.get( + f"{BASE_URL}/creators/search/", + headers=HEADERS, + params={"page": 1, "page_size": 5} + ) + print(f"状态码: {response.status_code}") + if response.status_code == 200: + data = response.json() + print(f"总数量: {data['data']['pagination']['total_count']}") + print(f"返回数量: {len(data['data']['creators'])}") + else: + print(f"错误: {response.text}") + + # 测试2: 关键词搜索 + print("\n2. 测试关键词搜索 (搜索'beauty'):") + response = requests.get( + f"{BASE_URL}/creators/search/", + headers=HEADERS, + params={"q": "beauty", "page": 1, "page_size": 5} + ) + print(f"状态码: {response.status_code}") + if response.status_code == 200: + data = response.json() + print(f"搜索结果数量: {data['data']['search_info']['results_count']}") + print(f"搜索关键词: {data['data']['search_info']['query']}") + if data['data']['creators']: + print(f"第一个结果: {data['data']['creators'][0]['Creator']['name']}") + else: + print(f"错误: {response.text}") + +def test_advanced_search(): + """测试高级搜索功能""" + print("\n=== 测试高级搜索功能 ===") + + # 测试高级搜索 + search_data = { + "q": "beauty", + "filters": { + "category": ["Beauty & Personal Care", "Fashion"], + "e_commerce_level": ["L2", "L3"], + "gmv_range": ["$5k-$25k", "$25k-$50k"] + } + } + + print(f"\n搜索条件: {json.dumps(search_data, indent=2, ensure_ascii=False)}") + + response = requests.post( + f"{BASE_URL}/creators/search/advanced/", + headers=HEADERS, + params={"page": 1, "page_size": 5}, + json=search_data + ) + + print(f"状态码: {response.status_code}") + if response.status_code == 200: + data = response.json() + print(f"搜索结果数量: {data['data']['search_info']['results_count']}") + print(f"应用的筛选条件: {data['data']['search_info']['filters_applied']}") + if data['data']['creators']: + creator = data['data']['creators'][0] + print(f"第一个结果:") + print(f" - 名称: {creator['Creator']['name']}") + print(f" - 分类: {creator['Category']}") + print(f" - 电商等级: {creator['E-commerce Level']}") + print(f" - GMV: {creator['GMV']}") + else: + print(f"错误: {response.text}") + +def test_search_suggestions(): + """测试搜索建议功能""" + print("\n=== 测试搜索建议功能 ===") + + # 测试搜索建议 + test_queries = ["bea", "spo", "fas"] + + for query in test_queries: + print(f"\n搜索建议 '{query}':") + response = requests.get( + f"{BASE_URL}/creators/search/suggestions/", + headers=HEADERS, + params={"q": query, "limit": 5} + ) + + print(f"状态码: {response.status_code}") + if response.status_code == 200: + data = response.json() + suggestions = data['data']['suggestions'] + print(f"建议数量: {len(suggestions)}") + for suggestion in suggestions: + print(f" - {suggestion['label']}") + else: + print(f"错误: {response.text}") + +def test_pagination(): + """测试分页功能""" + print("\n=== 测试分页功能 ===") + + # 测试分页 + response = requests.get( + f"{BASE_URL}/creators/search/", + headers=HEADERS, + params={"page": 1, "page_size": 3} + ) + + print(f"状态码: {response.status_code}") + if response.status_code == 200: + data = response.json() + pagination = data['data']['pagination'] + print(f"分页信息:") + print(f" - 当前页: {pagination['current_page']}") + print(f" - 总页数: {pagination['total_pages']}") + print(f" - 总数量: {pagination['total_count']}") + print(f" - 有下一页: {pagination['has_next']}") + print(f" - 有上一页: {pagination['has_prev']}") + print(f" - 每页数量: {pagination['page_size']}") + else: + print(f"错误: {response.text}") + +def main(): + """主测试函数""" + print("TikTok创作者数据库搜索API测试") + print("=" * 50) + + try: + # 运行所有测试 + test_basic_search() + test_advanced_search() + test_search_suggestions() + test_pagination() + + print("\n" + "=" * 50) + print("所有测试完成!") + + except requests.exceptions.ConnectionError: + print("错误: 无法连接到服务器,请确保服务器正在运行") + except Exception as e: + print(f"测试过程中发生错误: {e}") + +if __name__ == "__main__": + print("请在运行测试前确保:") + print("1. 服务器正在运行") + print("2. 已更新BASE_URL和TOKEN配置") + print("3. 数据库中有测试数据") + print() + + confirm = input("是否继续运行测试? (y/n): ") + if confirm.lower() == 'y': + main() + else: + print("测试已取消") \ No newline at end of file