modified: amazon/__pycache__/approve.cpython-39.pyc

modified:   amazon/__pycache__/asin_status.cpython-39.pyc
	modified:   amazon/__pycache__/del_brand.cpython-39.pyc
	modified:   amazon/__pycache__/match_action.cpython-39.pyc
	modified:   amazon/__pycache__/price_match.cpython-39.pyc
	modified:   amazon/approve.py
	modified:   amazon/asin_status.py
	modified:   amazon/del_brand.py
	modified:   amazon/match_action.py
	modified:   amazon/price_match.py
	modified:   main.py
This commit is contained in:
铭坤
2026-04-30 10:52:52 +08:00
parent e09ce6d13b
commit 83bd5839d0
11 changed files with 473 additions and 297 deletions

View File

@@ -1082,6 +1082,68 @@ class ApproveTask:
del runing_shop[shop_name] del runing_shop[shop_name]
self.log(f"店铺 {shop_name} 已从执行列表中移除") self.log(f"店铺 {shop_name} 已从执行列表中移除")
def action_init(self,driver,country_name,shop_name,risk_listing_filter):
"""切换到指定国际 -> 页面 -》 筛选好"""
max_retries = 5
switch_success = False
switch_success_pg = False
search_success = False
sku_ls = []
for retry in range(max_retries):
try:
self.log(f"尝试切换到国家 {country_name} (第 {retry + 1}/{max_retries} 次)")
if retry > 2:
# 刷新不行就重新打开店铺
self.log("重试前重新打开店铺...")
try:
driver.close_store()
time.sleep(3)
driver.open_shop(shop_name)
except Exception as e:
self.log(f"关闭重新打开店铺: {str(e)}", "WARNING")
# 如果不是第一次尝试,先刷新页面
if retry > 0:
self.log("重试前刷新页面...")
try:
driver.tab.refresh()
time.sleep(3)
except Exception as e:
self.log(f"刷新页面失败: {str(e)}", "WARNING")
switch_success = driver.SwitchingCountries(country_name)
if switch_success:
self.log(f"成功切换到国家 {country_name}")
# 切换到库存管理页面
driver.SwitchPage()
self.log(f"已切换到库存管理页面")
time.sleep(3)
switch_success_pg = True
sku_ls = driver.search(filter_type=risk_listing_filter)
search_success = True
self.log(f"国家 {country_name} 搜索出 {len(sku_ls)} 商品,开始处理...")
break
else:
self.log(f"切换到国家 {country_name} 失败", "WARNING")
except Exception as e:
import traceback
self.log(f"切换国家 {country_name} 异常: {str(e)}", "ERROR")
self.log(traceback.format_exc(), "ERROR")
# 如果还有重试机会,等待后继续
if retry < max_retries - 1:
time.sleep(2)
# 如果切换失败,直接返回
# if not switch_success or not switch_success_pg or not search_success:
# error_message = f"切换到国家({switch_success})/库存页面({switch_success_pg})/搜索筛选到指定选项({search_success}) {country_name} 失败,已重试 {max_retries} 次,跳过该国家"
# self.log(error_message, "ERROR")
# if task_id in runing_task:
# runing_task[task_id]["processed_countries"] += 1
return switch_success,switch_success_pg,search_success,sku_ls
def process_country(self, driver: AmzoneApprove, country_code: str, task_id: int, shop_name: str, risk_listing_filter: str): def process_country(self, driver: AmzoneApprove, country_code: str, task_id: int, shop_name: str, risk_listing_filter: str):
"""处理单个国家的审批任务 """处理单个国家的审批任务
@@ -1108,81 +1170,92 @@ class ApproveTask:
max_retries = 3 max_retries = 3
switch_success = False switch_success = False
for retry in range(max_retries): # for retry in range(max_retries):
try: # try:
self.log(f"尝试切换到国家 {country_name} (第 {retry + 1}/{max_retries} 次)") # self.log(f"尝试切换到国家 {country_name} (第 {retry + 1}/{max_retries} 次)")
if retry > 1: # if retry > 1:
# 刷新不行就重新打开店铺 # # 刷新不行就重新打开店铺
self.log("重试前重新打开店铺...") # self.log("重试前重新打开店铺...")
try: # try:
driver.close_store() # driver.close_store()
time.sleep(3) # time.sleep(3)
driver.open_shop(shop_name) # driver.open_shop(shop_name)
except Exception as e: # except Exception as e:
self.log(f"关闭重新打开店铺: {str(e)}", "WARNING") # self.log(f"关闭重新打开店铺: {str(e)}", "WARNING")
#
# 如果不是第一次尝试,先刷新页面 # # 如果不是第一次尝试,先刷新页面
if retry > 0: # if retry > 0:
self.log("重试前刷新页面...") # self.log("重试前刷新页面...")
try: # try:
driver.tab.refresh() # driver.tab.refresh()
time.sleep(3) # time.sleep(3)
except Exception as e: # except Exception as e:
self.log(f"刷新页面失败: {str(e)}", "WARNING") # self.log(f"刷新页面失败: {str(e)}", "WARNING")
#
switch_success = driver.SwitchingCountries(country_name) # switch_success = driver.SwitchingCountries(country_name)
if switch_success: # if switch_success:
self.log(f"成功切换到国家 {country_name}") # self.log(f"成功切换到国家 {country_name}")
break # break
else: # else:
self.log(f"切换到国家 {country_name} 失败", "WARNING") # self.log(f"切换到国家 {country_name} 失败", "WARNING")
#
except Exception as e: # except Exception as e:
import traceback # import traceback
self.log(f"切换国家 {country_name} 异常: {str(e)}", "ERROR") # self.log(f"切换国家 {country_name} 异常: {str(e)}", "ERROR")
self.log(traceback.format_exc(), "ERROR") # self.log(traceback.format_exc(), "ERROR")
#
# 如果还有重试机会,等待后继续 # # 如果还有重试机会,等待后继续
if retry < max_retries - 1: # if retry < max_retries - 1:
time.sleep(2) # time.sleep(2)
#
# # 如果切换失败,直接返回
# if not switch_success:
# error_message = f"切换到国家 {country_name} 失败,已重试 {max_retries} 次,跳过该国家"
# self.log(error_message, "ERROR")
# if task_id in runing_task:
# runing_task[task_id]["processed_countries"] += 1
# return
#
# # 切换到库存管理页面
# try:
# driver.SwitchPage()
# self.log(f"已切换到库存管理页面")
# except Exception as e:
# import traceback
# self.log(f"切换页面失败: {str(e)}", "ERROR")
# self.log(traceback.format_exc(), "ERROR")
# if task_id in runing_task:
# runing_task[task_id]["processed_countries"] += 1
# return
#
# # 搜索需要审批的商品最多重试3次
# sku_ls = []
# for retry in range(max_retries):
# try:
# self.log(f"尝试搜索需要审批的商品 (第 {retry + 1}/{max_retries} 次)")
# sku_ls = driver.search(filter_type=risk_listing_filter)
# break
# except Exception as e:
# self.log(f"搜索商品异常: {str(e)}", "ERROR")
# if retry < max_retries - 1:
# try:
# driver.tab.refresh()
# time.sleep(3)
# except Exception as refresh_error:
# self.log(f"刷新页面失败: {str(refresh_error)}", "WARNING")
#
max_retries = 5
switch_success, switch_success_pg, search_success,sku_ls = self.action_init(driver, country_name, shop_name,
risk_listing_filter)
# 如果切换失败,直接返回 # 如果切换失败,直接返回
if not switch_success: if not switch_success or not switch_success_pg or not search_success:
error_message = f"切换到国家 {country_name} 失败,已重试 {max_retries} 次,跳过该国家" error_message = f"切换到国家({switch_success})/库存页面({switch_success_pg})/搜索筛选到指定选项({search_success}) {country_name} 失败,已重试 {max_retries} 次,跳过该国家"
self.log(error_message, "ERROR") self.log(error_message, "ERROR")
if task_id in runing_task: if task_id in runing_task:
runing_task[task_id]["processed_countries"] += 1 runing_task[task_id]["processed_countries"] += 1
return return
# 切换到库存管理页面 # # 如果没有需要审批的商品,直接返回
try:
driver.SwitchPage()
self.log(f"已切换到库存管理页面")
except Exception as e:
import traceback
self.log(f"切换页面失败: {str(e)}", "ERROR")
self.log(traceback.format_exc(), "ERROR")
if task_id in runing_task:
runing_task[task_id]["processed_countries"] += 1
return
# 搜索需要审批的商品最多重试3次
sku_ls = []
for retry in range(max_retries):
try:
self.log(f"尝试搜索需要审批的商品 (第 {retry + 1}/{max_retries} 次)")
sku_ls = driver.search(filter_type=risk_listing_filter)
break
except Exception as e:
self.log(f"搜索商品异常: {str(e)}", "ERROR")
if retry < max_retries - 1:
try:
driver.tab.refresh()
time.sleep(3)
except Exception as refresh_error:
self.log(f"刷新页面失败: {str(refresh_error)}", "WARNING")
# 如果没有需要审批的商品,直接返回
if len(sku_ls) == 0: if len(sku_ls) == 0:
self.log(f"国家 {country_name} 没有需要审批的商品") self.log(f"国家 {country_name} 没有需要审批的商品")
if task_id in runing_task: if task_id in runing_task:

View File

@@ -537,8 +537,6 @@ class StatusTask:
self.log(f"切换页面失败重试退出", "ERROR") self.log(f"切换页面失败重试退出", "ERROR")
return return
# 处理所有需要审批的商品通过yield获取结果 # 处理所有需要审批的商品通过yield获取结果
# 指定 asin # 指定 asin

View File

@@ -8,11 +8,12 @@ import json
import os import os
from typing import Literal from typing import Literal
from DrissionPage import Chromium from DrissionPage import Chromium,ChromiumOptions
from DrissionPage.common import By from DrissionPage.common import By
from DrissionPage._pages.chromium_tab import ChromiumTab from DrissionPage._pages.chromium_tab import ChromiumTab
def kill_process(version: Literal["v5", "v6"]): def kill_process(version: Literal["v5", "v6"]):
"""杀紫鸟客户端进程(独立函数版本)""" """杀紫鸟客户端进程(独立函数版本)"""
driver = ZiniaoDriver({}) driver = ZiniaoDriver({})
@@ -205,7 +206,11 @@ class ZiniaoDriver:
Returns: Returns:
Chromium: DrissionPage浏览器实例 Chromium: DrissionPage浏览器实例
""" """
browser = Chromium(port) co = ChromiumOptions()
# 设置不加载图片、静音
co.set_local_port(port)
co.no_imgs(True).mute(True)
browser = Chromium(co)
return browser return browser
def start_client(self): def start_client(self):

View File

@@ -468,6 +468,70 @@ class MatchTak:
del runing_shop[shop_name] del runing_shop[shop_name]
self.log(f"店铺 {shop_name} 已从执行列表中移除") self.log(f"店铺 {shop_name} 已从执行列表中移除")
def action_init(self,driver,country_name,shop_name,risk_listing_filter):
"""切换到指定国际 -> 页面 -》 筛选好"""
max_retries = 5
switch_success = False
switch_success_pg = False
search_success = False
sku_ls = []
for retry in range(max_retries):
try:
self.log(f"尝试切换到国家 {country_name} (第 {retry + 1}/{max_retries} 次)")
if retry > 2:
# 刷新不行就重新打开店铺
self.log("重试前重新打开店铺...")
try:
driver.close_store()
time.sleep(3)
driver.open_shop(shop_name)
except Exception as e:
self.log(f"关闭重新打开店铺: {str(e)}", "WARNING")
# 如果不是第一次尝试,先刷新页面
if retry > 0:
self.log("重试前刷新页面...")
try:
driver.tab.refresh()
time.sleep(3)
except Exception as e:
self.log(f"刷新页面失败: {str(e)}", "WARNING")
switch_success = driver.SwitchingCountries(country_name)
if switch_success:
self.log(f"成功切换到国家 {country_name}")
# 切换到库存管理页面
driver.SwitchPage()
self.log(f"已切换到库存管理页面")
time.sleep(3)
switch_success_pg = True
sku_ls = driver.search(filter_type=risk_listing_filter)
search_success = True
self.log(f"国家 {country_name} 搜索出 {len(sku_ls)} 商品,开始处理...")
break
else:
self.log(f"切换到国家 {country_name} 失败", "WARNING")
except Exception as e:
import traceback
self.log(f"切换国家 {country_name} 异常: {str(e)}", "ERROR")
self.log(traceback.format_exc(), "ERROR")
# 如果还有重试机会,等待后继续
if retry < max_retries - 1:
time.sleep(2)
# 如果切换失败,直接返回
# if not switch_success or not switch_success_pg or not search_success:
# error_message = f"切换到国家({switch_success})/库存页面({switch_success_pg})/搜索筛选到指定选项({search_success}) {country_name} 失败,已重试 {max_retries} 次,跳过该国家"
# self.log(error_message, "ERROR")
# if task_id in runing_task:
# runing_task[task_id]["processed_countries"] += 1
return switch_success,switch_success_pg,search_success,sku_ls
def process_country(self, driver: AmzoneMatchAction, country_code: str, task_id: int, shop_name: str, risk_listing_filter: str,limit:str=None): def process_country(self, driver: AmzoneMatchAction, country_code: str, task_id: int, shop_name: str, risk_listing_filter: str,limit:str=None):
"""处理单个国家的审批任务 """处理单个国家的审批任务
@@ -491,86 +555,104 @@ class MatchTak:
runing_task[task_id]["current_country"] = country_name runing_task[task_id]["current_country"] = country_name
# 切换国家最多重试3次 # 切换国家最多重试3次
max_retries = 3 # max_retries = 3
switch_success = False # switch_success = False
#
for retry in range(max_retries): # for retry in range(max_retries):
try: # try:
self.log(f"尝试切换到国家 {country_name} (第 {retry + 1}/{max_retries} 次)") # self.log(f"尝试切换到国家 {country_name} (第 {retry + 1}/{max_retries} 次)")
if retry > 1: # if retry > 1:
# 刷新不行就重新打开店铺 # # 刷新不行就重新打开店铺
self.log("重试前重新打开店铺...") # self.log("重试前重新打开店铺...")
try: # try:
driver.close_store() # driver.close_store()
time.sleep(3) # time.sleep(3)
driver.open_shop(shop_name) # driver.open_shop(shop_name)
except Exception as e: # except Exception as e:
self.log(f"关闭重新打开店铺: {str(e)}", "WARNING") # self.log(f"关闭重新打开店铺: {str(e)}", "WARNING")
#
# 如果不是第一次尝试,先刷新页面 # # 如果不是第一次尝试,先刷新页面
if retry > 0: # if retry > 0:
self.log("重试前刷新页面...") # self.log("重试前刷新页面...")
try: # try:
driver.tab.refresh() # driver.tab.refresh()
time.sleep(3) # time.sleep(3)
except Exception as e: # except Exception as e:
self.log(f"刷新页面失败: {str(e)}", "WARNING") # self.log(f"刷新页面失败: {str(e)}", "WARNING")
#
switch_success = driver.SwitchingCountries(country_name) # switch_success = driver.SwitchingCountries(country_name)
if switch_success: # if switch_success:
self.log(f"成功切换到国家 {country_name}") # self.log(f"成功切换到国家 {country_name}")
break # break
else: # else:
self.log(f"切换到国家 {country_name} 失败", "WARNING") # self.log(f"切换到国家 {country_name} 失败", "WARNING")
#
except Exception as e: # except Exception as e:
import traceback # import traceback
self.log(f"切换国家 {country_name} 异常: {str(e)}", "ERROR") # self.log(f"切换国家 {country_name} 异常: {str(e)}", "ERROR")
self.log(traceback.format_exc(), "ERROR") # self.log(traceback.format_exc(), "ERROR")
#
# 如果还有重试机会,等待后继续 # # 如果还有重试机会,等待后继续
if retry < max_retries - 1: # if retry < max_retries - 1:
time.sleep(2) # time.sleep(2)
#
# # 如果切换失败,直接返回
# if not switch_success:
# error_message = f"切换到国家 {country_name} 失败,已重试 {max_retries} 次,跳过该国家"
# self.log(error_message, "ERROR")
# if task_id in runing_task:
# runing_task[task_id]["processed_countries"] += 1
# return
#
# # 切换到库存管理页面
# try:
# driver.SwitchPage()
# self.log(f"已切换到库存管理页面")
# except Exception as e:
# import traceback
# self.log(f"切换页面失败: {str(e)}", "ERROR")
# self.log(traceback.format_exc(), "ERROR")
# if task_id in runing_task:
# runing_task[task_id]["processed_countries"] += 1
# return
#
# # 搜索需要审批的商品最多重试3次
# sku_ls = []
# for retry in range(max_retries):
# try:
# self.log(f"尝试搜索匹配操作商品 (第 {retry + 1}/{max_retries} 次)")
# sku_ls = driver.search(filter_type=risk_listing_filter)
# break
# except Exception as e:
# self.log(f"搜索商品异常: {str(e)}", "ERROR")
# if retry < max_retries - 1:
# try:
# driver.tab.refresh()
# time.sleep(3)
# except Exception as refresh_error:
# self.log(f"刷新页面失败: {str(refresh_error)}", "WARNING")
#
# # 如果没有需要审批的商品,直接返回
# if len(sku_ls) == 0:
# self.log(f"国家 {country_name} 没有搜索出的商品")
# if task_id in runing_task:
# runing_task[task_id]["processed_countries"] += 1
# return
#
max_retries = 5
switch_success, switch_success_pg, search_success, sku_ls = self.action_init(driver, country_name, shop_name,
risk_listing_filter)
# 如果切换失败,直接返回 # 如果切换失败,直接返回
if not switch_success: if not switch_success or not switch_success_pg or not search_success:
error_message = f"切换到国家 {country_name} 失败,已重试 {max_retries} 次,跳过该国家" error_message = f"切换到国家({switch_success})/库存页面({switch_success_pg})/搜索筛选到指定选项({search_success}) {country_name} 失败,已重试 {max_retries} 次,跳过该国家"
self.log(error_message, "ERROR") self.log(error_message, "ERROR")
if task_id in runing_task: if task_id in runing_task:
runing_task[task_id]["processed_countries"] += 1 runing_task[task_id]["processed_countries"] += 1
return return
# 切换到库存管理页面 # # 如果没有需要审批的商品,直接返回
try:
driver.SwitchPage()
self.log(f"已切换到库存管理页面")
except Exception as e:
import traceback
self.log(f"切换页面失败: {str(e)}", "ERROR")
self.log(traceback.format_exc(), "ERROR")
if task_id in runing_task:
runing_task[task_id]["processed_countries"] += 1
return
# 搜索需要审批的商品最多重试3次
sku_ls = []
for retry in range(max_retries):
try:
self.log(f"尝试搜索匹配操作商品 (第 {retry + 1}/{max_retries} 次)")
sku_ls = driver.search(filter_type=risk_listing_filter)
break
except Exception as e:
self.log(f"搜索商品异常: {str(e)}", "ERROR")
if retry < max_retries - 1:
try:
driver.tab.refresh()
time.sleep(3)
except Exception as refresh_error:
self.log(f"刷新页面失败: {str(refresh_error)}", "WARNING")
# 如果没有需要审批的商品,直接返回
if len(sku_ls) == 0: if len(sku_ls) == 0:
self.log(f"国家 {country_name} 没有搜索出的商品") self.log(f"国家 {country_name} 没有需要审批的商品")
if task_id in runing_task: if task_id in runing_task:
runing_task[task_id]["processed_countries"] += 1 runing_task[task_id]["processed_countries"] += 1
return return

View File

@@ -16,28 +16,32 @@ from amazon.tool import show_notification,get_shop_info,remove_special_character
from config import runing_task, runing_shop,base_dir,DELETE_BRAND_API_BASE from config import runing_task, runing_shop,base_dir,DELETE_BRAND_API_BASE
class RepricingLogic: class RepricingLogic:
@staticmethod @staticmethod
def situation_1_general_decrease(my_price, rank1_price, is_first_time=False): def situation_1_general_decrease(my_price, rank1_price, is_first_time=False):
""" """
情况1常规递减跟价 (最左侧独立长图块) 情况1常规递减跟价 (最左侧独立长图块)
当不是自己购物车时,按总价当作第一名递减。
""" """
if is_first_time: if is_first_time:
return rank1_price - 0.3 return rank1_price - 0.3
diff = my_price - rank1_price # 这里的 diff 算出的是一个正数,代表“我比第一名低多少”
diff = rank1_price - my_price
# 精简了原图中长串的阶梯逻辑 # 如果当前价格高于或等于第一名默认按照第一名减0.3处理
if diff <= 0:
return rank1_price - 0.3
# 逻辑更清晰的写法:第一名价格 - (差价 + 对应阶梯值)
# 例如差价0.7,则相当于 第一名价格 - (0.7 + 0.5) = 第一名价格 - 1.2
if diff <= 0.3: if diff <= 0.3:
return rank1_price - 0.5 return rank1_price - (diff + 0.5)
elif diff <= 0.8: elif diff <= 0.8:
return rank1_price - 0.7 return rank1_price - (diff + 0.7)
elif diff <= 10.5: elif diff <= 10.5:
# 0.8 到 10.5 之间,原图逻辑全部是减1 # 0.8 到 10.5 之间全部是减1
return rank1_price - 1.0 return rank1_price - (diff + 1.0)
else: else:
return None # 相差10.5以上,跳过 return None # 相差10.5以上,跳过
@@ -45,55 +49,56 @@ class RepricingLogic:
def situation_2_own_buybox(my_price, rank2_price): def situation_2_own_buybox(my_price, rank2_price):
""" """
情况2已经是自己购物车 情况2已经是自己购物车
逻辑:判断与第二名的差价,如果拉开足够差距,则稍微降一点点(减0.5)保持优势,防止卖太便宜。
""" """
diff = rank2_price - my_price diff = rank2_price - my_price
# 1. 绝对差价2欧以上
if diff >= 2.0: if diff >= 2.0:
return rank2_price - 0.3 return rank2_price - 0.3
# 2. 产品售价区间判定 (是否要提高价格)
if (20 <= my_price < 30 and diff >= 4) or \ if (20 <= my_price < 30 and diff >= 4) or \
(30 <= my_price < 60 and diff >= 8) or \ (30 <= my_price < 60 and diff >= 8) or \
(60 <= my_price <= 150 and diff >= 15): (60 <= my_price <= 150 and diff >= 15):
return rank2_price - 0.3 # 满足区间大差价,提价至第二名之下 return rank2_price - 0.3
return my_price # 不满足条件则保持原价 return my_price
@staticmethod @staticmethod
def situation_3_not_own_buybox(my_price, rank1_price): def situation_3_not_own_buybox(my_price, rank1_price):
""" """
情况3不是自己购物车 (常规情况) 情况3不是自己购物车 (除Amazon US外)
""" """
diff = my_price - rank1_price diff = rank1_price - my_price
if diff <= 0:
return rank1_price - 0.3
# 逻辑更清晰的写法:第一名价格 - (差价 + 对应阶梯值)
if diff <= 0.3: if diff <= 0.3:
return rank1_price - 0.5 return rank1_price - (diff + 0.5)
elif 0.5 <= diff <= 0.8: elif diff <= 0.8:
return rank1_price - 0.7 return rank1_price - (diff + 0.7)
elif 0.8 < diff <= 2.5: elif diff <= 2.5:
return rank1_price - 1.0 return rank1_price - (diff + 1.0)
elif diff > 2.5: # 2.5-3.5以上跳过 else:
return None return None # 2.5-3.5以上跳过
return rank1_price - 0.3 # 默认按第一名减0.3
@staticmethod @staticmethod
def situation_4_encounter_amz_us_1st(my_price, amz_us_price): def situation_4_encounter_amz_us_1st(my_price, amz_us_price):
""" """
情况4第一名是 Amazon US 卖家 情况4遇到第一名是 Amazon US 卖家
""" """
diff = my_price - amz_us_price diff = amz_us_price - my_price
if diff <= 3: if diff <= 0:
return amz_us_price - 2
elif 4 <= diff <= 6:
return amz_us_price - 3 return amz_us_price - 3
elif 8 <= diff <= 12:
return None # 跳过不跟价
return amz_us_price - 3 # 默认直接按照 Amazon US 减去3 # 逻辑更清晰的写法:第一名价格 - (差价 + 对应阶梯值)
if diff <= 3:
return amz_us_price - (diff + 2)
elif diff <= 6:
return amz_us_price - (diff + 3)
else:
return None # 超过6直接跳过
@staticmethod @staticmethod
def situation_5_im_1st_amz_us_2nd(my_price, amz_us_price): def situation_5_im_1st_amz_us_2nd(my_price, amz_us_price):
@@ -103,7 +108,7 @@ class RepricingLogic:
diff = amz_us_price - my_price diff = amz_us_price - my_price
if diff <= 7: if diff <= 7:
return None # 相差5以内跳过 (保持原价) return None # 相差7以内跳过 (保持原价)
else: else:
return amz_us_price - 5 return amz_us_price - 5
@@ -133,16 +138,6 @@ def calculate_target_price(
price_col = recommended_price + recommended_shipping price_col = recommended_price + recommended_shipping
print(f"【紫鸟后台价格】{is_my_buybox} 推荐价格: {recommended_price},推荐运费: {recommended_shipping},总和: {price_col}") print(f"【紫鸟后台价格】{is_my_buybox} 推荐价格: {recommended_price},推荐运费: {recommended_shipping},总和: {price_col}")
return RepricingLogic.situation_1_general_decrease(my_price,price_col,is_my_buybox) return RepricingLogic.situation_1_general_decrease(my_price,price_col,is_my_buybox)
# backend_base_price = recommended_price
# backend_shipping = recommended_shipping
# total_price = backend_base_price + backend_shipping
#
# if is_my_buybox:
# # 🌟【逻辑更新】:如果是自己的购物车,直接跳过,不做任何价格调整
# return None
# else:
# # 不是自己的购物车:直接将紫鸟的总和作为"第一名"价格,强制抛入阶梯跟价逻辑
# return calculate_standard_competitor_pricing(my_price, total_price)
# # 第一名是自己, 第二名 Amazon US 卖家 的情况 # # 第一名是自己, 第二名 Amazon US 卖家 的情况
shop_name_1 = front_end_data.get("top_sellers")[0].get("shop_name") shop_name_1 = front_end_data.get("top_sellers")[0].get("shop_name")
@@ -152,13 +147,11 @@ def calculate_target_price(
return RepricingLogic.situation_5_im_1st_amz_us_2nd(my_price,price_2) return RepricingLogic.situation_5_im_1st_amz_us_2nd(my_price,price_2)
# --- Step 3: 常规核心分流逻辑 (如果没有紫鸟后台数据) --- # --- Step 3: 常规核心分流逻辑 (如果没有紫鸟后台数据) ---
# 分支 A第一名是 Amazon US 卖家 (特殊强敌优先) # 分支 A第一名是 Amazon US 卖家 (特殊强敌优先)
price_1 = float(front_end_data.get("top_sellers")[0].get("price")) # 实际第一名价格 price_1 = float(front_end_data.get("top_sellers")[0].get("price")) # 实际第一名价格
if "Amazon" in shop_name_1: if "Amazon" in shop_name_1:
return RepricingLogic.situation_4_encounter_amz_us_1st(my_price,price_1) return RepricingLogic.situation_4_encounter_amz_us_1st(my_price,price_1)
# 分支 B不是 Amazon US且目前是自己的购物车 # 分支 B不是 Amazon US且目前是自己的购物车
elif is_my_buybox: elif is_my_buybox:
price_2 = float(front_end_data.get("top_sellers")[1].get("price")) # 实际第二名价格 price_2 = float(front_end_data.get("top_sellers")[1].get("price")) # 实际第二名价格
@@ -650,6 +643,13 @@ class AmzonePriceMatch(AmamzonBase):
print(f"{self.mark_name}】获取到 {len(sku_ls)}") print(f"{self.mark_name}】获取到 {len(sku_ls)}")
if len(sku_ls) == 0: if len(sku_ls) == 0:
if mode == "asin":
print(f"asin 模式查询不到,{asin}")
yield (asin, {
"statu": "查询不到"
})
break
retry_num += 1 retry_num += 1
print(f"没有获取到 ASIN 商品,重试{retry_num}/{max_retry_num}") print(f"没有获取到 ASIN 商品,重试{retry_num}/{max_retry_num}")
self.tab.refresh() self.tab.refresh()
@@ -680,19 +680,16 @@ class AmzonePriceMatch(AmamzonBase):
bottom_price = f"{sum(split_currency_values(bottom_price_text))}" bottom_price = f"{sum(split_currency_values(bottom_price_text))}"
except Exception as e: except Exception as e:
print("最低价提取失败",e) print("最低价提取失败",e)
current_price_ele = sku_ele.eles('xpath:.//b[text()="价格"]/../..//kat-input',timeout=5) current_price_ele = sku_ele.eles('xpath:.//b[text()="价格"]/../..//kat-input',timeout=5)
if len(current_price_ele) > 0: if len(current_price_ele) > 0:
current_price = current_price_ele[0].attr("value") current_price = current_price_ele[0].attr("value")
print("获取到当前价格为 ->",current_price) print("获取到当前价格为 ->",current_price)
# 如果紫鸟里当前价格低于最低价则删除,否则跳过 # 如果紫鸟里当前价格低于最低价则删除,否则跳过
if mode == "status" and miniprice_info.get(asin): # if mode == "status" and miniprice_info.get(asin):
if miniprice_info.get(asin):
backend_price = float(miniprice_info.get(asin)) backend_price = float(miniprice_info.get(asin))
if float(current_price) <= backend_price: if float(current_price) <= backend_price:
# yield (asin, {
# "statu": "删除(当前价格低于最低价)",
# "deleteSkipAsin": True,
# "removeAsin": asin,
# })
yield (asin, { yield (asin, {
"statu": "跳过(当前价格低于或等于最低价)", "statu": "跳过(当前价格低于或等于最低价)",
"currentPrice": current_price, "currentPrice": current_price,
@@ -764,6 +761,9 @@ class AmzonePriceMatch(AmamzonBase):
print(asin,"-->>",f"【逻辑计算后的价格】",adjust_prices) print(asin,"-->>",f"【逻辑计算后的价格】",adjust_prices)
if adjust_prices is not None and adjust_prices != float(current_price): if adjust_prices is not None and adjust_prices != float(current_price):
#修改价格 #修改价格
print("准备填入的原始值:",adjust_prices)
adjust_prices = round(adjust_prices, 2)
print("保留两位小数后的::",adjust_prices)
self.tab.actions.click(current_price_ele[0]) self.tab.actions.click(current_price_ele[0])
time.sleep(1) time.sleep(1)
current_price_ele[0].input(f"{adjust_prices}", clear=True) current_price_ele[0].input(f"{adjust_prices}", clear=True)
@@ -784,12 +784,12 @@ class AmzonePriceMatch(AmamzonBase):
"statu" : "改价成功", "statu" : "改价成功",
"currentPrice" : current_price, "currentPrice" : current_price,
"recommendedPrice" : f"{recommendedPrice}", "recommendedPrice" : f"{recommendedPrice}",
"minimumPrice" : bottom_price, #最低价格 "minimumPrice" : f"{miniprice_info.get(asin)}" if miniprice_info.get(asin) else "",#bottom_price, #最低价格
"firstPlace": price_1, "firstPlace": price_1,
"secondPlace": price_2, "secondPlace": price_2,
"cartShopName" : cartShopName, "cartShopName" : cartShopName,
"shippingFee" : f"{shipping_fee}", "shippingFee" : f"{shipping_fee}",
"priceChangeStatus" : f"{adjust_prices}", "priceChangePrice" : f"{adjust_prices}",
"priceChangeStatus" : "改价成功", "priceChangeStatus" : "改价成功",
}) })
else: else:
@@ -803,12 +803,14 @@ class AmzonePriceMatch(AmamzonBase):
"statu": "跳过,无需改价", "statu": "跳过,无需改价",
"currentPrice": current_price, "currentPrice": current_price,
"recommendedPrice": f"{recommendedPrice}", "recommendedPrice": f"{recommendedPrice}",
"minimumPrice": bottom_price, # 最低价格 # "minimumPrice": bottom_price, # 最低价格
"minimumPrice": f"{miniprice_info.get(asin)}" if miniprice_info.get(asin) else "",
"firstPlace": price_1, "firstPlace": price_1,
"secondPlace": price_2, "secondPlace": price_2,
"cartShopName": cartShopName, "cartShopName": cartShopName,
"shippingFee": f"{shipping_fee}", "shippingFee": f"{shipping_fee}",
"priceChangeStatus": "跳过,无需改价", "priceChangeStatus": "跳过,无需改价",
"priceChangePrice": f"{adjust_prices}".replace("None",""),
}) })
already_asin.add(asin) already_asin.add(asin)
@@ -1029,6 +1031,68 @@ class PriceTask:
return driver return driver
return driver return driver
def action_init(self,driver,country_name,shop_name,risk_listing_filter):
"""切换到指定国际 -> 页面 -》 筛选好"""
max_retries = 5
switch_success = False
switch_success_pg = False
search_success = False
sku_ls = []
for retry in range(max_retries):
try:
self.log(f"尝试切换到国家 {country_name} (第 {retry + 1}/{max_retries} 次)")
if retry > 2:
# 刷新不行就重新打开店铺
self.log("重试前重新打开店铺...")
try:
driver.close_store()
time.sleep(3)
driver.open_shop(shop_name)
except Exception as e:
self.log(f"关闭重新打开店铺: {str(e)}", "WARNING")
# 如果不是第一次尝试,先刷新页面
if retry > 0:
self.log("重试前刷新页面...")
try:
driver.tab.refresh()
time.sleep(3)
except Exception as e:
self.log(f"刷新页面失败: {str(e)}", "WARNING")
switch_success = driver.SwitchingCountries(country_name)
if switch_success:
self.log(f"成功切换到国家 {country_name}")
# 切换到库存管理页面
driver.SwitchPage()
self.log(f"已切换到库存管理页面")
time.sleep(3)
switch_success_pg = True
sku_ls = driver.search(filter_type=risk_listing_filter)
search_success = True
self.log(f"国家 {country_name} 搜索出 {len(sku_ls)} 商品,开始处理...")
break
else:
self.log(f"切换到国家 {country_name} 失败", "WARNING")
except Exception as e:
import traceback
self.log(f"切换国家 {country_name} 异常: {str(e)}", "ERROR")
self.log(traceback.format_exc(), "ERROR")
# 如果还有重试机会,等待后继续
if retry < max_retries - 1:
time.sleep(2)
# 如果切换失败,直接返回
# if not switch_success or not switch_success_pg or not search_success:
# error_message = f"切换到国家({switch_success})/库存页面({switch_success_pg})/搜索筛选到指定选项({search_success}) {country_name} 失败,已重试 {max_retries} 次,跳过该国家"
# self.log(error_message, "ERROR")
# if task_id in runing_task:
# runing_task[task_id]["processed_countries"] += 1
return switch_success,switch_success_pg,search_success
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, limit: str = None): user_id=None, stage_index=None, final_stage: bool = True, limit: str = None):
@@ -1149,94 +1213,42 @@ class PriceTask:
runing_task[task_id]["current_country"] = country_name runing_task[task_id]["current_country"] = country_name
# 切换国家最多重试3次 # 切换国家最多重试3次
max_retries = 3 max_retries = 5
switch_success = False
for retry in range(max_retries):
try:
self.log(f"尝试切换到国家 {country_name} (第 {retry + 1}/{max_retries} 次)")
if retry > 1:
# 刷新不行就重新打开店铺
self.log("重试前重新打开店铺...")
try:
driver.close_store()
time.sleep(3)
driver.open_shop(shop_name)
except Exception as e:
self.log(f"关闭重新打开店铺: {str(e)}", "WARNING")
# 如果不是第一次尝试,先刷新页面
if retry > 0:
self.log("重试前刷新页面...")
try:
driver.tab.refresh()
time.sleep(3)
except Exception as e:
self.log(f"刷新页面失败: {str(e)}", "WARNING")
switch_success = driver.SwitchingCountries(country_name)
if switch_success:
self.log(f"成功切换到国家 {country_name}")
break
else:
self.log(f"切换到国家 {country_name} 失败", "WARNING")
except Exception as e:
import traceback
self.log(f"切换国家 {country_name} 异常: {str(e)}", "ERROR")
self.log(traceback.format_exc(), "ERROR")
# 如果还有重试机会,等待后继续
if retry < max_retries - 1:
time.sleep(2)
switch_success,switch_success_pg,search_success = self.action_init( driver, country_name, shop_name, risk_listing_filter)
# 如果切换失败,直接返回 # 如果切换失败,直接返回
if not switch_success: if not switch_success or not switch_success_pg or not search_success:
error_message = f"切换到国家 {country_name} 失败,已重试 {max_retries} 次,跳过该国家" error_message = f"切换到国家({switch_success})/库存页面({switch_success_pg})/搜索筛选到指定选项({search_success}) {country_name} 失败,已重试 {max_retries} 次,跳过该国家"
self.log(error_message, "ERROR") self.log(error_message, "ERROR")
if task_id in runing_task: if task_id in runing_task:
runing_task[task_id]["processed_countries"] += 1 runing_task[task_id]["processed_countries"] += 1
return return
# 切换到库存管理页面
for retry in range(max_retries):
try:
driver.SwitchPage()
self.log(f"已切换到库存管理页面")
except Exception as e:
import traceback
self.log(f"切换页面失败: {str(e)}", "ERROR")
self.log(traceback.format_exc(), "ERROR")
if retry >= max_retries-1:
if task_id in runing_task:
runing_task[task_id]["processed_countries"] += 1
self.log(f"切换页面失败重试退出", "ERROR")
return
# 搜索需要审批的商品最多重试3次 # 搜索需要审批的商品最多重试3次
sku_ls = [] # sku_ls = []
for retry in range(max_retries): # for retry in range(max_retries):
try: # try:
self.log(f"尝试搜索匹配操作商品 (第 {retry + 1}/{max_retries} 次)") # self.log(f"尝试搜索匹配操作商品 (第 {retry + 1}/{max_retries} 次)")
sku_ls = driver.search(filter_type=risk_listing_filter) # sku_ls = driver.search(filter_type=risk_listing_filter)
break # break
except Exception as e: # except Exception as e:
self.log(f"搜索商品异常: {str(e)}", "ERROR") # self.log(f"搜索商品异常: {str(e)}", "ERROR")
if retry < max_retries - 1: # if retry < max_retries - 1:
try: # try:
driver.tab.refresh() # driver.tab.refresh()
time.sleep(3) # driver.tab.wait.doc_loaded()
except Exception as refresh_error: # time.sleep(3)
self.log(f"刷新页面失败: {str(refresh_error)}", "WARNING") # except Exception as refresh_error:
# self.log(f"刷新页面失败: {str(refresh_error)}", "WARNING")
#
# # 如果没有需要审批的商品,直接返回
# if len(sku_ls) == 0:
# self.log(f"国家 {country_name} 没有搜索出的商品")
# if task_id in runing_task:
# runing_task[task_id]["processed_countries"] += 1
# return
# 如果没有需要审批的商品,直接返回 self.log(f"国家 {country_name} ,开始处理...")
if len(sku_ls) == 0:
self.log(f"国家 {country_name} 没有搜索出的商品")
if task_id in runing_task:
runing_task[task_id]["processed_countries"] += 1
return
self.log(f"国家 {country_name} 搜索出 {len(sku_ls)} 商品,开始处理...")
# 处理所有需要审批的商品通过yield获取结果 # 处理所有需要审批的商品通过yield获取结果
@@ -1350,13 +1362,14 @@ class PriceTask:
"firstPlace": status.get("firstPlace") if status.get("firstPlace") else "", "firstPlace": status.get("firstPlace") if status.get("firstPlace") else "",
"secondPlace": status.get("secondPlace") if status.get("secondPlace") else "", "secondPlace": status.get("secondPlace") if status.get("secondPlace") else "",
"cartShopName": status.get("cartShopName") if status.get("cartShopName") else "", "cartShopName": status.get("cartShopName") if status.get("cartShopName") else "",
"priceChangeStatus": "UPDATED", # "priceChangeStatus": "UPDATED",
"deleteSkipAsin": status.get("deleteSkipAsin",False), "deleteSkipAsin": status.get("deleteSkipAsin",False),
"removeAsin": status.get("removeAsin") if status.get("removeAsin") else "", "removeAsin": status.get("removeAsin") if status.get("removeAsin") else "",
"modifyCount": "1", "modifyCount": "1",
"shippingFee" : status.get("shippingFee") if status.get("shippingFee") else "", "shippingFee" : status.get("shippingFee") if status.get("shippingFee") else "",
"status": status.get("statu"), "status": status.get("statu"),
"priceChangeStatus" : status.get("priceChangeStatus") if status.get("priceChangeStatus") else "", "priceChangeStatus" : status.get("priceChangeStatus") if status.get("priceChangeStatus") else "",
"priceChangePrice" : status.get("priceChangePrice") if status.get("priceChangePrice") else "",
} }
] ]
}, },
@@ -1405,20 +1418,20 @@ class PriceTask:
if __name__ == '__main__': if __name__ == '__main__':
# 使用示例 # 使用示例
# user_info = { # user_info = {
# "company": "rongchuang123", # "company": "尾号5578的公司115",
# "username": "自动化_Robot", # "username": "自动化_Robot",
# "password": "#20zsg25" # "password": "#20zsg25"
# } # }
# shop_name = "郭亚芳" # shop_name = "刘建煌"
# country = "国" # country = "国"
# kill_process('v6') # kill_process('v6')
# driver = AmzonePriceMatch(user_info) # driver = AmzonePriceMatch(user_info)
# browser = driver.open_shop(shop_name) # browser = driver.open_shop(shop_name)
# sw_suc = driver.SwitchingCountries(country) # sw_suc = driver.SwitchingCountries(country)
# driver.SwitchPage() # driver.SwitchPage()
# risk_listing_filter = "Active" # risk_listing_filter = "Active"
# _shopMallName = "yafang123" # _shopMallName = "Jianhuang 888"
# ap_asin ="B0BTHBJDKG" # ap_asin ="B0F25Y52PH"
# skip_asin = [] # skip_asin = []
# for _ in range(3): # for _ in range(3):
# try: # try:
@@ -1436,14 +1449,19 @@ if __name__ == '__main__':
# print(f"ASIN {asin} 的处理结果: {status}") # print(f"ASIN {asin} 的处理结果: {status}")
# print("已完成操作") # print("已完成操作")
front_end_data = {'top_sellers': [{'rank': 1, 'price': '50.24', 'stock': '26', 'shop_name': 'yafang123'}, {'rank': 2, 'price': '44.35', 'stock': '100', 'shop_name': 'QinPimy'}], 'cart_seller': 'Amazon US', 'timestamp': '2026-04-27 16:17:42'} front_end_data = {'top_sellers': [{'rank': 1, 'price': '52.51', 'stock': '100', 'shop_name': 'Amazon US'}, {'rank': 2, 'price': '62.58', 'stock': '30', 'shop_name': 'Amazon US'}], 'cart_seller': 'Jianhuang 888', 'timestamp': '2026-04-30 09:52:49'}
current_Price = 44.35 current_Price = 52.51
current_shop_name = "yafang123" current_shop_name = "Jianhuang 888"
recommended_price = 44.35 recommended_price = 52.51
recommended_shipping =0.0 recommended_shipping =0.0
res = calculate_target_price( res = calculate_target_price(
front_end_data, current_Price, current_shop_name, front_end_data, current_Price, current_shop_name,
recommended_price, recommended_shipping recommended_price, recommended_shipping
) )
print(res) print(res)
# task_data = {'type': 'price-track-run', 'ts': 1777388166302, 'data': {'task_id': 4892, 'result_id': 6685, 'shop_name': '魏振峰', 'shop_id': '27543917795757', 'platform': '亚马逊', 'company_name': 'rongchuang123', 'shop_mall_name': 'WEIZHENFENG168', 'matched': True, 'match_status': 'MATCHED', 'match_message': None, 'success': False, 'error': None, 'output_filename': None, 'download_url': None, 'task_status': 'RUNNING', 'loop_run_id': 168, 'round_index': 1, 'country_codes': ['DE', 'UK', 'FR', 'IT', 'ES'], 'mode': 'asin', 'skip_asins': {}, 'skip_asins_by_country': {}, 'skip_asin_details_by_country': {}, 'minimum_price_by_country_and_asin': {'DE': {'B0CFY5M3XJ': '38', 'B0DSJGJBWV': '9'}, 'UK': {'B0G2LPSGP5': '30', 'B0G1LY2K8L': '10'}}, 'skip_asin_delete_policy': {'enabled': False, 'mode': 'NONE', 'deleteWhenPriceBelowMinimum': False, 'compareField': 'price', 'thresholdField': 'minimumPrice', 'target': 'skip_asin', 'source': 'frontend-price-track'}, 'delete_skip_asin_when_price_below_minimum': False, 'deleteSkipAsinWhenPriceBelowMinimum': False, 'asin_rows_by_country': {'DE': [{'shopMallName': '', 'asin': 'B0CFY5M3XJ', 'price': '', 'recommendedPrice': '', 'minimumPrice': '38', 'firstPlace': '', 'secondPlace': '', 'cartShopName': '', 'priceChangeStatus': '', 'modifyCount': '', 'status': ''}, {'shopMallName': '', 'asin': 'B0DSJGJBWV', 'price': '', 'recommendedPrice': '', 'minimumPrice': '9', 'firstPlace': '', 'secondPlace': '', 'cartShopName': '', 'priceChangeStatus': '', 'modifyCount': '', 'status': ''}], 'UK': [{'shopMallName': '', 'asin': 'B0G2LPSGP5', 'price': '', 'recommendedPrice': '', 'minimumPrice': '30', 'firstPlace': '', 'secondPlace': '', 'cartShopName': '', 'priceChangeStatus': '', 'modifyCount': '', 'status': ''}, {'shopMallName': '', 'asin': 'B0G1LY2K8L', 'price': '', 'recommendedPrice': '', 'minimumPrice': '10', 'firstPlace': '', 'secondPlace': '', 'cartShopName': '', 'priceChangeStatus': '', 'modifyCount': '', 'status': ''}]}, 'taskId': 4892, 'resultId': 6685, 'shopName': '魏振峰', 'shopId': '27543917795757', 'platformName': '亚马逊', 'companyName': 'rongchuang123', 'shopMallName': 'WEIZHENFENG168', 'matchStatus': 'MATCHED', 'matchMessage': None, 'outputFilename': None, 'downloadUrl': None, 'taskStatus': 'RUNNING', 'loopRunId': 168, 'roundIndex': 1}}
# price_cls = PriceTask(user_info)
# price_cls.process_task(task_data)

View File

@@ -438,7 +438,7 @@ def main():
window.expose(api.close, api.minimize, api.maximize, api.toggle_maximize, generate_images, api.save_image, api.save_image_to_folder, api.select_folder, api.select_brand_xlsx_files, api.select_brand_folder, window.expose(api.close, api.minimize, api.maximize, api.toggle_maximize, generate_images, api.save_image, api.save_image_to_folder, api.select_folder, api.select_brand_xlsx_files, api.select_brand_folder,
api.save_file_from_url, api.save_template_xlsx,api.save_template_zip,api.upload_file_to_java,api.open_external_url,api.enqueue_json,api.get_detail_del,api.save_file_from_url_new) api.save_file_from_url, api.save_template_xlsx,api.save_template_zip,api.upload_file_to_java,api.open_external_url,api.enqueue_json,api.get_detail_del,api.save_file_from_url_new)
webview.start( webview.start(
debug=False, debug=True,
storage_path=cache_path, storage_path=cache_path,
private_mode=False private_mode=False
) )