Jelajahi Sumber

Merge branch 'main' of https://git.port.run/puge/pythonTest

PUGE 1 bulan lalu
induk
melakukan
0978819d02
4 mengubah file dengan 757 tambahan dan 201 penghapusan
  1. 1 0
      .gitignore
  2. 420 0
      主播工具.py
  3. 336 11
      爱回收.py
  4. 0 190
      米画师.py

+ 1 - 0
.gitignore

@@ -1,3 +1,4 @@
 /build
 /dist
 *.spec
+user_scripts_backup.json

+ 420 - 0
主播工具.py

@@ -0,0 +1,420 @@
+import sys
+import json
+from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QHBoxLayout, 
+                             QVBoxLayout, QListWidget, QListWidgetItem, QCheckBox, 
+                             QPushButton, QLineEdit, QTextEdit, QGroupBox, 
+                             QSplitter, QLabel, QMessageBox, QComboBox, QShortcut)
+from PyQt5.QtCore import Qt
+from PyQt5.QtGui import QKeySequence
+
+class MainWindow(QMainWindow):
+    def __init__(self):
+        super().__init__()
+        self.setWindowTitle("三栏布局 - 用户独立话术管理 (快捷键F1-F5)")
+        self.setGeometry(100, 100, 1300, 750)
+        
+        # 数据存储:用户话术字典 {用户名: [话术列表]}
+        self.user_scripts = {}
+        # 当前选中的用户
+        self.current_user = None
+        
+        # 中央部件
+        central_widget = QWidget()
+        self.setCentralWidget(central_widget)
+        
+        # 主布局 - 水平分割器
+        main_splitter = QSplitter(Qt.Horizontal)
+        central_layout = QHBoxLayout(central_widget)
+        central_layout.addWidget(main_splitter)
+        central_layout.setContentsMargins(10, 10, 10, 10)
+        
+        # ---------- 左侧:日志显示 ----------
+        left_widget = QWidget()
+        left_layout = QVBoxLayout(left_widget)
+        left_layout.setContentsMargins(5, 5, 5, 5)
+        
+        log_label = QLabel("📋 运行日志")
+        log_label.setStyleSheet("font-weight: bold; font-size: 14px;")
+        left_layout.addWidget(log_label)
+        
+        self.log_text = QTextEdit()
+        self.log_text.setReadOnly(True)
+        self.log_text.setPlaceholderText("日志信息将显示在这里...")
+        self.log_text.append("[系统] 程序启动")
+        self.log_text.append("[系统] 界面初始化完成")
+        self.log_text.append("[系统] 快捷键: F1-F5 快速切换用户")
+        left_layout.addWidget(self.log_text)
+        
+        log_btn_layout = QHBoxLayout()
+        clear_log_btn = QPushButton("清空日志")
+        clear_log_btn.clicked.connect(self.clear_log)
+        log_btn_layout.addStretch()
+        log_btn_layout.addWidget(clear_log_btn)
+        left_layout.addLayout(log_btn_layout)
+        
+        # ---------- 中间:在线用户列表 ----------
+        middle_widget = QWidget()
+        middle_layout = QVBoxLayout(middle_widget)
+        middle_layout.setContentsMargins(5, 5, 5, 5)
+        
+        user_label = QLabel("👥 在线用户列表")
+        user_label.setStyleSheet("font-weight: bold; font-size: 14px;")
+        middle_layout.addWidget(user_label)
+        
+        select_btn_layout = QHBoxLayout()
+        self.select_all_btn = QPushButton("✅ 全选")
+        self.deselect_all_btn = QPushButton("❌ 取消全选")
+        self.select_all_btn.clicked.connect(self.select_all_users)
+        self.deselect_all_btn.clicked.connect(self.deselect_all_users)
+        select_btn_layout.addWidget(self.select_all_btn)
+        select_btn_layout.addWidget(self.deselect_all_btn)
+        select_btn_layout.addStretch()
+        middle_layout.addLayout(select_btn_layout)
+        
+        self.user_list_widget = QListWidget()
+        self.user_list_widget.setSelectionMode(QListWidget.NoSelection)
+        # 添加示例用户
+        sample_users = ["用户A", "用户B", "用户C", "用户D", "用户E"]
+        for user in sample_users:
+            self.add_user_item(user)
+            # 初始化用户话术数据
+            self.user_scripts[user] = [f"话术1: 您好{user},请问有什么可以帮您?", 
+                                       f"话术2: {user},感谢您的反馈,我们会尽快处理。",
+                                       f"话术3: {user},请您稍等,我为您转接专员。"]
+        middle_layout.addWidget(self.user_list_widget)
+        
+        user_count_label = QLabel(f"当前在线人数: {self.user_list_widget.count()}")
+        user_count_label.setStyleSheet("color: gray;")
+        middle_layout.addWidget(user_count_label)
+        
+        # ---------- 右侧:用户独立话术列表 ----------
+        right_widget = QWidget()
+        right_layout = QVBoxLayout(right_widget)
+        right_layout.setContentsMargins(5, 5, 5, 5)
+        
+        # 顶部:用户选择下拉框
+        user_select_layout = QHBoxLayout()
+        user_select_layout.addWidget(QLabel("选择用户 (快捷键F1-F5):"))
+        self.user_combo = QComboBox()
+        self.user_combo.addItems(sample_users)
+        self.user_combo.currentTextChanged.connect(self.on_user_changed)
+        user_select_layout.addWidget(self.user_combo)
+        user_select_layout.addStretch()
+        right_layout.addLayout(user_select_layout)
+        
+        # 话术列表标题
+        script_label = QLabel("📝 话术列表 (当前用户)")
+        script_label.setStyleSheet("font-weight: bold; font-size: 14px;")
+        right_layout.addWidget(script_label)
+        
+        # 话术列表显示
+        self.script_list_widget = QListWidget()
+        right_layout.addWidget(self.script_list_widget)
+        
+        # 话术编辑区域
+        script_edit_layout = QVBoxLayout()
+        
+        # 添加话术
+        add_layout = QHBoxLayout()
+        self.script_input = QLineEdit()
+        self.script_input.setPlaceholderText("输入新话术内容...")
+        add_btn = QPushButton("➕ 添加")
+        add_btn.clicked.connect(self.add_script)
+        add_layout.addWidget(self.script_input)
+        add_layout.addWidget(add_btn)
+        script_edit_layout.addLayout(add_layout)
+        
+        # 删除和修改按钮
+        modify_layout = QHBoxLayout()
+        delete_btn = QPushButton("🗑️ 删除选中")
+        delete_btn.clicked.connect(self.delete_script)
+        modify_btn = QPushButton("✏️ 修改选中")
+        modify_btn.clicked.connect(self.modify_script)
+        modify_layout.addWidget(delete_btn)
+        modify_layout.addWidget(modify_btn)
+        script_edit_layout.addLayout(modify_layout)
+        
+        # 保存按钮
+        save_layout = QHBoxLayout()
+        save_layout.addStretch()
+        self.save_scripts_btn = QPushButton("💾 保存话术配置")
+        self.save_scripts_btn.setStyleSheet("background-color: #2196F3; color: white; font-weight: bold; padding: 8px 20px; border-radius: 5px;")
+        self.save_scripts_btn.clicked.connect(self.save_user_scripts)
+        save_layout.addWidget(self.save_scripts_btn)
+        script_edit_layout.addLayout(save_layout)
+        
+        right_layout.addLayout(script_edit_layout)
+        
+        # 发送任务按钮
+        send_layout = QHBoxLayout()
+        send_layout.addStretch()
+        self.send_task_btn = QPushButton("🚀 发送任务")
+        self.send_task_btn.setStyleSheet("background-color: #4CAF50; color: white; font-weight: bold; padding: 8px 20px; border-radius: 5px;")
+        self.send_task_btn.clicked.connect(self.send_task)
+        send_layout.addWidget(self.send_task_btn)
+        right_layout.addLayout(send_layout)
+        
+        # ---------- 将三个部件加入分割器 ----------
+        main_splitter.addWidget(left_widget)
+        main_splitter.addWidget(middle_widget)
+        main_splitter.addWidget(right_widget)
+        main_splitter.setSizes([300, 450, 450])
+        
+        # 初始化显示第一个用户的话术
+        if sample_users:
+            self.current_user = sample_users[0]
+            self.load_user_scripts(self.current_user)
+        
+        # ---------- 设置快捷键 F1-F5 ----------
+        self.setup_shortcuts()
+        
+        # 窗口样式
+        self.setStyleSheet("""
+            QGroupBox {
+                font-weight: bold;
+                border: 1px solid #ccc;
+                border-radius: 5px;
+                margin-top: 10px;
+            }
+            QGroupBox::title {
+                subcontrol-origin: margin;
+                left: 10px;
+                padding: 0 5px 0 5px;
+            }
+            QListWidget::item {
+                padding: 5px;
+            }
+            QTextEdit {
+                background-color: #f8f8f8;
+                font-family: Consolas, monospace;
+            }
+            QComboBox {
+                padding: 5px;
+                min-width: 150px;
+            }
+        """)
+    
+    # ---------- 快捷键设置 ----------
+    def setup_shortcuts(self):
+        """设置F1-F5快捷键切换用户"""
+        # F1 选择第一个用户,F2 第二个,以此类推
+        shortcuts = [
+            (Qt.Key_F1, 0),
+            (Qt.Key_F2, 1),
+            (Qt.Key_F3, 2),
+            (Qt.Key_F4, 3),
+            (Qt.Key_F5, 4),
+        ]
+        
+        for key, index in shortcuts:
+            shortcut = QShortcut(QKeySequence(key), self)
+            # 使用lambda捕获index,并绑定到切换函数
+            shortcut.activated.connect(lambda idx=index: self.switch_to_user_by_index(idx))
+    
+    def switch_to_user_by_index(self, index):
+        """根据索引切换到对应用户"""
+        if index < self.user_combo.count():
+            user = self.user_combo.itemText(index)
+            self.user_combo.setCurrentText(user)
+            self.log_text.append(f"[快捷键] 切换到用户: {user}")
+        else:
+            self.log_text.append(f"[快捷键] 用户索引 {index} 不存在")
+    
+    # ---------- 用户管理 ----------
+    def add_user_item(self, username):
+        """添加一个带复选框的用户项"""
+        item = QListWidgetItem()
+        checkbox = QCheckBox(username)
+        self.user_list_widget.addItem(item)
+        self.user_list_widget.setItemWidget(item, checkbox)
+        checkbox.stateChanged.connect(self.on_user_check_changed)
+    
+    def get_user_checkboxes(self):
+        """获取所有用户复选框"""
+        checkboxes = []
+        for i in range(self.user_list_widget.count()):
+            item = self.user_list_widget.item(i)
+            widget = self.user_list_widget.itemWidget(item)
+            if isinstance(widget, QCheckBox):
+                checkboxes.append(widget)
+        return checkboxes
+    
+    def get_selected_users(self):
+        """获取所有选中的用户"""
+        selected = []
+        for cb in self.get_user_checkboxes():
+            if cb.isChecked():
+                selected.append(cb.text())
+        return selected
+    
+    def select_all_users(self):
+        for cb in self.get_user_checkboxes():
+            cb.setChecked(True)
+        self.log_text.append("[操作] 已全选所有用户")
+    
+    def deselect_all_users(self):
+        for cb in self.get_user_checkboxes():
+            cb.setChecked(False)
+        self.log_text.append("[操作] 已取消全选所有用户")
+    
+    def on_user_check_changed(self):
+        """复选框状态改变时更新下拉框列表"""
+        # 获取当前所有用户
+        current_users = []
+        for i in range(self.user_list_widget.count()):
+            item = self.user_list_widget.item(i)
+            widget = self.user_list_widget.itemWidget(item)
+            if isinstance(widget, QCheckBox):
+                current_users.append(widget.text())
+        
+        # 更新下拉框
+        current_selection = self.user_combo.currentText()
+        self.user_combo.clear()
+        self.user_combo.addItems(current_users)
+        
+        # 如果之前的用户还在列表中,恢复选择
+        if current_selection in current_users:
+            self.user_combo.setCurrentText(current_selection)
+        elif current_users:
+            self.user_combo.setCurrentIndex(0)
+    
+    # ---------- 话术管理 (用户独立) ----------
+    def load_user_scripts(self, username):
+        """加载指定用户的话术列表"""
+        if username in self.user_scripts:
+            self.script_list_widget.clear()
+            for script in self.user_scripts[username]:
+                self.script_list_widget.addItem(script)
+            self.current_user = username
+            self.log_text.append(f"[系统] 加载用户 '{username}' 的话术列表")
+    
+    def on_user_changed(self, username):
+        """下拉框切换用户"""
+        if username and username != self.current_user:
+            # 如果当前用户有修改但未保存,先保存
+            if self.current_user and self.current_user in self.user_scripts:
+                # 自动保存当前用户的话术
+                self.save_current_user_scripts()
+            
+            self.load_user_scripts(username)
+    
+    def save_current_user_scripts(self):
+        """保存当前用户的话术到内存"""
+        if self.current_user and self.current_user in self.user_scripts:
+            scripts = []
+            for i in range(self.script_list_widget.count()):
+                scripts.append(self.script_list_widget.item(i).text())
+            self.user_scripts[self.current_user] = scripts
+            self.log_text.append(f"[系统] 自动保存用户 '{self.current_user}' 的话术配置")
+    
+    def save_user_scripts(self):
+        """手动保存当前用户的话术(并持久化)"""
+        if not self.current_user:
+            QMessageBox.warning(self, "提示", "请先选择一个用户!")
+            return
+        
+        # 保存到内存
+        self.save_current_user_scripts()
+        
+        # 持久化保存到文件
+        try:
+            with open("user_scripts_backup.json", "w", encoding="utf-8") as f:
+                json.dump(self.user_scripts, f, ensure_ascii=False, indent=2)
+            self.log_text.append(f"[系统] 话术配置已保存到文件 user_scripts_backup.json")
+            QMessageBox.information(self, "保存成功", f"用户 '{self.current_user}' 的话术配置已保存!")
+        except Exception as e:
+            QMessageBox.critical(self, "保存失败", f"保存到文件时出错: {str(e)}")
+    
+    def add_script(self):
+        """添加话术到当前用户"""
+        text = self.script_input.text().strip()
+        if text:
+            self.script_list_widget.addItem(text)
+            self.script_input.clear()
+            # 自动保存
+            self.save_current_user_scripts()
+            self.log_text.append(f"[操作] 为用户 '{self.current_user}' 添加话术: {text}")
+        else:
+            QMessageBox.information(self, "提示", "话术内容不能为空!")
+    
+    def delete_script(self):
+        """删除当前用户选中的话术"""
+        current_row = self.script_list_widget.currentRow()
+        if current_row >= 0:
+            item_text = self.script_list_widget.item(current_row).text()
+            self.script_list_widget.takeItem(current_row)
+            # 自动保存
+            self.save_current_user_scripts()
+            self.log_text.append(f"[操作] 删除用户 '{self.current_user}' 的话术: {item_text}")
+        else:
+            QMessageBox.information(self, "提示", "请先选中一条话术!")
+    
+    def modify_script(self):
+        """修改当前用户选中的话术"""
+        current_row = self.script_list_widget.currentRow()
+        if current_row >= 0:
+            old_text = self.script_list_widget.item(current_row).text()
+            new_text, ok = QMessageBox.getText(self, "修改话术", "请输入新的话术内容:", 
+                                               text=old_text)
+            if ok and new_text.strip():
+                self.script_list_widget.item(current_row).setText(new_text.strip())
+                # 自动保存
+                self.save_current_user_scripts()
+                self.log_text.append(f"[操作] 修改用户 '{self.current_user}' 的话术: {old_text} -> {new_text.strip()}")
+            elif ok and not new_text.strip():
+                QMessageBox.information(self, "提示", "话术内容不能为空!")
+        else:
+            QMessageBox.information(self, "提示", "请先选中一条话术!")
+    
+    def clear_log(self):
+        self.log_text.clear()
+        self.log_text.append("[系统] 日志已清空")
+        self.log_text.append("[系统] 快捷键: F1-F5 快速切换用户")
+    
+    # ---------- 发送任务 ----------
+    def send_task(self):
+        """发送任务到选中的用户"""
+        selected_users = self.get_selected_users()
+        
+        if not selected_users:
+            QMessageBox.warning(self, "提示", "请至少选择一个用户!")
+            return
+        
+        # 获取每个用户选中的话术 (这里演示:取每个用户的第一条话术)
+        task_details = []
+        for user in selected_users:
+            if user in self.user_scripts and self.user_scripts[user]:
+                script = self.user_scripts[user][0]  # 取第一条
+                task_details.append(f"{user}: {script}")
+            else:
+                task_details.append(f"{user}: (无话术)")
+        
+        # 记录日志
+        self.log_text.append(f"[任务] 发送给 {len(selected_users)} 个用户:")
+        for detail in task_details:
+            self.log_text.append(f"  - {detail}")
+        self.log_text.append("[任务] 发送完成 (功能待实现)")
+        
+        # 显示详情
+        msg = "\n".join(task_details)
+        QMessageBox.information(self, "发送任务", 
+                                 f"已发送任务给 {len(selected_users)} 个用户:\n\n{msg}")
+    
+    # ---------- 窗口关闭时保存 ----------
+    def closeEvent(self, event):
+        """关闭窗口时自动保存所有用户话术"""
+        if self.current_user:
+            self.save_current_user_scripts()
+        try:
+            with open("user_scripts_backup.json", "w", encoding="utf-8") as f:
+                json.dump(self.user_scripts, f, ensure_ascii=False, indent=2)
+            self.log_text.append("[系统] 程序退出,已自动保存所有话术配置")
+        except:
+            pass
+        event.accept()
+
+if __name__ == "__main__":
+    app = QApplication(sys.argv)
+    window = MainWindow()
+    window.show()
+    sys.exit(app.exec_())

+ 336 - 11
爱回收.py

@@ -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"
@@ -26,13 +27,31 @@ class PhoneQueryApp:
         self.total_pages = 0
         self.page_size = 30
         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",
+            "价格从低到高": "sort_price_asc",
+            "价格从高到低": "sort_price_desc"
+        }
         
         # 筛选选项映射
         self.memory_map = {
             "1T": 187,
             "512G": 192,
             "256G": 209,
-            "128G": 210
+            "128G": 210,
+            "64G": 217,
         }
         
         self.battery_map = {
@@ -118,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")
@@ -137,14 +156,26 @@ class PhoneQueryApp:
         query_frame = ttk.LabelFrame(main_frame, text="查询条件", padding="10")
         query_frame.grid(row=1, column=0, sticky=(tk.W, tk.E), pady=(0, 10))
         query_frame.columnconfigure(1, weight=1)
+        query_frame.columnconfigure(3, weight=1)
         
+        # 机型输入
         ttk.Label(query_frame, text="机型:", font=("Arial", 11)).grid(row=0, column=0, sticky=tk.W, padx=(0, 10))
-        self.model_entry = ttk.Entry(query_frame, width=50, font=("Arial", 11))
+        self.model_entry = ttk.Entry(query_frame, width=35, font=("Arial", 11))
         self.model_entry.grid(row=0, column=1, sticky=(tk.W, tk.E), padx=(0, 10))
         self.model_entry.insert(0, "iPhone 15 Pro")
         
+        # 排序选择
+        ttk.Label(query_frame, text="排序方式:", font=("Arial", 11)).grid(row=0, column=2, sticky=tk.W, padx=(10, 10))
+        self.sort_var = tk.StringVar(value="综合排序")
+        self.sort_combo = ttk.Combobox(query_frame, textvariable=self.sort_var, 
+                                       values=["综合排序", "价格从低到高", "价格从高到低", "最新上架", "人气最高"],
+                                       width=15, state="readonly", font=("Arial", 10))
+        self.sort_combo.grid(row=0, column=3, sticky=(tk.W, tk.E), padx=(0, 10))
+        self.sort_combo.bind('<<ComboboxSelected>>', self.on_sort_changed)
+        
+        # 搜索按钮
         self.search_btn = ttk.Button(query_frame, text="搜索", command=self.search_products, width=15)
-        self.search_btn.grid(row=0, column=2)
+        self.search_btn.grid(row=0, column=4)
         
         # ==================== 筛选区域 ====================
         # 第一行:内存 + 电池效率(各占一半)
@@ -211,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)
         
@@ -246,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))
@@ -260,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))
@@ -283,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("就绪")
@@ -292,6 +353,11 @@ class PhoneQueryApp:
         
         self.update_pagination_buttons()
         
+    def on_sort_changed(self, event=None):
+        """排序方式改变时自动搜索"""
+        if self.model_entry.get().strip():
+            self.search_products()
+        
     def get_selected_memory(self):
         """获取选中的内存PPV ID列表"""
         selected = []
@@ -349,6 +415,8 @@ class PhoneQueryApp:
             
         self.current_keyword = keyword
         self.current_page = page
+        # 获取当前选中的排序方式
+        self.current_sort = self.sort_map.get(self.sort_var.get(), "sort_composite")
         
         for item in self.result_tree.get_children():
             self.result_tree.delete(item)
@@ -376,7 +444,7 @@ class PhoneQueryApp:
             condition_tags = self.get_selected_condition_tags()
             
             request_data = {
-                "sortValue": "sort_composite",
+                "sortValue": self.current_sort,  # 使用当前选中的排序方式
                 "scene": "search",
                 "keyword": keyword,
                 "sirReq": False,
@@ -497,7 +565,8 @@ class PhoneQueryApp:
                 'info': info
             })
             
-        self.status_var.set(f"第 {page + 1} 页,共找到 {len(data)} 条结果")
+        sort_name = self.sort_var.get()
+        self.status_var.set(f"第 {page + 1} 页,排序: {sort_name},共找到 {len(data)} 条结果")
         self.update_pagination_buttons()
         
     def update_pagination_buttons(self):
@@ -711,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}")

+ 0 - 190
米画师.py

@@ -1,190 +0,0 @@
-import tkinter as tk
-from tkinter import scrolledtext, messagebox
-import requests
-import threading
-import time
-import json
-
-class PurchaseApp:
-    def __init__(self, root):
-        self.root = root
-        self.root.title("米画师商品购买工具")
-        self.root.geometry("600x500")
-        
-        self.running = False
-        self.stop_flag = False
-        
-        self.setup_ui()
-        
-    def setup_ui(self):
-        # 密码输入框
-        tk.Label(self.root, text="支付密码:").pack(pady=5)
-        self.password_entry = tk.Entry(self.root, show="*", width=50)
-        self.password_entry.pack(pady=5)
-        
-        # 商品ID输入框
-        tk.Label(self.root, text="商品ID:").pack(pady=5)
-        self.product_id_entry = tk.Entry(self.root, width=50)
-        self.product_id_entry.pack(pady=5)
-        
-        # 按钮框架
-        button_frame = tk.Frame(self.root)
-        button_frame.pack(pady=20)
-        
-        self.start_button = tk.Button(button_frame, text="启动", command=self.start_request, 
-                                     bg="green", fg="white", width=10)
-        self.start_button.pack(side=tk.LEFT, padx=10)
-        
-        self.stop_button = tk.Button(button_frame, text="停止", command=self.stop_request, 
-                                    bg="red", fg="white", width=10, state=tk.DISABLED)
-        self.stop_button.pack(side=tk.LEFT, padx=10)
-        
-        # 日志框架
-        tk.Label(self.root, text="请求日志:").pack(pady=5)
-        self.log_text = scrolledtext.ScrolledText(self.root, width=70, height=20, 
-                                                   wrap=tk.WORD)
-        self.log_text.pack(pady=5, padx=10, fill=tk.BOTH, expand=True)
-        
-    def log_message(self, message, is_error=False):
-        """在日志框中添加消息"""
-        timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
-        tag = "ERROR" if is_error else "INFO"
-        log_entry = f"[{timestamp}] [{tag}] {message}\n"
-        
-        self.log_text.insert(tk.END, log_entry)
-        self.log_text.see(tk.END)
-        
-        # 如果是错误消息,用红色显示
-        if is_error:
-            start_idx = self.log_text.index(f"end-{len(log_entry)}c")
-            end_idx = self.log_text.index("end-1c")
-            self.log_text.tag_add("error", start_idx, end_idx)
-            self.log_text.tag_config("error", foreground="red")
-    
-    def make_request(self, product_id, pay_password):
-        """执行单次请求"""
-        url = f"https://www.mihuashi.com/api/v1/manufactures/{product_id}/purchase"
-        
-        # 构建请求数据
-        payload = f"pay_password={pay_password}&remember_token=xxrohSwRvhkMzftVNTb2SQ&specification_ids%5B%5D=3931"
-        
-        headers = {
-            "content-type": "application/x-www-form-urlencoded",
-            "baggage": "sentry-environment=production,sentry-public_key=afa52c0802b4441ba513fb25cb6bc20a,sentry-release=com.mihuashi.iOS%408.8.1%2B245,sentry-trace_id=c576b096547343d790462dbe3600ce7e",
-            "x-track-trans-data": "CiQ4N2Q4NTRmOS02ZTQ4LTQ3YmMtOGIwYy1mMzRhY2U2Yzg4YTUSFQoMaG90XzE2NzY0X3YyFQDAr0QYHRISCggxNjc3OF92MhVgU9G+GNcBGhAKCTE1NzU4OmN0ch0A0l09GhAKCTE1NzU4OmN2ch0AyLg5IgAq1wIhC3L415YlBiR6vAJ7ImFwaV9yZXFfdGltZSI6MTc4MTI1MDQ2NCwic3BtXzMiOiLmjqjojZAiLCJzcG1fMiI6Iuapseeqly3mnI3orr4m5rC05Y2wIiwicGFnZV9udW0iOjEsImFiX3ZlcnNpb24iOiIiLCJzcG0iOiLnsbPnlLvluIgkIyMk5qmx56qXLeacjeiuvibmsLTljbAkIyMk5o6o6I2QIiwiY2xvc2VfcGVyc29uYWxpemVkX3JlYyI6ZmFsc2UsImJodl90aW1lIjoiMTc4MTI1MDQ2NCIsImJodl90aW1lX2hvdXIiOiIxNSIsImJodl90aW1lX21vbnRoZGF5IjoiMTIiLCJiaHZfdGltZV93ZWVrZGF5IjoiNSIsImZha2VfY29udGV4dF9pZCI6ImZha2VfY29udGV4dF9pZCJ9sAEOygEGNzY3NDY00AEy",
-            "sensor-id": "96F047C1-773B-4596-BA8F-EB8E3D08D404",
-            "authorization": "Bearer xxrohSdEFqQSt0fNY1mh4k",
-            "x-track-volc-extra": '{"recall_list":"hot_16764_v2,16778_v2","doc_type":"manufacture","status":"1","tags":"","score":"0.00001649724435992539"}',
-            "accept": "*/*",
-            "priority": "u=3, i",
-            "x-track-spm": "%E7%B1%B3%E7%94%BB%E5%B8%88$%23%23$%E6%9C%8D%E8%AE%BE&%E6%B0%B4%E5%8D%B0$%23%23$%E8%AF%A6%E6%83%85%E9%A1%B5$%23%23$12",
-            "accept-language": "zh-Hans-AU;q=1, en-AU;q=0.9",
-            "sentry-trace": "a0ac9f45d3f74b51b387a345b0d95389-2b3cdabdb84741e4-0",
-            "x-network-type": "wifi",
-            "user-agent": "MHSIPhoneApp/8.7.1 (iPhone; iOS 26.5; Scale/3.00; iPhone15,4)",
-            "cookie": "aliyungf_tc=5db016b76e04464209e124368fc4945f6be91f55436d0e145e708503abaa3602",
-            "Accept-Encoding": "gzip, deflate, br",
-            "Connection": "keep-alive"
-        }
-        
-        try:
-            response = requests.request("POST", url, data=payload, headers=headers, timeout=10)
-            
-            # 尝试解析JSON响应
-            try:
-                response_json = response.json()
-                formatted_response = json.dumps(response_json, ensure_ascii=False, indent=2)
-            except:
-                formatted_response = response.text
-            
-            self.log_message(f"请求URL: {url}")
-            self.log_message(f"响应状态码: {response.status_code}")
-            self.log_message(f"响应内容: {formatted_response[:500]}")  # 限制显示长度
-            
-            if response.status_code == 200:
-                self.log_message("请求成功")
-            else:
-                self.log_message(f"请求失败,状态码: {response.status_code}", is_error=True)
-                
-        except requests.exceptions.Timeout:
-            self.log_message("请求超时", is_error=True)
-        except requests.exceptions.ConnectionError:
-            self.log_message("连接错误,请检查网络", is_error=True)
-        except Exception as e:
-            self.log_message(f"请求异常: {str(e)}", is_error=True)
-    
-    def request_loop(self):
-        """请求循环,每2秒执行一次"""
-        product_id = self.product_id_entry.get().strip()
-        pay_password = self.password_entry.get()
-        
-        # 验证输入
-        if not product_id:
-            self.log_message("请输入商品ID", is_error=True)
-            self.stop_request()
-            return
-        
-        if not pay_password:
-            self.log_message("请输入支付密码", is_error=True)
-            self.stop_request()
-            return
-        
-        # 验证商品ID是否为数字
-        try:
-            int(product_id)
-        except ValueError:
-            self.log_message("商品ID必须是数字", is_error=True)
-            self.stop_request()
-            return
-        
-        self.log_message(f"开始请求循环,商品ID: {product_id}")
-        
-        while self.running and not self.stop_flag:
-            self.make_request(product_id, pay_password)
-            
-            # 等待2秒,但每秒检查一次停止标志
-            for _ in range(2):
-                if not self.running or self.stop_flag:
-                    break
-                time.sleep(1)
-        
-        self.log_message("请求循环已停止")
-    
-    def start_request(self):
-        """启动请求线程"""
-        # 验证输入
-        if not self.password_entry.get():
-            messagebox.showwarning("警告", "请输入支付密码")
-            return
-        
-        if not self.product_id_entry.get():
-            messagebox.showwarning("警告", "请输入商品ID")
-            return
-        
-        self.running = True
-        self.stop_flag = False
-        
-        # 更新按钮状态
-        self.start_button.config(state=tk.DISABLED)
-        self.stop_button.config(state=tk.NORMAL)
-        
-        # 启动请求线程
-        self.request_thread = threading.Thread(target=self.request_loop, daemon=True)
-        self.request_thread.start()
-    
-    def stop_request(self):
-        """停止请求"""
-        self.running = False
-        self.stop_flag = True
-        
-        # 更新按钮状态
-        self.start_button.config(state=tk.NORMAL)
-        self.stop_button.config(state=tk.DISABLED)
-
-def main():
-    root = tk.Tk()
-    app = PurchaseApp(root)
-    root.mainloop()
-
-if __name__ == "__main__":
-    main()