diff --git a/apps/daren_detail/views.py b/apps/daren_detail/views.py index bb047e9..2b02e22 100644 --- a/apps/daren_detail/views.py +++ b/apps/daren_detail/views.py @@ -12,6 +12,7 @@ import shutil import dotenv import random import uuid +from django.db.models import Q dotenv.load_dotenv() @@ -24,729 +25,6 @@ directory_monitoring = {} monitor_thread = None is_monitoring = False -# # 在文件开头定义日志目录 -# BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -# LOG_DIR = os.path.join(BASE_DIR, 'logs', 'process_monitor') - -# 创建保存视频的基本路径 -# TIKTOK_VIDEOS_PATH = os.path.join(BASE_DIR, 'media', 'tiktok_videos') - -# 确保基本目录存在 -# os.makedirs(TIKTOK_VIDEOS_PATH, exist_ok=True) - -# 确保基础目录结构存在,添加 'all' 目录 -# for resource_type in ['cpu', 'memory', 'gpu', 'all']: -# os.makedirs(os.path.join(LOG_DIR, resource_type), exist_ok=True) - -# 全局变量来跟踪监控的目录 -# monitored_directories = set() - -# 在文件顶部添加 API 基础 URL -# API_BASE_URL = os.getenv("API_BASE_URL") - -# -# @csrf_exempt -# @require_http_methods(["POST"]) -# def fetch_tiktok_videos(request): -# """获取TikTok视频""" -# try: -# # 添加全局变量引用 -# global all_downloaded_videos -# -# # 如果变量未初始化,则初始化为空列表 -# if 'all_downloaded_videos' not in globals(): -# all_downloaded_videos = [] -# -# data = json.loads(request.body) -# unique_id = data.get('unique_id') -# -# if not unique_id: -# return JsonResponse({ -# 'status': 'error', -# 'message': '请提供TikTok用户ID(unique_id)' -# }, json_dumps_params={'ensure_ascii': False}) -# -# # 检查数据库中是否已存在该unique_id -# if TiktokUserVideos.objects.filter(unique_id=unique_id).exists(): -# logger.info(f"用户 {unique_id} 已存在于数据库中,跳过处理") -# return JsonResponse({ -# 'status': 'success', -# 'message': f'用户 {unique_id} 已存在,跳过处理' -# }, json_dumps_params={'ensure_ascii': False}) -# -# # 获取creator_oec_id -# creator_oec_id = data.get('creator_oec_id') -# logger.info(f"从请求数据中获取到creator_oec_id: {creator_oec_id}") -# -# # 调用API获取用户资料和secUid -# logger.info(f"正在获取用户 {unique_id} 的资料...") -# # user_profile = fetch_user_profile(unique_id) -# user_profile = None -# if not user_profile or 'data' not in user_profile: -# # 尝试使用备用方法获取用户资料 -# logger.info(f"尝试使用备用方法获取用户 {unique_id} 的资料...") -# -# # 从data中获取creator_oec_id -# try: -# # 从响应中提取creator_oec_id -# creator_oec_id = data.get('creator_oec_id') -# logger.info(f"从请求数据中获取到creator_oec_id: {creator_oec_id}") -# -# # 如果有creator_oec_id,则使用备用方法获取用户资料 -# if creator_oec_id: -# user_profile = fetch_user_profile_1(creator_oec_id) -# -# if not user_profile or 'data' not in user_profile: -# return JsonResponse({ -# 'status': 'error', -# 'message': f'无法获取用户 {unique_id} 的资料(两种方法均失败)' -# }, json_dumps_params={'ensure_ascii': False}) -# else: -# return JsonResponse({ -# 'status': 'error', -# 'message': f'无法获取用户 {unique_id} 的资料' -# }, json_dumps_params={'ensure_ascii': False}) -# except Exception as e: -# logger.error(f"备用方法获取用户资料失败: {e}") -# return JsonResponse({ -# 'status': 'error', -# 'message': f'无法获取用户 {unique_id} 的资料: {str(e)}' -# }, json_dumps_params={'ensure_ascii': False}) -# -# # 从API响应中提取secUid和其他用户信息 -# try: -# user_info = user_profile['data']['userInfo']['user'] -# sec_uid = user_info['secUid'] -# -# # 提取其他用户信息 -# nickname = user_info.get('nickname', f'用户_{unique_id}') -# signature = user_info.get('signature', '') -# avatar_url = user_info.get('avatarLarger', '') -# user_stats = user_profile['data']['userInfo']['stats'] -# follower_count = user_stats.get('followerCount', 0) -# heart_count = user_stats.get('heartCount', 0) -# -# logger.info(f"成功获取用户secUid: {sec_uid}") -# except (KeyError, TypeError) as e: -# logger.error(f"解析用户资料出错: {e}") -# return JsonResponse({ -# 'status': 'error', -# 'message': f'解析用户资料出错: {str(e)}' -# }, json_dumps_params={'ensure_ascii': False}) -# -# # 确保用户目录存在 -# user_dir = os.path.join(TIKTOK_VIDEOS_PATH, unique_id) -# os.makedirs(user_dir, exist_ok=True) -# -# # 获取用户视频 -# downloaded_videos = [] -# videos_data = fetch_user_videos(sec_uid) -# if videos_data and 'data' in videos_data and 'itemList' in videos_data['data']: -# videos = videos_data['data']['itemList'] -# user_dir = os.path.join(TIKTOK_VIDEOS_PATH, unique_id) -# os.makedirs(user_dir, exist_ok=True) -# -# # 获取前10个热门视频 -# top_videos = sorted(videos, key=lambda x: x.get('stats', {}).get('playCount', 0), reverse=True)[:10] -# -# # 定义下载任务函数 -# def download_task(video): -# try: -# video_id = video.get('id', '') -# if not video_id: -# return None -# -# save_path = os.path.join(user_dir, f"{video_id}.mp4") -# -# stats = video.get('stats', {}) -# play_count = int(stats.get('playCount', 0)) -# -# # 检查是否已下载过该视频 -# is_downloaded = os.path.exists(save_path) and os.path.getsize(save_path) > 0 -# -# if is_downloaded and is_valid_video_file(save_path): -# logger.info(f"视频已存在且有效,跳过下载: {video_id}") -# # 即使跳过下载,也添加到已下载列表中 -# return { -# 'id': video_id, -# 'desc': video.get('desc', ''), -# 'play_count': play_count, -# 'user_unique_id': unique_id, -# 'skipped': True -# } -# elif download_video(video_id, unique_id, save_path): -# # download_video函数内部已经检查了文件有效性 -# return { -# 'id': video_id, -# 'desc': video.get('desc', ''), -# 'play_count': play_count, -# 'user_unique_id': unique_id, -# 'skipped': False -# } -# return None -# except Exception as e: -# logger.error(f"下载视频时出错: {e}") -# return None -# -# # 使用线程池并发下载 -# with concurrent.futures.ThreadPoolExecutor(max_workers=4) as executor: -# # 提交所有下载任务 -# future_to_video = {executor.submit(download_task, video): video for video in top_videos} -# -# # 获取结果 -# for future in concurrent.futures.as_completed(future_to_video): -# result = future.result() -# if result: -# downloaded_videos.append(result) -# -# all_downloaded_videos.extend(downloaded_videos) -# -# # 视频下载完成后,先调用API分析视频,然后再保存到数据库 -# if downloaded_videos: # 只有当有视频下载成功时才处理 -# video_info_json = json.dumps([{ -# 'id': v['id'], -# 'desc': v['desc'], -# 'play_count': v['play_count'] -# } for v in downloaded_videos], ensure_ascii=False) -# -# # 先调用视频分析和文档创建API -# document_id = analyze_videos_and_create_document(user_dir, nickname, unique_id, sec_uid) -# -# # 只有在API调用成功后才写入数据库 -# if document_id: -# user_record = TiktokUserVideos.objects.update_or_create( -# sec_user_id=sec_uid, -# defaults={ -# 'unique_id': unique_id, # 添加unique_id字段 -# 'nickname': nickname, -# 'signature': signature, -# 'follower_count': follower_count, -# 'total_favorited': heart_count, -# 'avatar_url': avatar_url, -# 'videos_folder': user_dir, -# 'video_paths': video_info_json, -# 'creator_oec_id': creator_oec_id, -# 'document_id': document_id -# } -# ) -# logger.info( -# f"API调用成功,用户 {nickname} 的数据已写入数据库,下载视频数: {len(downloaded_videos)},document_id: {document_id}") -# else: -# logger.warning(f"API调用失败,跳过用户 {nickname} 的数据库写入") -# else: -# logger.warning(f"未获取到用户 {unique_id} 的视频数据") -# document_id = None -# -# return JsonResponse({ -# 'status': 'success', -# 'message': '处理完成' -# }, json_dumps_params={'ensure_ascii': False}) -# -# except Exception as e: -# logger.error(f"处理TikTok视频失败: {e}") -# import traceback -# logger.error(f"详细错误: {traceback.format_exc()}") -# return JsonResponse({ -# 'status': 'error', -# 'message': f'处理TikTok视频失败: {str(e)}' -# }, json_dumps_params={'ensure_ascii': False}) -# -# -# @require_http_methods(["GET"]) -# def get_tiktok_user_videos(request): -# """获取已下载的TikTok用户视频列表""" -# try: -# sec_user_id = request.GET.get('sec_user_id') -# -# if not sec_user_id: -# # 如果没有指定用户ID,返回所有用户列表 -# users = TiktokUserVideos.objects.all().values('sec_user_id', 'unique_id', 'nickname', 'follower_count', -# 'videos_folder', 'create_time', 'avatar_url', 'signature', -# 'creator_oec_id') -# return JsonResponse({ -# 'status': 'success', -# 'users': list(users) -# }, json_dumps_params={'ensure_ascii': False}) -# -# # 查询指定用户信息 -# try: -# user = TiktokUserVideos.objects.get(sec_user_id=sec_user_id) -# # 解析视频信息JSON -# video_info = json.loads(user.video_paths) if user.video_paths else [] -# -# # 获取文件夹中的文件列表 -# videos_folder = user.videos_folder -# video_files = [] -# if os.path.exists(videos_folder): -# video_files = [f for f in os.listdir(videos_folder) if os.path.isfile(os.path.join(videos_folder, f))] -# -# except TiktokUserVideos.DoesNotExist: -# return JsonResponse({ -# 'status': 'error', -# 'message': f'用户 {sec_user_id} 不存在' -# }, json_dumps_params={'ensure_ascii': False}) -# -# return JsonResponse({ -# 'status': 'success', -# 'user_info': { -# 'sec_user_id': user.sec_user_id, -# 'unique_id': user.unique_id, -# 'nickname': user.nickname, -# 'signature': user.signature, -# 'follower_count': user.follower_count, -# 'total_favorited': user.total_favorited, -# 'avatar_url': user.avatar_url, -# 'creator_oec_id': user.creator_oec_id, # 添加creator_oec_id字段 -# 'create_time': user.create_time.strftime('%Y-%m-%d %H:%M:%S'), -# 'update_time': user.update_time.strftime('%Y-%m-%d %H:%M:%S') -# }, -# 'videos_folder': videos_folder, -# 'video_files': video_files, -# 'video_info': video_info -# }, json_dumps_params={'ensure_ascii': False}) -# -# except Exception as e: -# logger.error(f"获取TikTok视频列表失败: {str(e)}") -# return JsonResponse({ -# 'status': 'error', -# 'message': f'获取TikTok视频列表失败: {str(e)}' -# }, json_dumps_params={'ensure_ascii': False}) -# -# -# # 辅助函数 -# -# def fetch_user_videos(sec_uid, cursor=0, count=30): -# """获取用户视频列表""" -# url = f"{API_BASE_URL}/api/tiktok/web/fetch_user_post?secUid={sec_uid}&cursor={cursor}&count={count}" -# -# try: -# response = requests.get(url, timeout=30) -# -# if response.status_code == 200: -# data = response.json() -# logger.info(f"成功获取用户视频,共 {len(data['data'].get('itemList', []))} 个视频") -# return data -# else: -# logger.error(f"获取用户视频失败: {response.status_code}") -# return None -# except Exception as e: -# logger.error(f"获取用户视频异常: {e}") -# return None -# -# -# def fetch_user_profile(unique_id): -# """获取用户基本信息""" -# url = f"{API_BASE_URL}/api/tiktok/web/fetch_user_profile?uniqueId={unique_id}" -# -# try: -# logger.info(f"正在请求用户资料: {url}") -# response = requests.get(url, timeout=30) -# -# if response.status_code == 200: -# data = response.json() -# logger.info(f"成功获取用户资料: {unique_id}") -# -# # 打印完整响应以便调试 -# logger.info(f"API原始响应: {data}") -# -# # 验证数据完整性 -# if 'data' not in data or not data['data']: -# logger.error(f"API响应缺少data字段: {data}") -# return None -# -# if 'userInfo' not in data['data'] or not data['data']['userInfo']: -# logger.error(f"API响应缺少userInfo字段: {data['data']}") -# return None -# -# if 'user' not in data['data']['userInfo'] or not data['data']['userInfo']['user']: -# logger.error(f"API响应缺少user字段: {data['data']['userInfo']}") -# return None -# -# # 打印用户信息 -# logger.info(f"用户信息: {data['data']['userInfo']['user']}") -# -# return data -# else: -# logger.error(f"获取用户信息失败: HTTP {response.status_code}, 响应: {response.text[:500]}") -# return None -# except Exception as e: -# logger.error(f"获取用户信息异常: {e}") -# return None -# -# -# def get_sec_user_id(unique_id): -# """ -# 通过TikTok用户名获取secUid -# -# Args: -# unique_id: TikTok用户名 -# -# Returns: -# secUid字符串,如果获取失败则返回None -# """ -# url = f"{API_BASE_URL}/api/tiktok/web/get_sec_user_id?url=https://www.tiktok.com/@{unique_id}" -# -# try: -# logger.info(f"正在请求secUid: {url}") -# response = requests.get(url, timeout=30) -# -# if response.status_code == 200: -# data = response.json() -# logger.info(f"成功获取secUid: {data}") -# -# if 'data' in data and data['data']: -# sec_uid = data['data'] -# logger.info(f"获取到secUid: {sec_uid}") -# return sec_uid -# else: -# logger.error(f"API响应缺少data字段或data为空: {data}") -# return None -# else: -# logger.error(f"获取secUid失败: HTTP {response.status_code}, 响应: {response.text[:500]}") -# return None -# except Exception as e: -# logger.error(f"获取secUid异常: {e}") -# return None -# -# -# def fetch_user_profile_1(creator_oec_id): -# """ -# 通过creator_oec_id获取用户基本信息 -# -# 当fetch_user_profile无法获取用户信息时,使用此备用方法 -# 通过POST请求获取用户详细资料 -# -# Args: -# creator_oec_id: 创作者OEC ID -# -# Returns: -# 与fetch_user_profile相同格式的用户信息 -# """ -# url = "http://100.65.206.105:8899/profile" -# -# try: -# logger.info(f"正在通过creator_oec_id请求用户资料: {creator_oec_id}") -# -# # 构建请求体 -# payload = { -# "creator_oec_id": creator_oec_id, -# "profile_types": [1, 6] -# } -# -# # 发送POST请求 -# response = requests.post(url, json=payload, timeout=30) -# -# if response.status_code == 200: -# data = response.json() -# logger.info(f"成功获取用户资料(方法2): {creator_oec_id}") -# -# # 验证数据完整性 -# if 'creator_profile' not in data: -# logger.error(f"API响应缺少creator_profile字段: {data}") -# return None -# -# # 转换为与fetch_user_profile相同的格式 -# creator_profile = data.get('creator_profile', {}) -# -# # 获取uniqueId -# unique_id = creator_profile.get('handle', {}).get('value', '') -# -# # 通过uniqueId获取secUid -# sec_uid = None -# if unique_id: -# sec_uid = get_sec_user_id(unique_id) -# -# # 如果无法获取secUid,使用一个占位符 -# if not sec_uid: -# logger.warning(f"无法获取用户 {unique_id} 的secUid,使用占位符") -# sec_uid = f"placeholder_secuid_{creator_oec_id}" -# -# # 构建与原API相同格式的响应 -# formatted_data = { -# 'data': { -# 'userInfo': { -# 'user': { -# 'secUid': sec_uid, -# 'uniqueId': unique_id, -# 'nickname': creator_profile.get('nickname', {}).get('value', ''), -# 'signature': creator_profile.get('bio', {}).get('value', ''), -# 'avatarLarger': creator_profile.get('avatar', {}).get('value', {}).get('url_list', [''])[0] -# }, -# 'stats': { -# 'followerCount': int(creator_profile.get('follower_cnt', {}).get('value', '0')), -# 'heartCount': 0, # 没有对应字段,设为0 -# 'followingCount': 0, # 没有对应字段,设为0 -# 'videoCount': 0 # 没有对应字段,设为0 -# } -# } -# } -# } -# -# return formatted_data -# else: -# logger.error(f"获取用户信息失败(方法2): HTTP {response.status_code}, 响应: {response.text[:500]}") -# return None -# except Exception as e: -# logger.error(f"获取用户信息异常(方法2): {e}") -# import traceback -# logger.error(f"详细错误: {traceback.format_exc()}") -# return None -# -# -# def is_valid_video_file(file_path): -# """ -# 检查视频文件是否有效 -# -# Args: -# file_path: 视频文件路径 -# -# Returns: -# bool: 如果文件有效返回True,否则返回False -# """ -# try: -# # 检查文件是否存在 -# if not os.path.exists(file_path): -# logger.error(f"视频文件不存在: {file_path}") -# return False -# -# # 检查文件大小是否合理 (大于10KB) -# file_size = os.path.getsize(file_path) -# if file_size < 10 * 1024: # 小于10KB的文件可能是无效的 -# logger.warning(f"视频文件过小,可能无效: {file_path},大小: {file_size}字节") -# return False -# -# # 检查文件是否可读 -# with open(file_path, 'rb') as f: -# # 尝试读取前100字节 -# data = f.read(100) -# if not data: -# logger.warning(f"视频文件内容为空: {file_path}") -# return False -# -# return True -# -# except Exception as e: -# logger.error(f"验证视频文件时出错: {e}") -# return False -# -# -# def download_video(video_id, unique_id, save_path): -# """使用API的直接下载接口下载TikTok视频""" -# # 确保视频ID是纯数字 -# if not str(video_id).isdigit(): -# logger.error(f"无效的视频ID: {video_id},必须是纯数字") -# return False -# -# # 检查文件是否已存在 -# if os.path.exists(save_path): -# file_size = os.path.getsize(save_path) -# if file_size > 0: # 确保文件不是空文件 -# # 进一步验证文件有效性 -# if is_valid_video_file(save_path): -# logger.info(f"视频文件已存在且有效,跳过下载: {save_path},文件大小: {file_size}字节") -# return True -# else: -# logger.warning(f"发现无效视频文件,将重新下载: {save_path}") -# # 删除无效文件 -# os.remove(save_path) -# else: -# logger.warning(f"发现空视频文件,将重新下载: {save_path}") -# # 删除空文件 -# os.remove(save_path) -# -# # 构建标准TikTok视频URL -# tiktok_url = f"https://www.tiktok.com/@{unique_id}/video/{video_id}" -# logger.info(f"构建的TikTok URL: {tiktok_url}") -# -# # 构建完整的API请求URL -# api_url = f"{API_BASE_URL}/api/download" -# full_url = f"{api_url}?url={tiktok_url}&prefix=true&with_watermark=false" -# logger.info(f"完整的API请求URL: {full_url}") -# -# try: -# # 直接使用完整URL发送请求 -# response = requests.get(full_url, stream=True, timeout=60) -# -# # 检查响应状态 -# if response.status_code != 200: -# logger.error( -# f"下载视频失败: {response.status_code} - {response.text[:200] if response.text else '无响应内容'}") -# return False -# -# # 获取内容类型 -# content_type = response.headers.get('Content-Type', '') -# logger.info(f"响应内容类型: {content_type}") -# -# # 保存文件 -# with open(save_path, 'wb') as f: -# for chunk in response.iter_content(chunk_size=8192): -# if chunk: -# f.write(chunk) -# -# file_size = os.path.getsize(save_path) -# logger.info(f"视频已下载到: {save_path},文件大小: {file_size}字节") -# -# # 验证下载的文件是否有效 -# if is_valid_video_file(save_path): -# logger.info(f"视频文件验证通过: {save_path}") -# return True -# else: -# logger.error(f"下载的视频文件无效: {save_path}") -# # 删除无效文件 -# if os.path.exists(save_path): -# os.remove(save_path) -# return False -# -# except Exception as e: -# logger.error(f"下载视频异常: {e}") -# import traceback -# logger.error(f"详细错误: {traceback.format_exc()}") -# return False -# -# -# def analyze_videos_and_create_document(videos_folder, nickname, unique_id, sec_uid): -# """ -# 分析视频目录并创建Yanxi文档 -# -# Args: -# videos_folder: 视频所在目录路径 -# nickname: 用户昵称 -# unique_id: TikTok用户ID -# sec_uid: 用户的安全ID -# -# Returns: -# str: 如果API调用成功返回document_id,否则返回None -# """ -# try: -# # 直接调用analyze_directory函数进行视频分析 -# from app.api.summary.summary import analyze_directory -# -# logger.info(f"开始分析视频目录: {videos_folder}") -# analyze_result = analyze_directory(videos_folder) -# -# if analyze_result: -# logger.info(f"分析成功") -# -# # 提取分析结果中的用户画像和聚合摘要 -# user_persona = analyze_result.get('user_persona', '') -# content_summary = analyze_result.get('content_summary', '') -# -# content = f"{user_persona}\n\n{content_summary}" -# -# # 调用yanxi API创建文档 -# try: -# yanxi_url = "http://localhost:8899/yanxi/api/dataset/231aaa42-fc97-11ef-a8ef-0242ac120006/document" -# yanxi_data = { -# "name": f"{nickname}@{unique_id}", -# "paragraphs": [ -# { -# "content": content, -# "title": sec_uid, -# "is_active": True -# } -# ] -# } -# -# logger.info(f"调用Yanxi API: {yanxi_url}") -# yanxi_response = requests.post( -# yanxi_url, -# json=yanxi_data, -# headers={"Content-Type": "application/json"}, -# timeout=60 -# ) -# -# if yanxi_response.status_code in [200, 201]: -# response_data = yanxi_response.json() -# document_id = response_data.get('data', {}).get('id') -# if document_id: -# logger.info(f"文档创建成功,document_id: {document_id}") -# return document_id -# else: -# logger.error("文档创建成功但未获取到document_id") -# return None -# else: -# logger.error(f"文档创建失败: {yanxi_response.status_code}, {yanxi_response.text}") -# return None -# except Exception as e: -# logger.error(f"调用Yanxi API出错: {e}") -# return None -# else: -# logger.error(f"分析失败") -# return None -# except Exception as e: -# logger.error(f"视频分析出错: {e}") -# return None -# -# -# @csrf_exempt -# @require_http_methods(["DELETE"]) -# def delete_tiktok_user(request): -# """删除TikTok用户及其相关视频和文档""" -# try: -# data = json.loads(request.body) -# sec_uid = data.get('sec_uid') -# -# if not sec_uid: -# return JsonResponse({ -# 'status': 'error', -# 'message': 'invalid params' -# }, json_dumps_params={'ensure_ascii': False}) -# -# try: -# user = TiktokUserVideos.objects.get(sec_user_id=sec_uid) -# except TiktokUserVideos.DoesNotExist: -# return JsonResponse({ -# 'status': 'error', -# 'message': f'用户 {sec_uid} 不存在' -# }, json_dumps_params={'ensure_ascii': False}) -# -# # 获取必要信息 -# videos_folder = user.videos_folder -# document_id = user.document_id -# -# # 1. 删除视频文件夹 -# if videos_folder and os.path.exists(videos_folder): -# try: -# shutil.rmtree(videos_folder) -# logger.info(f"成功删除视频文件夹: {videos_folder}") -# except Exception as e: -# logger.error(f"删除视频文件夹失败: {e}") -# -# # 2. 删除文档 -# if document_id: -# try: -# delete_url = f"http://localhost:8899/yanxi/api/dataset/231aaa42-fc97-11ef-a8ef-0242ac120006/document/{document_id}" -# delete_response = requests.delete( -# delete_url, -# headers={"Content-Type": "application/json"}, -# timeout=30 -# ) -# -# if delete_response.status_code in [200, 204]: -# logger.info(f"成功删除文档: {document_id}") -# else: -# logger.error(f"删除文档失败: {delete_response.status_code}, {delete_response.text}") -# except Exception as e: -# logger.error(f"调用删除文档API出错: {e}") -# -# # 3. 删除数据库记录 -# user.delete() -# logger.info(f"成功删除用户数据: {sec_uid}") -# -# return JsonResponse({ -# 'status': 'success', -# 'message': f'成功删除用户 {sec_uid} 及其相关数据' -# }, json_dumps_params={'ensure_ascii': False}) -# -# except Exception as e: -# logger.error(f"删除用户异常: {e}") -# import traceback -# logger.error(f"详细错误: {traceback.format_exc()}") -# return JsonResponse({ -# 'status': 'error', -# 'message': f'删除用户时发生错误: {str(e)}' -# }, json_dumps_params={'ensure_ascii': False}) - @csrf_exempt @require_http_methods(["POST"]) @@ -765,48 +43,55 @@ def filter_creators(request): # 基础查询 query = CreatorProfile.objects.all() - # Category 单选过滤 + # Category 多选过滤 category = filter_data.get('category') if category and len(category) > 0: - query = query.filter(category=category[0]) + query = query.filter(category__in=category) - # 电商能力等级过滤 (L1-L7),单选 + # 电商能力等级过滤 (L1-L7),多选 e_commerce_level = filter_data.get('e_commerce_level') if e_commerce_level and len(e_commerce_level) > 0: - level_str = e_commerce_level[0] - if level_str.startswith('L'): - level_num = int(level_str[1:]) - query = query.filter(e_commerce_level=level_num) + level_nums = [] + for level_str in e_commerce_level: + if level_str.startswith('L'): + level_nums.append(int(level_str[1:])) + if level_nums: + query = query.filter(e_commerce_level__in=level_nums) - # 曝光等级过滤 (KOL-1, KOL-2, KOC-1等),单选 + # 曝光等级过滤 (KOL-1, KOL-2, KOC-1等),多选 exposure_level = filter_data.get('exposure_level') if exposure_level and len(exposure_level) > 0: - query = query.filter(exposure_level=exposure_level[0]) + query = query.filter(exposure_level__in=exposure_level) - # GMV范围过滤 ($0-$5k, $5k-$25k, $25k-$50k等),单选 + # GMV范围过滤 ($0-$5k, $5k-$25k, $25k-$50k等),多选 gmv_range = filter_data.get('gmv_range') if gmv_range and len(gmv_range) > 0: - gmv_min, gmv_max = 0, float('inf') - gmv_val = gmv_range[0] - if gmv_val == "$0-$5k": - gmv_min, gmv_max = 0, 5 - elif gmv_val == "$5k-$25k": - gmv_min, gmv_max = 5, 25 - elif gmv_val == "$25k-$50k": - gmv_min, gmv_max = 25, 50 - elif gmv_val == "$50k-$150k": - gmv_min, gmv_max = 50, 150 - elif gmv_val == "$150k-$400k": - gmv_min, gmv_max = 150, 400 - elif gmv_val == "$400k-$1500k": - gmv_min, gmv_max = 400, 1500 - elif gmv_val == "$1500k+": - gmv_min = 1500 + gmv_q = Q() + for gmv_val in gmv_range: + gmv_min, gmv_max = 0, float('inf') + if gmv_val == "$0-$5k": + gmv_min, gmv_max = 0, 5 + elif gmv_val == "$5k-$25k": + gmv_min, gmv_max = 5, 25 + elif gmv_val == "$25k-$50k": + gmv_min, gmv_max = 25, 50 + elif gmv_val == "$50k-$150k": + gmv_min, gmv_max = 50, 150 + elif gmv_val == "$150k-$400k": + gmv_min, gmv_max = 150, 400 + elif gmv_val == "$400k-$1500k": + gmv_min, gmv_max = 400, 1500 + elif gmv_val == "$1500k+": + gmv_min, gmv_max = 1500, float('inf') - if gmv_min > 0: - query = query.filter(gmv__gte=gmv_min) - if gmv_max < float('inf'): - query = query.filter(gmv__lte=gmv_max) + range_q = Q() + if gmv_min > 0: + range_q &= Q(gmv__gte=gmv_min) + if gmv_max < float('inf'): + range_q &= Q(gmv__lte=gmv_max) + gmv_q |= range_q + + query = query.filter(gmv_q) # 观看量范围过滤,单选 views_range = filter_data.get('views_range') diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..0fb7d29 Binary files /dev/null and b/requirements.txt differ