|
|
@@ -8,12 +8,13 @@ import uuid
|
|
|
import time
|
|
|
import os
|
|
|
import configparser
|
|
|
+import re
|
|
|
|
|
|
class PhoneQueryApp:
|
|
|
def __init__(self, root):
|
|
|
self.root = root
|
|
|
self.root.title("手机数据查询系统")
|
|
|
- self.root.geometry("1300x850")
|
|
|
+ self.root.geometry("1300x920") # 增加高度以容纳新控件
|
|
|
|
|
|
# 配置文件
|
|
|
self.config_file = "config.ini"
|
|
|
@@ -28,6 +29,15 @@ class PhoneQueryApp:
|
|
|
self.current_keyword = ""
|
|
|
self.current_sort = "sort_composite" # 默认综合排序
|
|
|
|
|
|
+ # 自动检测相关变量
|
|
|
+ self.auto_detect_running = False
|
|
|
+ self.auto_detect_stop = False
|
|
|
+ self.auto_detect_page = 0
|
|
|
+ self.auto_detect_products = []
|
|
|
+ self.auto_detect_thread = None
|
|
|
+ self.auto_collect_count = 0
|
|
|
+ self.auto_check_count = 0
|
|
|
+
|
|
|
# 排序选项映射
|
|
|
self.sort_map = {
|
|
|
"综合排序": "sort_composite",
|
|
|
@@ -127,7 +137,7 @@ class PhoneQueryApp:
|
|
|
self.root.columnconfigure(0, weight=1)
|
|
|
self.root.rowconfigure(0, weight=1)
|
|
|
main_frame.columnconfigure(0, weight=1)
|
|
|
- main_frame.rowconfigure(5, weight=1)
|
|
|
+ main_frame.rowconfigure(6, weight=1) # 调整行索引
|
|
|
|
|
|
# Token设置区域
|
|
|
token_frame = ttk.LabelFrame(main_frame, text="Token设置", padding="10")
|
|
|
@@ -232,9 +242,39 @@ class PhoneQueryApp:
|
|
|
cb = ttk.Checkbutton(condition_frame, text=tag_name, variable=var)
|
|
|
cb.grid(row=row, column=col, sticky=tk.W, padx=10, pady=3)
|
|
|
|
|
|
+ # ==================== 自动检测区域 ====================
|
|
|
+ auto_detect_frame = ttk.LabelFrame(main_frame, text="自动检测与收藏", padding="10")
|
|
|
+ auto_detect_frame.grid(row=4, column=0, sticky=(tk.W, tk.E), pady=(0, 10))
|
|
|
+ auto_detect_frame.columnconfigure(2, weight=1)
|
|
|
+
|
|
|
+ # 循环次数阈值设置
|
|
|
+ ttk.Label(auto_detect_frame, text="循环次数少于:", font=("Arial", 10)).grid(row=0, column=0, sticky=tk.W, padx=(0, 5))
|
|
|
+ self.cycle_threshold_var = tk.StringVar(value="100")
|
|
|
+ self.cycle_threshold_entry = ttk.Entry(auto_detect_frame, textvariable=self.cycle_threshold_var, width=10)
|
|
|
+ self.cycle_threshold_entry.grid(row=0, column=1, sticky=tk.W, padx=(0, 10))
|
|
|
+ ttk.Label(auto_detect_frame, text="次", font=("Arial", 10)).grid(row=0, column=2, sticky=tk.W, padx=(0, 20))
|
|
|
+
|
|
|
+ # 启动按钮
|
|
|
+ self.start_auto_btn = ttk.Button(auto_detect_frame, text="🚀 启动自动检测", command=self.start_auto_detect, width=18)
|
|
|
+ self.start_auto_btn.grid(row=0, column=3, padx=(0, 10))
|
|
|
+
|
|
|
+ # 停止按钮
|
|
|
+ self.stop_auto_btn = ttk.Button(auto_detect_frame, text="⏹ 停止", command=self.stop_auto_detect, width=12, state="disabled")
|
|
|
+ self.stop_auto_btn.grid(row=0, column=4, padx=(0, 20))
|
|
|
+
|
|
|
+ # 自动检测状态显示(使用Label而不是StringVar)
|
|
|
+ self.auto_status_label = ttk.Label(auto_detect_frame, text="自动检测未启动", font=("Arial", 9))
|
|
|
+ self.auto_status_label.grid(row=0, column=5, sticky=tk.W)
|
|
|
+
|
|
|
+ # 统计信息
|
|
|
+ self.auto_stats_var = tk.StringVar()
|
|
|
+ self.auto_stats_var.set("检测: 0 | 收藏: 0")
|
|
|
+ stats_label = ttk.Label(auto_detect_frame, textvariable=self.auto_stats_var, font=("Arial", 9))
|
|
|
+ stats_label.grid(row=0, column=6, sticky=tk.W, padx=(20, 0))
|
|
|
+
|
|
|
# ==================== 结果显示区域 ====================
|
|
|
result_frame = ttk.LabelFrame(main_frame, text="查询结果(可多选商品,点击批量收藏)", padding="10")
|
|
|
- result_frame.grid(row=4, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
|
|
|
+ result_frame.grid(row=5, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
|
|
|
result_frame.columnconfigure(0, weight=1)
|
|
|
result_frame.rowconfigure(0, weight=1)
|
|
|
|
|
|
@@ -267,7 +307,7 @@ class PhoneQueryApp:
|
|
|
|
|
|
# ==================== 底部按钮栏 ====================
|
|
|
bottom_frame = ttk.Frame(main_frame)
|
|
|
- bottom_frame.grid(row=5, column=0, sticky=(tk.W, tk.E), pady=(10, 5))
|
|
|
+ bottom_frame.grid(row=6, column=0, sticky=(tk.W, tk.E), pady=(10, 5))
|
|
|
|
|
|
self.check_price_btn = ttk.Button(bottom_frame, text="获取循环次数", command=self.check_prices, width=18)
|
|
|
self.check_price_btn.pack(side=tk.LEFT, padx=(0, 10))
|
|
|
@@ -281,7 +321,7 @@ class PhoneQueryApp:
|
|
|
|
|
|
# ==================== 分页栏 ====================
|
|
|
pagination_frame = ttk.Frame(main_frame)
|
|
|
- pagination_frame.grid(row=6, column=0, sticky=(tk.W, tk.E), pady=(0, 5))
|
|
|
+ pagination_frame.grid(row=7, column=0, sticky=(tk.W, tk.E), pady=(0, 5))
|
|
|
|
|
|
self.prev_btn = ttk.Button(pagination_frame, text="上一页", command=self.prev_page, width=10)
|
|
|
self.prev_btn.pack(side=tk.LEFT, padx=(0, 10))
|
|
|
@@ -304,7 +344,7 @@ class PhoneQueryApp:
|
|
|
|
|
|
# ==================== 状态栏 ====================
|
|
|
status_frame = ttk.Frame(main_frame)
|
|
|
- status_frame.grid(row=7, column=0, sticky=(tk.W, tk.E), pady=(5, 0))
|
|
|
+ status_frame.grid(row=8, column=0, sticky=(tk.W, tk.E), pady=(5, 0))
|
|
|
|
|
|
self.status_var = tk.StringVar()
|
|
|
self.status_var.set("就绪")
|
|
|
@@ -740,8 +780,264 @@ class PhoneQueryApp:
|
|
|
self.status_var.set(f"✗ 收藏失败: {msg}")
|
|
|
messagebox.showerror("收藏失败", f"收藏失败\n{product['info']['name']}\n错误: {msg}")
|
|
|
return
|
|
|
+
|
|
|
+ # ==================== 自动检测功能 ====================
|
|
|
+ def start_auto_detect(self):
|
|
|
+ """启动自动检测"""
|
|
|
+ # 检查是否已启动
|
|
|
+ if self.auto_detect_running:
|
|
|
+ messagebox.showinfo("提示", "自动检测已在运行中")
|
|
|
+ return
|
|
|
+
|
|
|
+ # 检查是否有搜索关键词
|
|
|
+ keyword = self.model_entry.get().strip()
|
|
|
+ if not keyword:
|
|
|
+ messagebox.showwarning("警告", "请先输入要搜索的机型")
|
|
|
+ return
|
|
|
+
|
|
|
+ # 获取阈值
|
|
|
+ try:
|
|
|
+ threshold = int(self.cycle_threshold_var.get().strip())
|
|
|
+ if threshold < 0:
|
|
|
+ raise ValueError("阈值不能为负数")
|
|
|
+ except ValueError:
|
|
|
+ messagebox.showerror("错误", "请输入有效的循环次数阈值(正整数)")
|
|
|
+ return
|
|
|
+
|
|
|
+ # 确认启动
|
|
|
+ result = messagebox.askyesno("确认启动",
|
|
|
+ f"将自动检测所有符合条件的商品\n"
|
|
|
+ f"机型: {keyword}\n"
|
|
|
+ f"循环次数阈值: {threshold} 次\n\n"
|
|
|
+ f"检测到的商品将自动收藏,是否继续?")
|
|
|
+ if not result:
|
|
|
+ return
|
|
|
+
|
|
|
+ # 重置状态
|
|
|
+ self.auto_detect_running = True
|
|
|
+ self.auto_detect_stop = False
|
|
|
+ self.auto_detect_page = 0
|
|
|
+ self.auto_detect_products = []
|
|
|
+ self.auto_collect_count = 0
|
|
|
+ self.auto_check_count = 0
|
|
|
+
|
|
|
+ # 更新UI状态
|
|
|
+ self.start_auto_btn.config(state="disabled")
|
|
|
+ self.stop_auto_btn.config(state="normal")
|
|
|
+ self.auto_status_label.config(text="🔄 正在检测中...", foreground="blue")
|
|
|
+ self.auto_stats_var.set("检测: 0 | 收藏: 0")
|
|
|
+
|
|
|
+ # 清空当前显示
|
|
|
+ for item in self.result_tree.get_children():
|
|
|
+ self.result_tree.delete(item)
|
|
|
+ self.current_products.clear()
|
|
|
+
|
|
|
+ # 启动检测线程
|
|
|
+ self.auto_detect_thread = threading.Thread(target=self.auto_detect_loop, args=(keyword, threshold))
|
|
|
+ self.auto_detect_thread.daemon = True
|
|
|
+ self.auto_detect_thread.start()
|
|
|
+
|
|
|
+ def stop_auto_detect(self):
|
|
|
+ """停止自动检测"""
|
|
|
+ if self.auto_detect_running:
|
|
|
+ self.auto_detect_stop = True
|
|
|
+ self.auto_status_label.config(text="⏹ 正在停止...", foreground="orange")
|
|
|
+ self.stop_auto_btn.config(state="disabled")
|
|
|
+
|
|
|
+ def auto_detect_loop(self, keyword, threshold):
|
|
|
+ """自动检测循环"""
|
|
|
+ page = 0
|
|
|
+ total_collected = 0
|
|
|
+ total_checked = 0
|
|
|
+
|
|
|
+ while not self.auto_detect_stop:
|
|
|
+ try:
|
|
|
+ # 搜索商品
|
|
|
+ self.root.after(0, lambda: self.auto_status_label.config(
|
|
|
+ text=f"🔄 正在检测第 {page + 1} 页...", foreground="blue"))
|
|
|
+
|
|
|
+ # 执行搜索
|
|
|
+ memory_ids = self.get_selected_memory()
|
|
|
+ battery_ids = self.get_selected_battery()
|
|
|
+ fineness_ids = self.get_selected_fineness()
|
|
|
+ condition_tags = self.get_selected_condition_tags()
|
|
|
+
|
|
|
+ request_data = {
|
|
|
+ "sortValue": "sort_composite",
|
|
|
+ "scene": "search",
|
|
|
+ "keyword": keyword,
|
|
|
+ "sirReq": False,
|
|
|
+ "cityId": 324,
|
|
|
+ "pageIndex": page,
|
|
|
+ "pageSize": self.page_size
|
|
|
+ }
|
|
|
+
|
|
|
+ if memory_ids:
|
|
|
+ request_data["gaeaSkuPpvIds"] = {"22": memory_ids}
|
|
|
+ if battery_ids:
|
|
|
+ request_data["gaeaPricePpvIds"] = {"473": battery_ids}
|
|
|
+ if fineness_ids:
|
|
|
+ request_data["gaeaFinenessIds"] = fineness_ids
|
|
|
+ if condition_tags:
|
|
|
+ request_data["conditionTagList"] = condition_tags
|
|
|
+
|
|
|
+ url = "https://dubai-mp.aihuishou.com/ahs-yanxuan-service/products/search-goods-v2"
|
|
|
+ response = requests.post(url, json=request_data, headers=self.get_headers(), timeout=30)
|
|
|
+ result = response.json()
|
|
|
+
|
|
|
+ if result.get('code') != 0:
|
|
|
+ self.root.after(0, lambda: self.show_error(f"搜索失败: {result.get('resultMessage', '未知错误')}"))
|
|
|
+ break
|
|
|
+
|
|
|
+ data = result.get('data', [])
|
|
|
+ total_count = result.get('totalCount', 0)
|
|
|
+
|
|
|
+ # 计算总页数
|
|
|
+ if total_count > 0:
|
|
|
+ total_pages = (total_count + self.page_size - 1) // self.page_size
|
|
|
+ else:
|
|
|
+ total_pages = page + 1 if len(data) < self.page_size else page + 2
|
|
|
+
|
|
|
+ # 如果没有数据,结束
|
|
|
+ if not data:
|
|
|
+ self.root.after(0, lambda: self.auto_status_label.config(
|
|
|
+ text="✅ 检测完成,没有更多商品", foreground="green"))
|
|
|
+ break
|
|
|
+
|
|
|
+ # 处理当前页商品
|
|
|
+ page_collected = 0
|
|
|
+ for product in data:
|
|
|
+ if self.auto_detect_stop:
|
|
|
+ break
|
|
|
+
|
|
|
+ total_checked += 1
|
|
|
+
|
|
|
+ # 获取商品信息
|
|
|
+ sale_goods_no = product.get('saleGoodsNo', '')
|
|
|
+ product_no = product.get('productNo', '')
|
|
|
+ name = product.get('name', '')
|
|
|
+
|
|
|
+ # 获取循环次数
|
|
|
+ cycle_count = self.get_cycle_count(sale_goods_no)
|
|
|
+
|
|
|
+ # 解析循环次数为数字
|
|
|
+ cycle_num = 0
|
|
|
+ if cycle_count:
|
|
|
+ try:
|
|
|
+ # 尝试提取数字
|
|
|
+ numbers = re.findall(r'\d+', cycle_count)
|
|
|
+ if numbers:
|
|
|
+ cycle_num = int(numbers[0])
|
|
|
+ except:
|
|
|
+ pass
|
|
|
+
|
|
|
+ # 判断是否满足条件
|
|
|
+ if cycle_num > 0 and cycle_num <= threshold:
|
|
|
+ # 满足条件,收藏
|
|
|
+ item_no = product_no or sale_goods_no
|
|
|
+ success, msg = self.add_to_collection(item_no)
|
|
|
+
|
|
|
+ if success:
|
|
|
+ total_collected += 1
|
|
|
+ page_collected += 1
|
|
|
+ # 在列表中显示
|
|
|
+ self.root.after(0, lambda p=product, c=cycle_count: self.display_auto_collected(p, c))
|
|
|
+ else:
|
|
|
+ # 收藏失败,记录日志
|
|
|
+ print(f"自动收藏失败: {name} - {msg}")
|
|
|
+
|
|
|
+ # 更新统计
|
|
|
+ self.root.after(0, lambda tc=total_checked, tl=total_collected:
|
|
|
+ self.auto_stats_var.set(f"检测: {tc} | 收藏: {tl}"))
|
|
|
+
|
|
|
+ # 延时避免请求过快
|
|
|
+ time.sleep(0.2)
|
|
|
+
|
|
|
+ # 更新页面信息
|
|
|
+ self.root.after(0, lambda p=page+1, pc=page_collected, tc=total_checked, tl=total_collected:
|
|
|
+ self.auto_status_label.config(
|
|
|
+ text=f"🔄 第 {p} 页完成,本页收藏 {pc} 个", foreground="blue"))
|
|
|
+
|
|
|
+ # 检查是否到达最后一页
|
|
|
+ if page >= total_pages - 1 or len(data) < self.page_size:
|
|
|
+ self.root.after(0, lambda: self.auto_status_label.config(
|
|
|
+ text="✅ 检测完成!已检测所有页面", foreground="green"))
|
|
|
+ break
|
|
|
+
|
|
|
+ page += 1
|
|
|
+
|
|
|
+ # 检查是否被停止
|
|
|
+ if self.auto_detect_stop:
|
|
|
+ break
|
|
|
+
|
|
|
+ # 页面间延时
|
|
|
+ time.sleep(0.5)
|
|
|
+
|
|
|
+ except Exception as e:
|
|
|
+ self.root.after(0, lambda: self.show_error(f"自动检测出错: {e}"))
|
|
|
+ break
|
|
|
+
|
|
|
+ # 完成或停止
|
|
|
+ self.root.after(0, self.finish_auto_detect, total_checked, total_collected)
|
|
|
+
|
|
|
+ def display_auto_collected(self, product, cycle_count):
|
|
|
+ """显示自动收藏的商品(高亮标记)"""
|
|
|
+ info = self.parse_product_info(product)
|
|
|
+ item_id = self.result_tree.insert("", tk.END, values=(
|
|
|
+ info['name'],
|
|
|
+ f"¥{info['price']}",
|
|
|
+ info['battery_efficiency'],
|
|
|
+ cycle_count,
|
|
|
+ info['fineness'],
|
|
|
+ info['memory'],
|
|
|
+ info['color'],
|
|
|
+ info['network']
|
|
|
+ ))
|
|
|
+
|
|
|
+ # 标记为已收藏(绿色背景)
|
|
|
+ self.result_tree.tag_configure('collected', background='#90EE90')
|
|
|
+ self.result_tree.item(item_id, tags=('collected',))
|
|
|
+
|
|
|
+ # 保存到当前商品列表
|
|
|
+ self.current_products.append({
|
|
|
+ 'item_id': item_id,
|
|
|
+ 'saleGoodsNo': info['saleGoodsNo'],
|
|
|
+ 'productNo': info['productNo'],
|
|
|
+ 'info': info
|
|
|
+ })
|
|
|
+
|
|
|
+ def finish_auto_detect(self, total_checked, total_collected):
|
|
|
+ """完成自动检测"""
|
|
|
+ self.auto_detect_running = False
|
|
|
+ self.start_auto_btn.config(state="normal")
|
|
|
+ self.stop_auto_btn.config(state="disabled")
|
|
|
+
|
|
|
+ if self.auto_detect_stop:
|
|
|
+ self.auto_status_label.config(
|
|
|
+ text=f"⏹ 已停止 - 检测: {total_checked} | 收藏: {total_collected}",
|
|
|
+ foreground="orange")
|
|
|
+ else:
|
|
|
+ self.auto_status_label.config(
|
|
|
+ text=f"✅ 完成 - 检测: {total_checked} | 收藏: {total_collected}",
|
|
|
+ foreground="green")
|
|
|
+
|
|
|
+ self.auto_stats_var.set(f"检测: {total_checked} | 收藏: {total_collected}")
|
|
|
+
|
|
|
+ # 显示完成消息
|
|
|
+ if total_collected > 0:
|
|
|
+ messagebox.showinfo("自动检测完成",
|
|
|
+ f"检测完成!\n\n"
|
|
|
+ f"共检测商品: {total_checked} 个\n"
|
|
|
+ f"成功收藏: {total_collected} 个")
|
|
|
+ else:
|
|
|
+ if not self.auto_detect_stop:
|
|
|
+ messagebox.showinfo("自动检测完成",
|
|
|
+ f"检测完成!\n\n"
|
|
|
+ f"共检测商品: {total_checked} 个\n"
|
|
|
+ f"没有找到符合条件的商品")
|
|
|
|
|
|
def show_error(self, error_msg):
|
|
|
+ """显示错误信息"""
|
|
|
self.search_btn.config(state="normal")
|
|
|
self.jump_btn.config(state="normal")
|
|
|
self.status_var.set(f"查询失败: {error_msg}")
|