diff --git a/app/amazon/__pycache__/approve.cpython-39.pyc b/app/amazon/__pycache__/approve.cpython-39.pyc index fb67c01..694a2d5 100644 Binary files a/app/amazon/__pycache__/approve.cpython-39.pyc and b/app/amazon/__pycache__/approve.cpython-39.pyc differ diff --git a/app/amazon/__pycache__/del_brand.cpython-39.pyc b/app/amazon/__pycache__/del_brand.cpython-39.pyc index 7f3c49b..1e620db 100644 Binary files a/app/amazon/__pycache__/del_brand.cpython-39.pyc and b/app/amazon/__pycache__/del_brand.cpython-39.pyc differ diff --git a/app/amazon/__pycache__/main.cpython-39.pyc b/app/amazon/__pycache__/main.cpython-39.pyc index 7e85b18..a10a71e 100644 Binary files a/app/amazon/__pycache__/main.cpython-39.pyc and b/app/amazon/__pycache__/main.cpython-39.pyc differ diff --git a/app/amazon/__pycache__/match_action.cpython-39.pyc b/app/amazon/__pycache__/match_action.cpython-39.pyc index a9709a9..cfa3167 100644 Binary files a/app/amazon/__pycache__/match_action.cpython-39.pyc and b/app/amazon/__pycache__/match_action.cpython-39.pyc differ diff --git a/app/amazon/__pycache__/tool.cpython-39.pyc b/app/amazon/__pycache__/tool.cpython-39.pyc index c74884a..08dd859 100644 Binary files a/app/amazon/__pycache__/tool.cpython-39.pyc and b/app/amazon/__pycache__/tool.cpython-39.pyc differ diff --git a/app/amazon/approve.py b/app/amazon/approve.py index 7615a80..5577327 100644 --- a/app/amazon/approve.py +++ b/app/amazon/approve.py @@ -7,6 +7,8 @@ from config import runing_task, runing_shop from datetime import datetime from amazon.del_brand import AmamzonBase, kill_process +from amazon.tool import get_shop_info,show_notification + # 导入 webview 用于前端通知 try: import webview @@ -92,17 +94,18 @@ class AmzoneApprove(AmamzonBase): print("等待加载中消失出错", e) def clear_tab(self): - all_tab = self.browser.get_tabs(title="亚马逊") - close_tab = [] - for tab in all_tab: - tab_id = tab if isinstance(tab,str) else tab.tab_id - if self.tab.tab_id == tab: - continue - close_tab.append(tab) - print("需要关闭的标签页",close_tab) - print("当前操作的tab_id",self.tab.tab_id) - self.browser.close_tabs(close_tab) - + for i in ["亚马逊","您的账户是否符合条件?"]: + all_tab = self.browser.get_tabs(title=i) + close_tab = [] + for tab in all_tab: + tab_id = tab if isinstance(tab,str) else tab.tab_id + if self.tab.tab_id == tab_id: + continue + close_tab.append(tab) + print("需要关闭的标签页",close_tab) + print("当前操作的tab_id",self.tab.tab_id) + self.browser.close_tabs(close_tab) + def handle_repair_product(self,tab:ChromiumTab): def wait_loaded(): # 等待加载完成 @@ -118,7 +121,7 @@ class AmzoneApprove(AmamzonBase): submit_btn_ls = tab.eles('xpath://div[@id="ahd-product-policies-table"]/div//div[@data-testid="nextStepsMetricWrapper"]//kat-button[@label="提交"]',timeout=10) if len(submit_btn_ls) == 0: print("未找到提交按钮,可能页面未加载完成或者页面结构发生变化") - return + # return for _ in range(3): try: # todo 遍历 @@ -265,6 +268,7 @@ class AmzoneApprove(AmamzonBase): action_panel.wait.displayed(timeout=5, raise_err=False) self.wait_loaded() + time.sleep(1) try: # 如果存在“请求批准”按钮,则直接返回跳过 @@ -342,7 +346,7 @@ class AmzoneApprove(AmamzonBase): kat_box_ls = action_panel.eles('xpath:.//kat-box[@variant="white"]', timeout=5) # 修复商品信息 - repair_product = action_panel.eles('xpath:.//kat-button[@class="action-button"]',timeout=5) + repair_product = action_panel.eles('xpath:.//kat-button[@class="action-button" and not(@variant="secondary")]',timeout=5) if len(repair_product) > 0: print(f"ASIN {asin} 存在修复商品信息按钮,正在点击...") self.clear_tab() @@ -556,11 +560,9 @@ class ApproveTask: if not task_id: self.log("任务ID为空,跳过", "WARNING") return - if not items: self.log("店铺列表为空,跳过", "WARNING") return - if not country_codes: self.log("国家列表为空,跳过", "WARNING") return @@ -673,16 +675,28 @@ class ApproveTask: if browser and browser != "店铺不存在": self.log(f"成功打开店铺 {shop_name}") - break + # break else: self.log(f"打开店铺失败: {browser}", "WARNING") driver = None + continue # 判断是否需要登录 need_login = driver.need_login() + print("【是否需要登录】:",need_login) if need_login: self.log(f"店铺 {shop_name} 需要登录,正在登录...") - password = "" + # 获取店铺凭证 + response = get_shop_info(shop_name) + print("【获取店铺凭证返回】:",response.text) + shop_data = response.json() + if not shop_data: + mes = f"获取店铺凭证失败,响应数据: {shop_data.get('message', '未知错误')}" + self.log(mes, "ERROR") + show_notification(mes, "ERROR") + continue + + password = shop_data["data"]["password"] login_success = driver.login(password) if login_success: @@ -697,7 +711,8 @@ class ApproveTask: else: self.log(f"店铺 {shop_name} 登录失败", "WARNING") driver = None - + else: + break except Exception as e: import traceback self.log(f"打开店铺异常: {traceback.format_exc()}", "INFO") diff --git a/app/amazon/del_brand.py b/app/amazon/del_brand.py index d0f60f2..4c92467 100644 --- a/app/amazon/del_brand.py +++ b/app/amazon/del_brand.py @@ -489,7 +489,9 @@ class AmamzonBase(ZiniaoDriver): 处理流程: """ - need_login_ele = self.tab.eles('xpath://h1[@class="a-spacing-small"]',timeout=5) + time.sleep(3) # 等待页面可能的登录元素加载,避免跳转等等 + self.tab.wait.doc_loaded(timeout=30, raise_err=False) + need_login_ele = self.tab.eles('xpath://h1[@class="a-spacing-small"]|//span[contains(text(),"登录")]',timeout=5) if len(need_login_ele) >0: print("检测到需要登录元素") return True @@ -497,13 +499,15 @@ class AmamzonBase(ZiniaoDriver): def login(self,password,username=""): try: - pwd_input = self.tab.ele('xpath://input[@type="password"]',timeout=10) - pwd_input.input(password) - submit_btn = self.tab.ele('xpath://input[@id="signInSubmit"]',timeout=10) - submit_btn.click() + pwd_input = self.tab.eles('xpath://input[@type="password"]',timeout=10) + if len(pwd_input) > 0: + pwd_input[0].input(password) + submit_btn = self.tab.eles('xpath://input[@id="signInSubmit"]',timeout=10) + if len(submit_btn) > 0: + submit_btn[0].click() self.tab.wait.doc_loaded() - opt_code_input = self.tab.ele('xpath://input[@name="otpCode"]',timeout=10) + opt_code_input = self.tab.ele('xpath://input[@name="otpCode"]',timeout=60) opt_code_input.wait.displayed(timeout=10,raise_err=False) for _ in range(30): if opt_code_input.value is not None and opt_code_input.value.strip() != "": @@ -512,6 +516,9 @@ class AmamzonBase(ZiniaoDriver): submit_btn.click() self.tab.wait.doc_loaded() return True + time.sleep(1) + submit_btn = self.tab.ele('xpath://input[@id="auth-signin-button"]',timeout=10) + submit_btn.click() except Exception as e: print("登录过程中发生异常:" + traceback.format_exc()) return False diff --git a/app/amazon/main.py b/app/amazon/main.py index 1f7d6f7..0e072bc 100644 --- a/app/amazon/main.py +++ b/app/amazon/main.py @@ -10,6 +10,9 @@ from amazon.approve import ApproveTask from amazon.match_action import MatchTak +from amazon.tool import get_shop_info,show_notification + + class TaskMonitor: """任务监控器:负责监控队列并执行品牌删除任务""" @@ -64,13 +67,13 @@ class TaskMonitor: if task_type == "delete-brand-run": # 提交删除品牌任务到线程池 - self.log(f"接收到删除品牌任务,提交到线程池处理...") + self.log(f"接收到【删除品牌】任务,提交到线程池处理...") future = self.executor.submit(self._process_task_wrapper, task_data) futures.append(future) elif task_type in task_type_info: # 提交产品风险审批任务到线程池 - self.log(f"接收到产品风险审批任务,提交到线程池处理...") + self.log(f"接收到任务,提交到线程池处理...") future = self.executor.submit(self._process_approve_task_wrapper, task_data, task_type_info[task_type]) futures.append(future) @@ -242,10 +245,22 @@ class TaskMonitor: driver = None # 判断是否需要登录 + driver.tab.wait.doc_loaded(timeout=30, raise_err=False) # 等待页面加载,避免过早判断登录状态 need_login = driver.need_login() + print("【是否需要登录】:",need_login) if need_login: self.log(f"店铺 {shop_name} 需要登录,正在登录...") - password = "" + # 获取店铺凭证 + response = get_shop_info(shop_name) + print("【获取店铺凭证返回】:",response.text) + shop_data = response.json() + if not shop_data: + mes = f"获取店铺凭证失败,响应数据: {shop_data.get('message', '未知错误')}" + self.log(mes, "ERROR") + show_notification(mes, "ERROR") + continue + + password = shop_data["data"]["password"] login_success = driver.login(password) if login_success: @@ -611,6 +626,7 @@ class TaskMonitor: verify=False # 忽略SSL证书验证 ) print("【结果提交】:",payload) + print("【结果提交返回】:",response.text) if response.status_code == 200: self.log(f"结果回传成功: {url}") diff --git a/app/amazon/match_action.py b/app/amazon/match_action.py index e0ad574..1841aa8 100644 --- a/app/amazon/match_action.py +++ b/app/amazon/match_action.py @@ -7,7 +7,7 @@ from config import runing_task, runing_shop from datetime import datetime from amazon.del_brand import AmamzonBase, kill_process -from amazon.tool import show_notification +from amazon.tool import show_notification,get_shop_info class AmzoneMatchAction(AmamzonBase): @@ -273,6 +273,8 @@ class MatchTak: try: self.process_shop(shop_item, country_codes, task_id, risk_listing_filter, user_id, stage_index, final_stage) + + # self.process_shop(shop_item, country_codes, task_id,risk_listing_filter) # 更新已处理店铺数 if task_id in runing_task: runing_task[task_id]["processed_shops"] += 1 @@ -299,6 +301,7 @@ class MatchTak: runing_task[task_id]["status"] = "failed" runing_task[task_id]["error"] = str(e) + # def process_shop(self, shop_item: dict, country_codes: list, task_id: int, risk_listing_filter: str): def process_shop(self, shop_item: dict, country_codes: list, task_id: int, risk_listing_filter: str, user_id=None, stage_index=None, final_stage: bool = True): """处理单个店铺 @@ -350,16 +353,27 @@ class MatchTak: if browser and browser != "店铺不存在": self.log(f"成功打开店铺 {shop_name}") - break else: self.log(f"打开店铺失败: {browser}", "WARNING") driver = None + continue # 判断是否需要登录 need_login = driver.need_login() + print("【是否需要登录】:",need_login) if need_login: self.log(f"店铺 {shop_name} 需要登录,正在登录...") - password = "" + # 获取店铺凭证 + response = get_shop_info(shop_name) + print("【获取店铺凭证返回】:",response.text) + shop_data = response.json() + if not shop_data: + mes = f"获取店铺凭证失败,响应数据: {shop_data.get('message', '未知错误')}" + self.log(mes, "ERROR") + show_notification(mes, "ERROR") + continue + + password = shop_data["data"]["password"] login_success = driver.login(password) if login_success: @@ -374,6 +388,8 @@ class MatchTak: else: self.log(f"店铺 {shop_name} 登录失败", "WARNING") driver = None + else: + break except Exception as e: import traceback @@ -409,7 +425,9 @@ class MatchTak: import traceback self.log(f"处理国家 {country_code} 失败: {str(e)}", "ERROR") self.log(traceback.format_exc(), "ERROR") + # 最后回传,标记完成 try: + # self.post_result(task_id, shop_name, country_code, "", "", is_done=True) if final_stage: self.post_result(task_id, shop_name, country_code, "", "", is_done=True) else: @@ -564,7 +582,6 @@ class MatchTak: runing_task[task_id]["processed_countries"] += 1 self.log(f"国家 {country_name} 处理完成") - def post_stage_finished(self, task_id: int, user_id, stage_index): import requests from config import DELETE_BRAND_API_BASE @@ -639,8 +656,8 @@ class MatchTak: max_retries = 3 for retry in range(max_retries): try: - print("================回传处理结果====================") - self.log(f"尝试回传结果 (第{retry + 1}/{max_retries} 次)") + print("================【匹配】=====================") + self.log(f"尝试回传结果 (第 {retry + 1}/{max_retries} 次)") self.log(f"回传URL: {url}") self.log(f"回传数据: {payload}") response = requests.post( @@ -653,9 +670,10 @@ class MatchTak: self.log(f"回传结果: {response.text}") data = response.json() if response.text else {} if response.status_code == 200 and isinstance(data, dict) and data.get("success"): - self.log(f"回传结果成功: {asin} - {status}") + self.log(f"结果回传成功: {asin} - {status}") return - self.log(f"回传结果失败,状态码: {response.status_code}", "WARNING") + else: + self.log(f"结果回传失败,状态码: {response.status_code}", "WARNING") print("=====================================") except Exception as e: @@ -666,7 +684,10 @@ class MatchTak: if retry < max_retries - 1: time.sleep(2) + self.log(f"已达到最大重试次数,结果回传最终失败", "ERROR") raise RuntimeError("已达到最大重试次数,结果回传最终失败") + + if __name__ == "__main__": user_info = { diff --git a/app/amazon/tool.py b/app/amazon/tool.py index 4f688cc..af48ded 100644 --- a/app/amazon/tool.py +++ b/app/amazon/tool.py @@ -5,6 +5,9 @@ except ImportError: webview = None +import requests +from urllib.parse import quote + def show_notification(message: str, message_type: str = "error"): """显示 pywebview 顶层通知(5秒后自动消失) @@ -75,3 +78,39 @@ def show_notification(message: str, message_type: str = "error"): print(f"显示通知失败: {str(e)}") + + +def get_shop_info(shop_name: str, base_url: str = "http://8.136.19.173:18080") -> requests.Response: + """ + 调用商铺凭证接口 + :param shop_name: 商铺名称,将自动进行 URL 编码 + :param base_url: API 基础地址,默认从 curl 中提取 + :return: requests.Response 对象 + """ + url = f"{base_url}/api/admin/shop-manages/credential" + params = {"shopName": shop_name} + + headers = { + "Accept": "*/*", + "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8", + "Cache-Control": "no-cache", + "Connection": "keep-alive", + "Pragma": "no-cache", + "Referer": "http://8.136.19.173:18080/doc.html", + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36", + "X-Internal-Token": "59c6691199917a2095827b5188502029", + } + + response = requests.get( + url, + params=params, + headers=headers, + verify=False, + timeout=30 + ) + return response + + + + +