diff --git a/backend/Timer_worker.py b/backend/Timer_worker.py index 3fcebd3..6e1fa88 100644 --- a/backend/Timer_worker.py +++ b/backend/Timer_worker.py @@ -136,25 +136,9 @@ class DouyinAutoScheduler: return len(killed_processes) > 0 except ImportError: - # 如果没有psutil,使用系统命令 - try: - import subprocess - import os - - script_dir = os.path.dirname(os.path.abspath(__file__)) - profile_dir = os.path.join(script_dir, 'config', 'chrome_profile_timer', 'douyin_persistent') - - # 使用taskkill命令终止Chrome进程 - result = subprocess.run(['taskkill', '/F', '/IM', 'chrome.exe'], capture_output=True, text=True) - if result.returncode == 0: - logging.info('使用系统命令终止Chrome进程') - return True - else: - logging.warning('无法终止Chrome进程') - return False - except Exception as e: - logging.warning(f'系统命令清理Chrome进程失败: {e}') - return False + # 如果没有psutil,跳过清理以避免影响其他脚本实例 + logging.warning('psutil 不可用,跳过进程清理(避免全局终止 Chrome)') + return False except Exception as e: logging.warning(f'清理Chrome进程时出错: {e}') return False diff --git a/backend/handlers/Rankings/rank_data_scraper.py b/backend/handlers/Rankings/rank_data_scraper.py index a71d3ef..6cfcde8 100644 --- a/backend/handlers/Rankings/rank_data_scraper.py +++ b/backend/handlers/Rankings/rank_data_scraper.py @@ -851,10 +851,13 @@ class DouyinPlayVVScraper: def _cleanup_chrome_processes(self): """清理可能占用配置文件的Chrome进程""" try: - - # 获取当前配置文件路径 + # 获取当前配置文件路径(按模式隔离) script_dir = os.path.dirname(os.path.abspath(__file__)) - profile_dir = os.path.join(script_dir, 'config', 'chrome_profile_scraper', 'douyin_persistent') + is_timer_mode = os.environ.get('TIMER_MODE') == '1' + if is_timer_mode: + profile_dir = os.path.join(script_dir, 'config', 'chrome_profile_timer', 'douyin_persistent') + else: + profile_dir = os.path.join(script_dir, 'config', 'chrome_profile_scraper', 'douyin_persistent') # 查找使用该配置文件的Chrome进程 killed_processes = [] @@ -874,18 +877,9 @@ class DouyinPlayVVScraper: time.sleep(2) return len(killed_processes) > 0 - except ImportError: - # 如果没有psutil,使用系统命令 - try: - result = subprocess.run(['taskkill', '/f', '/im', 'chrome.exe'], - capture_output=True, text=True, timeout=10) - if result.returncode == 0: - logging.info('使用taskkill清理Chrome进程') - time.sleep(2) - return True - except Exception as e: - logging.warning(f'清理Chrome进程失败: {e}') + # 如果没有psutil,跳过清理以避免影响其他脚本实例 + logging.warning('psutil 不可用,跳过进程清理(避免全局终止 Chrome)') return False except Exception as e: logging.warning(f'清理Chrome进程时出错: {e}') diff --git a/backend/routers/rank_api_routes.py b/backend/routers/rank_api_routes.py index 18f3700..b083615 100644 --- a/backend/routers/rank_api_routes.py +++ b/backend/routers/rank_api_routes.py @@ -68,7 +68,7 @@ def find_management_data(query, target_date=None): Args: query: 查询条件字典,可以包含mix_id, mix_name等字段 - target_date: 目标日期,用于日期过滤 + target_date: 目标日期(已不用于管理库过滤,保留参数兼容) Returns: 查询到的文档或None @@ -78,20 +78,6 @@ def find_management_data(query, target_date=None): if 'mix_id' in query and query['mix_id']: mix_id_query = {"mix_id": query['mix_id']} - # 添加日期过滤(如果提供了target_date) - if target_date: - if isinstance(target_date, str): - target_date = parse_date_string(target_date) - if target_date: - start_of_day = datetime.combine(target_date, datetime.min.time()) - end_of_day = datetime.combine(target_date, datetime.max.time()) - mix_id_query.update({ - "$or": [ - {"created_at": {"$gte": start_of_day, "$lte": end_of_day}}, - {"last_updated": {"$gte": start_of_day, "$lte": end_of_day}} - ] - }) - result = rankings_management_collection.find_one(mix_id_query) if result: logging.info(f"通过mix_id找到管理数据: {query['mix_id']}") @@ -100,20 +86,6 @@ def find_management_data(query, target_date=None): # 如果通过mix_id没找到,或者没有mix_id,尝试其他查询条件 fallback_query = {k: v for k, v in query.items() if k != 'mix_id'} - # 添加日期过滤(如果提供了target_date) - if target_date and fallback_query: - if isinstance(target_date, str): - target_date = parse_date_string(target_date) - if target_date: - start_of_day = datetime.combine(target_date, datetime.min.time()) - end_of_day = datetime.combine(target_date, datetime.max.time()) - fallback_query.update({ - "$or": [ - {"created_at": {"$gte": start_of_day, "$lte": end_of_day}}, - {"last_updated": {"$gte": start_of_day, "$lte": end_of_day}} - ] - }) - if fallback_query: result = rankings_management_collection.find_one(fallback_query) if result: @@ -1294,11 +1266,12 @@ def update_content_classification(): try: data = request.get_json() - # 验证必需参数 - if not data or 'mix_name' not in data or 'classification_type' not in data: - return jsonify({"success": False, "message": "缺少必需参数 mix_name 或 classification_type"}) + # 验证必需参数(支持 mix_id 或 mix_name 任一) + if not data or ('mix_id' not in data and 'mix_name' not in data) or 'classification_type' not in data: + return jsonify({"success": False, "message": "缺少必需参数:需要 mix_id 或 mix_name,以及 classification_type"}) - mix_name = data['mix_name'] + mix_id_param = data.get('mix_id') + mix_name = data.get('mix_name') classification_type = data['classification_type'] # 'novel', 'anime', 'drama' action = data.get('action', 'add') # 'add' 或 'remove' exclusive = data.get('exclusive', True) # 默认启用互斥模式,确保每个短剧只能属于一个分类 @@ -1316,24 +1289,14 @@ def update_content_classification(): } field_name = field_mapping[classification_type] - # 首先从Rankings_management获取短剧的mix_id,使用今天的日期 - today = datetime.now().date() - start_of_day = datetime.combine(today, datetime.min.time()) - end_of_day = datetime.combine(today, datetime.max.time()) - - mgmt_doc = rankings_management_collection.find_one({ - "mix_name": mix_name, - "$or": [ - {"created_at": {"$gte": start_of_day, "$lte": end_of_day}}, - {"last_updated": {"$gte": start_of_day, "$lte": end_of_day}} - ] - }) + # 优先使用 mix_id 获取管理库文档,不做日期过滤 + mgmt_doc = find_management_data({'mix_id': mix_id_param, 'mix_name': mix_name}) if not mgmt_doc: - return jsonify({"success": False, "message": f"未找到短剧: {mix_name}"}) + return jsonify({"success": False, "message": f"未找到短剧:{mix_name or mix_id_param}"}) mix_id = mgmt_doc.get('mix_id') if not mix_id: - return jsonify({"success": False, "message": f"短剧 {mix_name} 缺少 mix_id"}) + return jsonify({"success": False, "message": f"短剧 {mix_name or '[未知名称]'} 缺少 mix_id"}) updated_count = 0 @@ -1350,7 +1313,7 @@ def update_content_classification(): # 1. 从Rankings_management中移除其他分类 for other_field in other_fields: result = rankings_management_collection.update_many( - {"mix_name": mix_name, other_field: mix_id}, + {"mix_id": mix_id, other_field: mix_id}, {"$pull": {other_field: mix_id}} ) if result.modified_count > 0: @@ -1375,7 +1338,7 @@ def update_content_classification(): # 添加到分类字段(使用$addToSet避免重复) # 1. 更新Rankings_management数据库 result_mgmt = rankings_management_collection.update_many( - {"mix_name": mix_name}, + {"mix_id": mix_id}, {"$addToSet": {field_name: mix_id}} ) @@ -1394,7 +1357,7 @@ def update_content_classification(): # 从分类字段中移除 # 1. 更新Rankings_management数据库 result_mgmt = rankings_management_collection.update_many( - {"mix_name": mix_name}, + {"mix_id": mix_id}, {"$pull": {field_name: mix_id}} ) @@ -1412,14 +1375,8 @@ def update_content_classification(): logging.info(f"分类更新: {message}, Rankings_management({result_mgmt.modified_count}), Ranking_storage({result_storage.modified_count})") - # 获取更新后的分类状态,使用今天的日期 - updated_mgmt_doc = rankings_management_collection.find_one({ - "mix_name": mix_name, - "$or": [ - {"created_at": {"$gte": start_of_day, "$lte": end_of_day}}, - {"last_updated": {"$gte": start_of_day, "$lte": end_of_day}} - ] - }) + # 获取更新后的分类状态(按 mix_id 直接查询,不做日期过滤) + updated_mgmt_doc = rankings_management_collection.find_one({"mix_id": mix_id}) classification_status = { 'novel': mix_id in updated_mgmt_doc.get('Novel_IDs', []) if updated_mgmt_doc else False, 'anime': mix_id in updated_mgmt_doc.get('Anime_IDs', []) if updated_mgmt_doc else False, @@ -1449,19 +1406,20 @@ def update_content_classification(): def get_content_classification(): """获取短剧的分类状态""" try: + mix_id_param = request.args.get('mix_id') mix_name = request.args.get('mix_name') - - if not mix_name: - return jsonify({"success": False, "message": "缺少必需参数 mix_name"}) - - # 从Rankings_management获取短剧信息 - mgmt_doc = rankings_management_collection.find_one({"mix_name": mix_name}) + + if not mix_id_param and not mix_name: + return jsonify({"success": False, "message": "缺少必需参数:需要 mix_id 或 mix_name"}) + + # 优先使用 mix_id 获取管理库信息(不做日期过滤) + mgmt_doc = find_management_data({'mix_id': mix_id_param, 'mix_name': mix_name}) if not mgmt_doc: - return jsonify({"success": False, "message": f"未找到短剧: {mix_name}"}) + return jsonify({"success": False, "message": f"未找到短剧:{mix_name or mix_id_param}"}) mix_id = mgmt_doc.get('mix_id') if not mix_id: - return jsonify({"success": False, "message": f"短剧 {mix_name} 缺少 mix_id"}) + return jsonify({"success": False, "message": f"短剧 {mix_name or '[未知名称]'} 缺少 mix_id"}) # 检查短剧在各个分类中的状态 novel_ids = mgmt_doc.get('Novel_IDs', []) @@ -1476,9 +1434,9 @@ def get_content_classification(): return jsonify({ "success": True, - "message": f"获取短剧 {mix_name} 分类状态成功", + "message": f"获取短剧 {mgmt_doc.get('mix_name', mix_name)} 分类状态成功", "data": { - "mix_name": mix_name, + "mix_name": mgmt_doc.get('mix_name', mix_name), "mix_id": mix_id, "classification_status": classification_status, "classification_details": { diff --git a/frontend/src/AdminPanel.vue b/frontend/src/AdminPanel.vue index 2ab9b45..c311807 100644 --- a/frontend/src/AdminPanel.vue +++ b/frontend/src/AdminPanel.vue @@ -13,6 +13,7 @@ const showEditModal = ref(false) // 编辑表单数据 const editForm = reactive({ id: null, + mix_id: '', title: '', mix_name: '', series_author: '', @@ -106,6 +107,7 @@ const fetchRankingData = async () => { // 编辑项目 const editItem = async (item) => { editForm.id = item.id || item._id + editForm.mix_id = item.mix_id || '' editForm.title = item.title || '' editForm.mix_name = item.mix_name || '' editForm.series_author = item.series_author || '' @@ -120,17 +122,17 @@ const editItem = async (item) => { play_vv_change_rate: item.timeline_data?.play_vv_change_rate || 0 } - // 加载分类状态 - await loadClassificationStatus(item.mix_name) + // 加载分类状态(优先使用 mix_id,兼容 mix_name) + await loadClassificationStatus(item.mix_id, item.mix_name) showEditModal.value = true } // 加载分类状态 -const loadClassificationStatus = async (mixName) => { +const loadClassificationStatus = async (mixId, mixName) => { try { const response = await axios.get(`${API_BASE_URL}/rank/get_content_classification`, { - params: { mix_name: mixName } + params: { mix_id: mixId, mix_name: mixName } }) if (response.data.success) { @@ -150,8 +152,8 @@ const loadClassificationStatus = async (mixName) => { // 更新分类 const updateClassification = async (classificationType, isChecked) => { - if (!editForm.mix_name) { - alert('合集名不能为空') + if (!editForm.mix_id && !editForm.mix_name) { + alert('缺少短剧标识(mix_id 或 mix_name)') return } @@ -172,6 +174,7 @@ const updateClassification = async (classificationType, isChecked) => { try { const response = await axios.post(`${API_BASE_URL}/rank/update_content_classification`, { + mix_id: editForm.mix_id, mix_name: editForm.mix_name, classification_type: classificationType, action: isChecked ? 'add' : 'remove', @@ -190,13 +193,13 @@ const updateClassification = async (classificationType, isChecked) => { } else { alert(`分类更新失败: ${response.data.message}`) // 恢复checkbox状态 - await loadClassificationStatus(editForm.mix_name) + await loadClassificationStatus(editForm.mix_id, editForm.mix_name) } } catch (error) { console.error('分类更新失败:', error) alert('分类更新失败,请检查网络连接') // 恢复checkbox状态 - await loadClassificationStatus(editForm.mix_name) + await loadClassificationStatus(editForm.mix_id, editForm.mix_name) } } @@ -237,6 +240,7 @@ const deleteItem = async (item) => { const saveEdit = async () => { try { const updateData = { + mix_id: editForm.mix_id, title: editForm.title, mix_name: editForm.mix_name, series_author: editForm.series_author,