modified: .env
modified: amazon/__pycache__/approve.cpython-39.pyc modified: amazon/__pycache__/detail_spider.cpython-39.pyc modified: amazon/__pycache__/price_match.cpython-39.pyc modified: amazon/approve.py modified: amazon/detail_spider.py modified: amazon/price_match.py modified: assets/convert.js modified: assets/dedupe.js modified: assets/delete-brand.js modified: assets/split.js modified: blueprints/__pycache__/admin.cpython-39.pyc modified: blueprints/__pycache__/brand.cpython-39.pyc modified: blueprints/__pycache__/main.cpython-39.pyc modified: blueprints/main.py modified: brand_spider/__pycache__/main.cpython-39.pyc modified: new_web_source/convert.html modified: new_web_source/dedupe.html modified: new_web_source/delete-brand.html modified: new_web_source/split.html modified: tool/__pycache__/devices.cpython-39.pyc
This commit is contained in:
4
app/.env
4
app/.env
@@ -13,7 +13,7 @@ client_name=ShuFuAI
|
|||||||
|
|
||||||
|
|
||||||
# java_api_base=http://47.111.163.154:18080
|
# java_api_base=http://47.111.163.154:18080
|
||||||
java_api_base=http://127.0.0.1:18080
|
# java_api_base=http://127.0.0.1:18080
|
||||||
# java_api_base=http://8.136.19.173:18080
|
java_api_base=http://8.136.19.173:18080
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -475,7 +475,8 @@ class AmzoneApprove(AmamzonBase):
|
|||||||
retry_num = 0
|
retry_num = 0
|
||||||
already_asin = set()
|
already_asin = set()
|
||||||
now_page = None
|
now_page = None
|
||||||
while retry_num < 3: # 最多重试3次
|
max_retry_num = 5
|
||||||
|
while retry_num < max_retry_num: # 最多重试3次
|
||||||
# if num > 3: #测试
|
# if num > 3: #测试
|
||||||
# return
|
# return
|
||||||
# 等待加载完成
|
# 等待加载完成
|
||||||
@@ -505,8 +506,11 @@ class AmzoneApprove(AmamzonBase):
|
|||||||
sku_ls = self.tab.eles("xpath://div[@data-sku]",timeout=10)
|
sku_ls = self.tab.eles("xpath://div[@data-sku]",timeout=10)
|
||||||
print(f"获取到 {len(sku_ls)}")
|
print(f"获取到 {len(sku_ls)}")
|
||||||
if len(sku_ls) == 0:
|
if len(sku_ls) == 0:
|
||||||
print(f"搜索不出SKU停止")
|
retry_num += 1
|
||||||
break
|
print(f"没有获取到 ASIN 商品,重试{retry_num}/{max_retry_num}")
|
||||||
|
self.tab.refresh()
|
||||||
|
self.tab.wait.doc_loaded(raise_err=False, timeout=120)
|
||||||
|
continue
|
||||||
# for sku_ele in sku_ls[0:2]:
|
# for sku_ele in sku_ls[0:2]:
|
||||||
for sku_ele in sku_ls[1:]:
|
for sku_ele in sku_ls[1:]:
|
||||||
# solve_problem = sku_ele.eles('xpath:.//kat-link[@label="解决商品信息问题"]')
|
# solve_problem = sku_ele.eles('xpath:.//kat-link[@label="解决商品信息问题"]')
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -28,7 +28,7 @@ class RepricingLogic:
|
|||||||
if is_first_time:
|
if is_first_time:
|
||||||
return rank1_price - 0.3
|
return rank1_price - 0.3
|
||||||
|
|
||||||
diff = abs(my_price - rank1_price)
|
diff = my_price - rank1_price
|
||||||
|
|
||||||
# 精简了原图中长串的阶梯逻辑
|
# 精简了原图中长串的阶梯逻辑
|
||||||
if diff <= 0.3:
|
if diff <= 0.3:
|
||||||
@@ -47,7 +47,7 @@ class RepricingLogic:
|
|||||||
情况2:已经是自己购物车
|
情况2:已经是自己购物车
|
||||||
逻辑:判断与第二名的差价,如果拉开足够差距,则稍微降一点点(减0.5)保持优势,防止卖太便宜。
|
逻辑:判断与第二名的差价,如果拉开足够差距,则稍微降一点点(减0.5)保持优势,防止卖太便宜。
|
||||||
"""
|
"""
|
||||||
diff = abs(rank2_price - my_price)
|
diff = rank2_price - my_price
|
||||||
|
|
||||||
# 1. 绝对差价2欧以上
|
# 1. 绝对差价2欧以上
|
||||||
if diff >= 2.0:
|
if diff >= 2.0:
|
||||||
@@ -66,7 +66,7 @@ class RepricingLogic:
|
|||||||
"""
|
"""
|
||||||
情况3:不是自己购物车 (常规情况)
|
情况3:不是自己购物车 (常规情况)
|
||||||
"""
|
"""
|
||||||
diff = abs(my_price - rank1_price)
|
diff = my_price - rank1_price
|
||||||
|
|
||||||
if diff <= 0.3:
|
if diff <= 0.3:
|
||||||
return rank1_price - 0.5
|
return rank1_price - 0.5
|
||||||
@@ -84,7 +84,7 @@ class RepricingLogic:
|
|||||||
"""
|
"""
|
||||||
情况4:第一名是 Amazon US 卖家
|
情况4:第一名是 Amazon US 卖家
|
||||||
"""
|
"""
|
||||||
diff = abs(my_price - amz_us_price)
|
diff = my_price - amz_us_price
|
||||||
|
|
||||||
if diff <= 3:
|
if diff <= 3:
|
||||||
return amz_us_price - 2
|
return amz_us_price - 2
|
||||||
@@ -100,7 +100,7 @@ class RepricingLogic:
|
|||||||
"""
|
"""
|
||||||
情况5:自己是第一名,第二名是 Amazon US 卖家
|
情况5:自己是第一名,第二名是 Amazon US 卖家
|
||||||
"""
|
"""
|
||||||
diff = abs(amz_us_price - my_price)
|
diff = amz_us_price - my_price
|
||||||
|
|
||||||
if diff <= 7:
|
if diff <= 7:
|
||||||
return None # 相差5以内跳过 (保持原价)
|
return None # 相差5以内跳过 (保持原价)
|
||||||
@@ -127,7 +127,12 @@ def calculate_target_price(
|
|||||||
# --- Step 2: 紫鸟后台特殊判定 (最高优先级) ---
|
# --- Step 2: 紫鸟后台特殊判定 (最高优先级) ---
|
||||||
if recommended_shipping > 0:
|
if recommended_shipping > 0:
|
||||||
price_1 = float(front_end_data.get("top_sellers")[0].get("price")) # 实际第一名价格
|
price_1 = float(front_end_data.get("top_sellers")[0].get("price")) # 实际第一名价格
|
||||||
return RepricingLogic.situation_1_general_decrease(my_price,price_1,is_my_buybox)
|
if is_my_buybox:
|
||||||
|
price_col = price_1
|
||||||
|
else:
|
||||||
|
price_col = recommended_price + recommended_shipping
|
||||||
|
print(f"【紫鸟后台价格】{is_my_buybox} 推荐价格: {recommended_price},推荐运费: {recommended_shipping},总和: {price_col}")
|
||||||
|
return RepricingLogic.situation_1_general_decrease(my_price,price_col,is_my_buybox)
|
||||||
# backend_base_price = recommended_price
|
# backend_base_price = recommended_price
|
||||||
# backend_shipping = recommended_shipping
|
# backend_shipping = recommended_shipping
|
||||||
# total_price = backend_base_price + backend_shipping
|
# total_price = backend_base_price + backend_shipping
|
||||||
@@ -151,7 +156,7 @@ def calculate_target_price(
|
|||||||
# 分支 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.isituation_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,且目前是自己的购物车
|
||||||
@@ -619,6 +624,11 @@ class AmzonePriceMatch(AmamzonBase):
|
|||||||
page_pamel = self.tab.eles('xpath://kat-pagination', timeout=5)
|
page_pamel = self.tab.eles('xpath://kat-pagination', timeout=5)
|
||||||
if len(page_pamel) > 0:
|
if len(page_pamel) > 0:
|
||||||
current_page = page_pamel[0].sr('xpath:.//ul[@class="pages"]//li[@aria-current="true"]').text
|
current_page = page_pamel[0].sr('xpath:.//ul[@class="pages"]//li[@aria-current="true"]').text
|
||||||
|
#测试=====
|
||||||
|
# if int(current_page) > 3:
|
||||||
|
# break
|
||||||
|
# 测试===========
|
||||||
|
|
||||||
# yield (None,{"page":int(current_page.strip())})
|
# yield (None,{"page":int(current_page.strip())})
|
||||||
# 总页数
|
# 总页数
|
||||||
total_page = page_pamel[0].sr.eles(
|
total_page = page_pamel[0].sr.eles(
|
||||||
@@ -653,9 +663,9 @@ class AmzonePriceMatch(AmamzonBase):
|
|||||||
'xpath:.//div[contains(@class,"JanusSplitBox-module__container")]//div[contains(@class,"JanusSplitBox-module__panel--") and contains(string(.),"ASIN")]/..//div[last()]',
|
'xpath:.//div[contains(@class,"JanusSplitBox-module__container")]//div[contains(@class,"JanusSplitBox-module__panel--") and contains(string(.),"ASIN")]/..//div[last()]',
|
||||||
timeout=3).text
|
timeout=3).text
|
||||||
print(f"【{self.mark_name}】ASIN {asin} 找到....")
|
print(f"【{self.mark_name}】ASIN {asin} 找到....")
|
||||||
if asin in already_asin:
|
# if asin in already_asin:
|
||||||
print(f"【{self.mark_name}】{asin} 已经处理过了,跳过")
|
# print(f"【{self.mark_name}】{asin} 已经处理过了,跳过")
|
||||||
continue
|
# continue
|
||||||
if asin in skip_asin:
|
if asin in skip_asin:
|
||||||
yield (asin, {
|
yield (asin, {
|
||||||
"statu": "跳过,需要跳过的ASIN"
|
"statu": "跳过,需要跳过的ASIN"
|
||||||
@@ -1391,11 +1401,11 @@ class PriceTask:
|
|||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
# 使用示例
|
# 使用示例
|
||||||
# user_info = {
|
# user_info = {
|
||||||
# "company": "尾号5578的公司115",
|
# "company": "rongchuang123",
|
||||||
# "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)
|
||||||
@@ -1403,8 +1413,8 @@ if __name__ == '__main__':
|
|||||||
# sw_suc = driver.SwitchingCountries(country)
|
# sw_suc = driver.SwitchingCountries(country)
|
||||||
# driver.SwitchPage()
|
# driver.SwitchPage()
|
||||||
# risk_listing_filter = "Active"
|
# risk_listing_filter = "Active"
|
||||||
# _shopMallName = "Jianhuang 888"
|
# _shopMallName = "yafang123"
|
||||||
# ap_asin ="B0BGHRP6BS"
|
# ap_asin ="B0BTHBJDKG"
|
||||||
# skip_asin = []
|
# skip_asin = []
|
||||||
# for _ in range(3):
|
# for _ in range(3):
|
||||||
# try:
|
# try:
|
||||||
@@ -1422,12 +1432,14 @@ if __name__ == '__main__':
|
|||||||
# print(f"ASIN {asin} 的处理结果: {status}")
|
# print(f"ASIN {asin} 的处理结果: {status}")
|
||||||
# print("已完成操作")
|
# print("已完成操作")
|
||||||
|
|
||||||
front_end_data = {"top_sellers": [{"rank": 1, "price": "36.81", "stock": "5", "shop_name": "Windera"}, {"rank": 2, "price": "36.50", "stock": "100", "shop_name": "Jianhuang 888"}], "cart_seller": "Windera", "timestamp": "2026-04-25 09:32:58"}
|
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'}
|
||||||
current_Price = 36.50
|
|
||||||
current_shop_name = "Jianhuang 888"
|
current_Price = 44.35
|
||||||
recommended_price = 36.81
|
current_shop_name = "yafang123"
|
||||||
recommended_shipping = 0
|
recommended_price = 44.35
|
||||||
calculate_target_price(
|
recommended_shipping =0.0
|
||||||
|
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)
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -2,7 +2,7 @@
|
|||||||
主页面蓝图:首页、home、图片工作台、品牌页、静态文件、Logo
|
主页面蓝图:首页、home、图片工作台、品牌页、静态文件、Logo
|
||||||
"""
|
"""
|
||||||
import os
|
import os
|
||||||
from flask import Blueprint, send_file, render_template_string
|
from flask import Blueprint, send_file
|
||||||
|
|
||||||
from app_common import (
|
from app_common import (
|
||||||
get_db,
|
get_db,
|
||||||
@@ -23,38 +23,6 @@ from config import base_url,version,JAVA_API_BASE
|
|||||||
main_bp = Blueprint('main', __name__)
|
main_bp = Blueprint('main', __name__)
|
||||||
|
|
||||||
|
|
||||||
def _resolve_asset_filename(filename):
|
|
||||||
"""Resolve hashed Vite assets without maintaining hardcoded alias tables."""
|
|
||||||
safe_name = os.path.basename(filename)
|
|
||||||
exact_path = os.path.join(ASSETS_DIR, safe_name)
|
|
||||||
if os.path.isfile(exact_path):
|
|
||||||
return safe_name
|
|
||||||
|
|
||||||
base_name, ext = os.path.splitext(safe_name)
|
|
||||||
if not base_name or not ext:
|
|
||||||
return safe_name
|
|
||||||
|
|
||||||
parts = base_name.split('-')
|
|
||||||
if len(parts) < 2:
|
|
||||||
return safe_name
|
|
||||||
|
|
||||||
try:
|
|
||||||
asset_names = os.listdir(ASSETS_DIR)
|
|
||||||
except OSError:
|
|
||||||
return safe_name
|
|
||||||
|
|
||||||
for prefix_length in range(len(parts) - 1, 0, -1):
|
|
||||||
prefix = '-'.join(parts[:prefix_length])
|
|
||||||
matches = [
|
|
||||||
asset_name for asset_name in asset_names
|
|
||||||
if asset_name.endswith(ext) and asset_name.startswith(prefix)
|
|
||||||
]
|
|
||||||
if len(matches) == 1:
|
|
||||||
return matches[0]
|
|
||||||
|
|
||||||
return safe_name
|
|
||||||
|
|
||||||
|
|
||||||
@main_bp.route('/')
|
@main_bp.route('/')
|
||||||
def index():
|
def index():
|
||||||
if session.get('user_id') and _is_session_user_valid():
|
if session.get('user_id') and _is_session_user_valid():
|
||||||
@@ -85,41 +53,9 @@ def wb():
|
|||||||
@main_bp.route('/brand')
|
@main_bp.route('/brand')
|
||||||
@login_required
|
@login_required
|
||||||
def brand_page():
|
def brand_page():
|
||||||
repo_brand_path = os.path.abspath(os.path.join(BASE_DIR, '..', 'web_source', 'brand.html'))
|
|
||||||
if os.path.isfile(repo_brand_path):
|
|
||||||
try:
|
|
||||||
with open(repo_brand_path, 'rb') as f:
|
|
||||||
raw = f.read()
|
|
||||||
try:
|
|
||||||
from html_crypto import decrypt
|
|
||||||
content = decrypt(raw).decode('utf-8')
|
|
||||||
except Exception:
|
|
||||||
if raw[:7] == b'gAAAAAB':
|
|
||||||
raise
|
|
||||||
content = raw.decode('utf-8', errors='replace')
|
|
||||||
return render_template_string(content, user_id=session.get('user_id'))
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
return _render_html('brand.html', user_id=session.get('user_id'))
|
return _render_html('brand.html', user_id=session.get('user_id'))
|
||||||
|
|
||||||
|
|
||||||
@main_bp.route('/brand/legacy')
|
|
||||||
@login_required
|
|
||||||
def brand_page_legacy():
|
|
||||||
legacy_path = os.path.join(BASE_DIR, 'web_source', 'templates_backup', 'brand.html')
|
|
||||||
if not os.path.isfile(legacy_path):
|
|
||||||
return '', 404
|
|
||||||
with open(legacy_path, 'rb') as f:
|
|
||||||
content = f.read().decode('utf-8', errors='replace')
|
|
||||||
content = content.replace(
|
|
||||||
'<header class="top-bar">',
|
|
||||||
'<header class="top-bar" style="display:none !important;">',
|
|
||||||
1,
|
|
||||||
)
|
|
||||||
content = content.replace('height: calc(100vh - 56px);', 'height: 100vh;', 1)
|
|
||||||
return render_template_string(content, user_id=session.get('user_id'))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@main_bp.route('/static/<path:filename>')
|
@main_bp.route('/static/<path:filename>')
|
||||||
@@ -136,7 +72,6 @@ def serve_static(filename):
|
|||||||
@main_bp.route('/assets/<path:filename>')
|
@main_bp.route('/assets/<path:filename>')
|
||||||
def serve_assets(filename):
|
def serve_assets(filename):
|
||||||
"""提供 static 目录及子目录下的静态文件访问。"""
|
"""提供 static 目录及子目录下的静态文件访问。"""
|
||||||
filename = _resolve_asset_filename(filename)
|
|
||||||
filepath = os.path.normpath(os.path.join(ASSETS_DIR, filename))
|
filepath = os.path.normpath(os.path.join(ASSETS_DIR, filename))
|
||||||
static_abs = os.path.abspath(ASSETS_DIR)
|
static_abs = os.path.abspath(ASSETS_DIR)
|
||||||
file_abs = os.path.abspath(filepath)
|
file_abs = os.path.abspath(filepath)
|
||||||
@@ -147,19 +82,14 @@ def serve_assets(filename):
|
|||||||
|
|
||||||
@main_bp.route('/new_web_source/<path:filename>')
|
@main_bp.route('/new_web_source/<path:filename>')
|
||||||
def serve_new_web_source(filename):
|
def serve_new_web_source(filename):
|
||||||
"""提供 new_web_source 目录下的静态页面文件访问。"""
|
"""提供 static 目录及子目录下的静态文件访问。"""
|
||||||
candidate_dirs = [
|
filepath = os.path.normpath(os.path.join("new_web_source", filename))
|
||||||
os.path.abspath(os.path.join(BASE_DIR, 'new_web_source')),
|
static_abs = os.path.abspath("new_web_source")
|
||||||
os.path.abspath(os.path.join(BASE_DIR, '..', 'new_web_source')),
|
|
||||||
]
|
|
||||||
for static_abs in candidate_dirs:
|
|
||||||
filepath = os.path.normpath(os.path.join(static_abs, filename))
|
|
||||||
file_abs = os.path.abspath(filepath)
|
file_abs = os.path.abspath(filepath)
|
||||||
if not file_abs.startswith(static_abs):
|
if not file_abs.startswith(static_abs) or not os.path.isfile(file_abs):
|
||||||
continue
|
|
||||||
if os.path.isfile(file_abs):
|
|
||||||
return send_file(file_abs, as_attachment=False)
|
|
||||||
return '', 404
|
return '', 404
|
||||||
|
return send_file(file_abs, as_attachment=False)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@main_bp.route('/logo.jpg', methods=['GET'])
|
@main_bp.route('/logo.jpg', methods=['GET'])
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user