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://127.0.0.1:18080
|
||||
# java_api_base=http://8.136.19.173:18080
|
||||
# java_api_base=http://127.0.0.1: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
|
||||
already_asin = set()
|
||||
now_page = None
|
||||
while retry_num < 3: # 最多重试3次
|
||||
max_retry_num = 5
|
||||
while retry_num < max_retry_num: # 最多重试3次
|
||||
# if num > 3: #测试
|
||||
# return
|
||||
# 等待加载完成
|
||||
@@ -505,8 +506,11 @@ class AmzoneApprove(AmamzonBase):
|
||||
sku_ls = self.tab.eles("xpath://div[@data-sku]",timeout=10)
|
||||
print(f"获取到 {len(sku_ls)}")
|
||||
if len(sku_ls) == 0:
|
||||
print(f"搜索不出SKU停止")
|
||||
break
|
||||
retry_num += 1
|
||||
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[1:]:
|
||||
# solve_problem = sku_ele.eles('xpath:.//kat-link[@label="解决商品信息问题"]')
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -18,7 +18,7 @@ from config import runing_task, runing_shop,base_dir,DELETE_BRAND_API_BASE
|
||||
|
||||
|
||||
class RepricingLogic:
|
||||
|
||||
|
||||
@staticmethod
|
||||
def situation_1_general_decrease(my_price, rank1_price, is_first_time=False):
|
||||
"""
|
||||
@@ -27,9 +27,9 @@ class RepricingLogic:
|
||||
"""
|
||||
if is_first_time:
|
||||
return rank1_price - 0.3
|
||||
|
||||
diff = abs(my_price - rank1_price)
|
||||
|
||||
|
||||
diff = my_price - rank1_price
|
||||
|
||||
# 精简了原图中长串的阶梯逻辑
|
||||
if diff <= 0.3:
|
||||
return rank1_price - 0.5
|
||||
@@ -47,63 +47,63 @@ class RepricingLogic:
|
||||
情况2:已经是自己购物车
|
||||
逻辑:判断与第二名的差价,如果拉开足够差距,则稍微降一点点(减0.5)保持优势,防止卖太便宜。
|
||||
"""
|
||||
diff = abs(rank2_price - my_price)
|
||||
|
||||
diff = rank2_price - my_price
|
||||
|
||||
# 1. 绝对差价2欧以上
|
||||
if diff >= 2.0:
|
||||
return rank2_price - 0.3
|
||||
|
||||
|
||||
# 2. 产品售价区间判定 (是否要提高价格)
|
||||
if (20 <= my_price < 30 and diff >= 4) or \
|
||||
(30 <= my_price < 60 and diff >= 8) or \
|
||||
(60 <= my_price <= 150 and diff >= 15):
|
||||
(30 <= my_price < 60 and diff >= 8) or \
|
||||
(60 <= my_price <= 150 and diff >= 15):
|
||||
return rank2_price - 0.3 # 满足区间大差价,提价至第二名之下
|
||||
|
||||
return my_price # 不满足条件则保持原价
|
||||
|
||||
return my_price # 不满足条件则保持原价
|
||||
|
||||
@staticmethod
|
||||
def situation_3_not_own_buybox(my_price, rank1_price):
|
||||
"""
|
||||
情况3:不是自己购物车 (常规情况)
|
||||
"""
|
||||
diff = abs(my_price - rank1_price)
|
||||
|
||||
diff = my_price - rank1_price
|
||||
|
||||
if diff <= 0.3:
|
||||
return rank1_price - 0.5
|
||||
elif 0.5 <= diff <= 0.8:
|
||||
return rank1_price - 0.7
|
||||
elif 0.8 < diff <= 2.5:
|
||||
return rank1_price - 1.0
|
||||
elif diff > 2.5: # 2.5-3.5以上跳过
|
||||
return None
|
||||
|
||||
return rank1_price - 0.3 # 默认按第一名减0.3
|
||||
elif diff > 2.5: # 2.5-3.5以上跳过
|
||||
return None
|
||||
|
||||
return rank1_price - 0.3 # 默认按第一名减0.3
|
||||
|
||||
@staticmethod
|
||||
def situation_4_encounter_amz_us_1st(my_price, amz_us_price):
|
||||
"""
|
||||
情况4:第一名是 Amazon US 卖家
|
||||
"""
|
||||
diff = abs(my_price - amz_us_price)
|
||||
|
||||
diff = my_price - amz_us_price
|
||||
|
||||
if diff <= 3:
|
||||
return amz_us_price - 2
|
||||
elif 4 <= diff <= 6:
|
||||
return amz_us_price - 3
|
||||
elif 8 <= diff <= 12:
|
||||
return None # 跳过不跟价
|
||||
|
||||
return amz_us_price - 3 # 默认直接按照 Amazon US 减去3
|
||||
return None # 跳过不跟价
|
||||
|
||||
return amz_us_price - 3 # 默认直接按照 Amazon US 减去3
|
||||
|
||||
@staticmethod
|
||||
def situation_5_im_1st_amz_us_2nd(my_price, amz_us_price):
|
||||
"""
|
||||
情况5:自己是第一名,第二名是 Amazon US 卖家
|
||||
"""
|
||||
diff = abs(amz_us_price - my_price)
|
||||
|
||||
diff = amz_us_price - my_price
|
||||
|
||||
if diff <= 7:
|
||||
return None # 相差5以内跳过 (保持原价)
|
||||
return None # 相差5以内跳过 (保持原价)
|
||||
else:
|
||||
return amz_us_price - 5
|
||||
|
||||
@@ -127,7 +127,12 @@ def calculate_target_price(
|
||||
# --- Step 2: 紫鸟后台特殊判定 (最高优先级) ---
|
||||
if recommended_shipping > 0:
|
||||
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_shipping = recommended_shipping
|
||||
# total_price = backend_base_price + backend_shipping
|
||||
@@ -151,7 +156,7 @@ def calculate_target_price(
|
||||
# 分支 A:第一名是 Amazon US 卖家 (特殊强敌优先)
|
||||
price_1 = float(front_end_data.get("top_sellers")[0].get("price")) # 实际第一名价格
|
||||
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,且目前是自己的购物车
|
||||
@@ -619,6 +624,11 @@ class AmzonePriceMatch(AmamzonBase):
|
||||
page_pamel = self.tab.eles('xpath://kat-pagination', timeout=5)
|
||||
if len(page_pamel) > 0:
|
||||
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())})
|
||||
# 总页数
|
||||
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()]',
|
||||
timeout=3).text
|
||||
print(f"【{self.mark_name}】ASIN {asin} 找到....")
|
||||
if asin in already_asin:
|
||||
print(f"【{self.mark_name}】{asin} 已经处理过了,跳过")
|
||||
continue
|
||||
# if asin in already_asin:
|
||||
# print(f"【{self.mark_name}】{asin} 已经处理过了,跳过")
|
||||
# continue
|
||||
if asin in skip_asin:
|
||||
yield (asin, {
|
||||
"statu": "跳过,需要跳过的ASIN"
|
||||
@@ -1391,11 +1401,11 @@ class PriceTask:
|
||||
if __name__ == '__main__':
|
||||
# 使用示例
|
||||
# user_info = {
|
||||
# "company": "尾号5578的公司115",
|
||||
# "company": "rongchuang123",
|
||||
# "username": "自动化_Robot",
|
||||
# "password": "#20zsg25"
|
||||
# }
|
||||
# shop_name = "刘建煌"
|
||||
# shop_name = "郭亚芳"
|
||||
# country = "德国"
|
||||
# kill_process('v6')
|
||||
# driver = AmzonePriceMatch(user_info)
|
||||
@@ -1403,8 +1413,8 @@ if __name__ == '__main__':
|
||||
# sw_suc = driver.SwitchingCountries(country)
|
||||
# driver.SwitchPage()
|
||||
# risk_listing_filter = "Active"
|
||||
# _shopMallName = "Jianhuang 888"
|
||||
# ap_asin ="B0BGHRP6BS"
|
||||
# _shopMallName = "yafang123"
|
||||
# ap_asin ="B0BTHBJDKG"
|
||||
# skip_asin = []
|
||||
# for _ in range(3):
|
||||
# try:
|
||||
@@ -1422,12 +1432,14 @@ if __name__ == '__main__':
|
||||
# print(f"ASIN {asin} 的处理结果: {status}")
|
||||
# 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"}
|
||||
current_Price = 36.50
|
||||
current_shop_name = "Jianhuang 888"
|
||||
recommended_price = 36.81
|
||||
recommended_shipping = 0
|
||||
calculate_target_price(
|
||||
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 = 44.35
|
||||
current_shop_name = "yafang123"
|
||||
recommended_price = 44.35
|
||||
recommended_shipping =0.0
|
||||
res = calculate_target_price(
|
||||
front_end_data, current_Price, current_shop_name,
|
||||
recommended_price, recommended_shipping
|
||||
)
|
||||
)
|
||||
print(res)
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -2,7 +2,7 @@
|
||||
主页面蓝图:首页、home、图片工作台、品牌页、静态文件、Logo
|
||||
"""
|
||||
import os
|
||||
from flask import Blueprint, send_file, render_template_string
|
||||
from flask import Blueprint, send_file
|
||||
|
||||
from app_common import (
|
||||
get_db,
|
||||
@@ -20,39 +20,7 @@ import requests
|
||||
|
||||
from config import base_url,version,JAVA_API_BASE
|
||||
|
||||
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 = Blueprint('main', __name__)
|
||||
|
||||
|
||||
@main_bp.route('/')
|
||||
@@ -82,42 +50,10 @@ def wb():
|
||||
return _render_html('index.html')
|
||||
|
||||
|
||||
@main_bp.route('/brand')
|
||||
@login_required
|
||||
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'))
|
||||
|
||||
|
||||
@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('/brand')
|
||||
@login_required
|
||||
def brand_page():
|
||||
return _render_html('brand.html', user_id=session.get('user_id'))
|
||||
|
||||
|
||||
|
||||
@@ -133,11 +69,10 @@ def serve_static(filename):
|
||||
return send_file(file_abs, as_attachment=False)
|
||||
|
||||
|
||||
@main_bp.route('/assets/<path:filename>')
|
||||
def serve_assets(filename):
|
||||
"""提供 static 目录及子目录下的静态文件访问。"""
|
||||
filename = _resolve_asset_filename(filename)
|
||||
filepath = os.path.normpath(os.path.join(ASSETS_DIR, filename))
|
||||
@main_bp.route('/assets/<path:filename>')
|
||||
def serve_assets(filename):
|
||||
"""提供 static 目录及子目录下的静态文件访问。"""
|
||||
filepath = os.path.normpath(os.path.join(ASSETS_DIR, filename))
|
||||
static_abs = os.path.abspath(ASSETS_DIR)
|
||||
file_abs = os.path.abspath(filepath)
|
||||
if not file_abs.startswith(static_abs) or not os.path.isfile(file_abs):
|
||||
@@ -147,19 +82,14 @@ def serve_assets(filename):
|
||||
|
||||
@main_bp.route('/new_web_source/<path:filename>')
|
||||
def serve_new_web_source(filename):
|
||||
"""提供 new_web_source 目录下的静态页面文件访问。"""
|
||||
candidate_dirs = [
|
||||
os.path.abspath(os.path.join(BASE_DIR, '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)
|
||||
if not file_abs.startswith(static_abs):
|
||||
continue
|
||||
if os.path.isfile(file_abs):
|
||||
return send_file(file_abs, as_attachment=False)
|
||||
return '', 404
|
||||
"""提供 static 目录及子目录下的静态文件访问。"""
|
||||
filepath = os.path.normpath(os.path.join("new_web_source", filename))
|
||||
static_abs = os.path.abspath("new_web_source")
|
||||
file_abs = os.path.abspath(filepath)
|
||||
if not file_abs.startswith(static_abs) or not os.path.isfile(file_abs):
|
||||
return '', 404
|
||||
return send_file(file_abs, as_attachment=False)
|
||||
|
||||
|
||||
|
||||
@main_bp.route('/logo.jpg', methods=['GET'])
|
||||
@@ -231,4 +161,4 @@ def proxy(path):
|
||||
return Response("无法连接到后端服务", status=502)
|
||||
except Exception as e:
|
||||
print(f"代理请求失败: {e}")
|
||||
return Response(f"代理错误: {str(e)}", status=500)
|
||||
return Response(f"代理错误: {str(e)}", status=500)
|
||||
|
||||
Binary file not shown.
@@ -5,10 +5,10 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>格式转换 - 数富AI</title>
|
||||
<script type="module" crossorigin src="/assets/convert.js"></script>
|
||||
<link rel="modulepreload" crossorigin href="/assets/pywebview-Bt854mYs.js">
|
||||
<link rel="modulepreload" crossorigin href="/assets/brand-5-W0Wonr.js">
|
||||
<link rel="modulepreload" crossorigin href="/assets/pywebview-C66x_2Dh.js">
|
||||
<link rel="modulepreload" crossorigin href="/assets/brand-BZije8D7.js">
|
||||
<link rel="stylesheet" crossorigin href="/assets/pywebview-Dp5dN8OO.css">
|
||||
<link rel="stylesheet" crossorigin href="/assets/convert-7wWJ02Tw.css">
|
||||
<link rel="stylesheet" crossorigin href="/assets/convert-BKSNvX8i.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>数据去重 - 数富AI</title>
|
||||
<script type="module" crossorigin src="/assets/dedupe.js"></script>
|
||||
<link rel="modulepreload" crossorigin href="/assets/pywebview-Bt854mYs.js">
|
||||
<link rel="modulepreload" crossorigin href="/assets/brand-5-W0Wonr.js">
|
||||
<link rel="modulepreload" crossorigin href="/assets/pywebview-C66x_2Dh.js">
|
||||
<link rel="modulepreload" crossorigin href="/assets/brand-BZije8D7.js">
|
||||
<link rel="stylesheet" crossorigin href="/assets/pywebview-Dp5dN8OO.css">
|
||||
<link rel="stylesheet" crossorigin href="/assets/dedupe-BpNHwt51.css">
|
||||
<link rel="stylesheet" crossorigin href="/assets/dedupe-DNlVfFj-.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>删除品牌 - 数富AI</title>
|
||||
<script type="module" crossorigin src="/assets/delete-brand.js"></script>
|
||||
<link rel="modulepreload" crossorigin href="/assets/pywebview-Bt854mYs.js">
|
||||
<link rel="modulepreload" crossorigin href="/assets/brand-5-W0Wonr.js">
|
||||
<link rel="modulepreload" crossorigin href="/assets/pywebview-C66x_2Dh.js">
|
||||
<link rel="modulepreload" crossorigin href="/assets/brand-BZije8D7.js">
|
||||
<link rel="stylesheet" crossorigin href="/assets/pywebview-Dp5dN8OO.css">
|
||||
<link rel="stylesheet" crossorigin href="/assets/delete-brand-CWLpe7lu.css">
|
||||
<link rel="stylesheet" crossorigin href="/assets/delete-brand-BP3XWKAC.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>数据拆分 - 数富AI</title>
|
||||
<script type="module" crossorigin src="/assets/split.js"></script>
|
||||
<link rel="modulepreload" crossorigin href="/assets/pywebview-Bt854mYs.js">
|
||||
<link rel="modulepreload" crossorigin href="/assets/brand-5-W0Wonr.js">
|
||||
<link rel="modulepreload" crossorigin href="/assets/pywebview-C66x_2Dh.js">
|
||||
<link rel="modulepreload" crossorigin href="/assets/brand-BZije8D7.js">
|
||||
<link rel="stylesheet" crossorigin href="/assets/pywebview-Dp5dN8OO.css">
|
||||
<link rel="stylesheet" crossorigin href="/assets/split-CRUIYKS6.css">
|
||||
<link rel="stylesheet" crossorigin href="/assets/split-BoBVrLdC.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user