Quellcode durchsuchen

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

PUGE vor 3 Wochen
Ursprung
Commit
169575bad2
5 geänderte Dateien mit 1808 neuen und 197 gelöschten Zeilen
  1. 906 0
      87总控.py
  2. 1 1
      test.py
  3. 68 8
      控制vm虚拟机.py
  4. 541 188
      解密机大侠.py
  5. 292 0
      采矿记录.py

+ 906 - 0
87总控.py

@@ -0,0 +1,906 @@
+import sys
+import time
+import win32gui
+import win32con
+import win32api
+import win32process
+import threading
+from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, 
+                             QHBoxLayout, QPushButton, QListWidget, QListWidgetItem,
+                             QMessageBox, QLabel, QLineEdit, QGroupBox, QTextEdit,
+                             QSplitter, QComboBox, QSpinBox, QCheckBox, QRadioButton,
+                             QButtonGroup)
+from PyQt5.QtCore import Qt, QTimer, pyqtSignal, QObject
+from PyQt5.QtGui import QFont
+
+
+class WorkerSignals(QObject):
+    """工作线程信号"""
+    status = pyqtSignal(str)
+    progress = pyqtSignal(int, int)  # 当前索引, 总数
+    finished = pyqtSignal()
+
+
+class WindowManagerApp(QMainWindow):
+    def __init__(self):
+        super().__init__()
+        self.window_handles = []
+        self.all_windows = []
+        self.is_running = False
+        self.should_stop = False  # 停止标志
+        self.initUI()
+        
+    def initUI(self):
+        """初始化界面"""
+        self.setWindowTitle('霸器窗口自动点击工具 - 多窗口批量执行')
+        self.setGeometry(100, 100, 950, 750)
+        
+        # 设置主窗口样式
+        self.setStyleSheet("""
+            QMainWindow {
+                background-color: #2b2b2b;
+            }
+            QGroupBox {
+                color: #ffffff;
+                border: 2px solid #555;
+                border-radius: 5px;
+                margin-top: 10px;
+                font-weight: bold;
+            }
+            QGroupBox::title {
+                subcontrol-origin: margin;
+                left: 10px;
+                padding: 0 5px 0 5px;
+            }
+            QPushButton {
+                background-color: #4a4a4a;
+                color: white;
+                border: 1px solid #555;
+                padding: 8px 16px;
+                border-radius: 4px;
+                font-weight: bold;
+                font-size: 12px;
+            }
+            QPushButton:hover {
+                background-color: #5a5a5a;
+            }
+            QPushButton:pressed {
+                background-color: #3a3a3a;
+            }
+            QPushButton#refreshBtn {
+                background-color: #0078d4;
+            }
+            QPushButton#refreshBtn:hover {
+                background-color: #1a8ad4;
+            }
+            QPushButton#startBtn {
+                background-color: #107c10;
+                font-size: 14px;
+                padding: 12px 24px;
+            }
+            QPushButton#startBtn:hover {
+                background-color: #1a8c1a;
+            }
+            QPushButton#startBtn:disabled {
+                background-color: #555;
+            }
+            QPushButton#stopBtn {
+                background-color: #d13438;
+                font-size: 14px;
+                padding: 12px 24px;
+            }
+            QPushButton#stopBtn:hover {
+                background-color: #e14448;
+            }
+            QListWidget {
+                background-color: #3c3c3c;
+                color: #ffffff;
+                border: 1px solid #555;
+                border-radius: 4px;
+                padding: 5px;
+            }
+            QListWidget::item {
+                padding: 8px;
+                border-bottom: 1px solid #444;
+            }
+            QListWidget::item:selected {
+                background-color: #0078d4;
+            }
+            QLabel {
+                color: #cccccc;
+            }
+            QCheckBox {
+                color: #ffffff;
+                font-size: 12px;
+                padding: 5px;
+            }
+            QCheckBox::indicator {
+                width: 18px;
+                height: 18px;
+            }
+            QCheckBox::indicator:unchecked {
+                background-color: #3c3c3c;
+                border: 2px solid #555;
+                border-radius: 3px;
+            }
+            QCheckBox::indicator:checked {
+                background-color: #0078d4;
+                border: 2px solid #0078d4;
+                border-radius: 3px;
+            }
+            QRadioButton {
+                color: #ffffff;
+                font-size: 12px;
+                padding: 5px;
+            }
+            QRadioButton::indicator {
+                width: 18px;
+                height: 18px;
+            }
+            QRadioButton::indicator:unchecked {
+                background-color: #3c3c3c;
+                border: 2px solid #555;
+                border-radius: 9px;
+            }
+            QRadioButton::indicator:checked {
+                background-color: #0078d4;
+                border: 2px solid #0078d4;
+                border-radius: 9px;
+            }
+            QLineEdit, QComboBox, QSpinBox {
+                background-color: #3c3c3c;
+                color: #ffffff;
+                border: 1px solid #555;
+                border-radius: 4px;
+                padding: 5px;
+            }
+            QTextEdit {
+                background-color: #1e1e1e;
+                color: #00ff00;
+                border: 1px solid #555;
+                border-radius: 4px;
+                font-family: 'Consolas', monospace;
+                font-size: 10pt;
+            }
+        """)
+        
+        # 创建中央窗口
+        central_widget = QWidget()
+        self.setCentralWidget(central_widget)
+        main_layout = QVBoxLayout(central_widget)
+        
+        # 标题
+        title_label = QLabel('霸器窗口自动点击工具 - 批量执行')
+        title_label.setAlignment(Qt.AlignCenter)
+        title_font = QFont('Arial', 18, QFont.Bold)
+        title_label.setFont(title_font)
+        title_label.setStyleSheet('color: #00b4d8; margin: 15px;')
+        main_layout.addWidget(title_label)
+        
+        # 控制区域
+        control_group = QGroupBox('控制面板')
+        control_layout = QHBoxLayout()
+        
+        self.refresh_btn = QPushButton('🔄 加载窗口')
+        self.refresh_btn.setObjectName('refreshBtn')
+        self.refresh_btn.clicked.connect(self.load_processes)
+        control_layout.addWidget(self.refresh_btn)
+        
+        self.filter_label = QLabel('筛选:')
+        control_layout.addWidget(self.filter_label)
+        
+        self.filter_input = QLineEdit()
+        self.filter_input.setPlaceholderText('输入标题关键词过滤...')
+        self.filter_input.textChanged.connect(self.filter_list)
+        control_layout.addWidget(self.filter_input)
+        
+        # 显示模式选择
+        self.show_mode_combo = QComboBox()
+        self.show_mode_combo.addItems(['所有窗口', '仅可见窗口', '仅隐藏窗口'])
+        self.show_mode_combo.currentTextChanged.connect(self.on_show_mode_changed)
+        control_layout.addWidget(QLabel('显示:'))
+        control_layout.addWidget(self.show_mode_combo)
+        
+        control_group.setLayout(control_layout)
+        main_layout.addWidget(control_group)
+        
+        # 复选框区域 - 功能选择
+        checkbox_group = QGroupBox('功能选择')
+        checkbox_layout = QHBoxLayout()
+        
+        self.cb_molong = QCheckBox('争夺魔龙之心')
+        self.cb_molong.setChecked(False)
+        checkbox_layout.addWidget(self.cb_molong)
+        
+        self.cb_feicui = QCheckBox('翡翠荣耀旗')
+        self.cb_feicui.setChecked(False)
+        checkbox_layout.addWidget(self.cb_feicui)
+        
+        self.cb_jinyan = QCheckBox('金岩霸者棋')
+        self.cb_jinyan.setChecked(False)
+        checkbox_layout.addWidget(self.cb_jinyan)
+        
+        self.cb_chiyan = QCheckBox('炽焰战神棋')
+        self.cb_chiyan.setChecked(False)
+        checkbox_layout.addWidget(self.cb_chiyan)
+        
+        checkbox_group.setLayout(checkbox_layout)
+        main_layout.addWidget(checkbox_group)
+        
+        # 单选按钮区域 - 皇宫占位
+        radio_group = QGroupBox('皇宫占位位置')
+        radio_layout = QHBoxLayout()
+        
+        self.radio_random = QRadioButton('随机')
+        self.radio_random.setChecked(True)
+        radio_layout.addWidget(self.radio_random)
+        
+        self.radio_door = QRadioButton('门口')
+        radio_layout.addWidget(self.radio_door)
+        
+        self.radio_corner = QRadioButton('角落')
+        radio_layout.addWidget(self.radio_corner)
+        
+        # 将单选按钮分组
+        self.radio_button_group = QButtonGroup()
+        self.radio_button_group.addButton(self.radio_random)
+        self.radio_button_group.addButton(self.radio_door)
+        self.radio_button_group.addButton(self.radio_corner)
+        
+        radio_group.setLayout(radio_layout)
+        main_layout.addWidget(radio_group)
+        
+        # 窗口列表 - 支持多选
+        list_group = QGroupBox('窗口列表 (霸器-开头) - 可多选')
+        list_layout = QVBoxLayout()
+        
+        self.window_list = QListWidget()
+        self.window_list.setSelectionMode(QListWidget.MultiSelection)  # 改为多选模式
+        self.window_list.itemDoubleClicked.connect(self.show_selected_window)
+        list_layout.addWidget(self.window_list)
+        
+        # 添加全选/取消全选按钮
+        select_layout = QHBoxLayout()
+        self.select_all_btn = QPushButton('全选')
+        self.select_all_btn.clicked.connect(self.select_all_items)
+        select_layout.addWidget(self.select_all_btn)
+        
+        self.deselect_all_btn = QPushButton('取消全选')
+        self.deselect_all_btn.clicked.connect(self.deselect_all_items)
+        select_layout.addWidget(self.deselect_all_btn)
+        
+        self.selected_count_label = QLabel('已选: 0 个窗口')
+        self.selected_count_label.setStyleSheet('color: #00b4d8;')
+        select_layout.addWidget(self.selected_count_label)
+        select_layout.addStretch()
+        
+        list_layout.addLayout(select_layout)
+        
+        list_group.setLayout(list_layout)
+        main_layout.addWidget(list_group)
+        
+        # 操作按钮区域
+        action_group = QGroupBox('操作')
+        action_layout = QHBoxLayout()
+        
+        self.start_btn = QPushButton('▶️ 开始批量执行')
+        self.start_btn.setObjectName('startBtn')
+        self.start_btn.clicked.connect(self.start_auto_click)
+        action_layout.addWidget(self.start_btn)
+        
+        self.stop_btn = QPushButton('⏹️ 停止')
+        self.stop_btn.setObjectName('stopBtn')
+        self.stop_btn.clicked.connect(self.stop_auto_click)
+        self.stop_btn.setEnabled(False)
+        action_layout.addWidget(self.stop_btn)
+        
+        self.show_btn = QPushButton('👁️ 显示选中窗口')
+        self.show_btn.setObjectName('showBtn')
+        self.show_btn.clicked.connect(self.show_selected_window)
+        action_layout.addWidget(self.show_btn)
+        
+        action_group.setLayout(action_layout)
+        main_layout.addWidget(action_group)
+        
+        # 日志区域
+        log_group = QGroupBox('运行日志')
+        log_layout = QVBoxLayout()
+        
+        self.log_text = QTextEdit()
+        self.log_text.setReadOnly(True)
+        self.log_text.setMaximumHeight(150)
+        log_layout.addWidget(self.log_text)
+        
+        log_group.setLayout(log_layout)
+        main_layout.addWidget(log_group)
+        
+        # 状态栏
+        self.status_label = QLabel('就绪')
+        self.status_label.setStyleSheet('color: #888; margin: 5px;')
+        main_layout.addWidget(self.status_label)
+        
+        # 自动加载
+        self.load_processes()
+        
+        # 工作线程信号
+        self.signals = WorkerSignals()
+        self.signals.status.connect(self.update_status)
+        self.signals.progress.connect(self.update_progress)
+        self.signals.finished.connect(self.on_finished)
+    
+    def select_all_items(self):
+        """全选所有窗口"""
+        for i in range(self.window_list.count()):
+            item = self.window_list.item(i)
+            if not item.isHidden():
+                item.setSelected(True)
+        self.update_selected_count()
+    
+    def deselect_all_items(self):
+        """取消全选"""
+        self.window_list.clearSelection()
+        self.update_selected_count()
+    
+    def update_selected_count(self):
+        """更新已选数量"""
+        count = len(self.window_list.selectedItems())
+        self.selected_count_label.setText(f'已选: {count} 个窗口')
+    
+    def get_window_title(self, hwnd):
+        """获取窗口标题"""
+        try:
+            return win32gui.GetWindowText(hwnd)
+        except:
+            return ''
+    
+    def is_window_visible(self, hwnd):
+        """检查窗口是否可见"""
+        try:
+            return win32gui.IsWindowVisible(hwnd)
+        except:
+            return False
+    
+    def enum_all_windows_callback(self, hwnd, windows_list):
+        """枚举所有窗口的回调函数(包括隐藏窗口)"""
+        if win32gui.IsWindow(hwnd):
+            title = self.get_window_title(hwnd)
+            if title:  # 只记录有标题的窗口
+                windows_list.append((hwnd, title))
+        return True
+    
+    def load_processes(self):
+        """加载所有进程窗口(包括隐藏的)"""
+        try:
+            self.window_list.clear()
+            self.window_handles = []
+            self.all_windows = []
+            
+            windows_list = []
+            win32gui.EnumWindows(self.enum_all_windows_callback, windows_list)
+            
+            self.all_windows = windows_list.copy()
+            
+            # 筛选以"霸器-"开头的窗口
+            baqi_windows = [(hwnd, title) for hwnd, title in windows_list if title.startswith('霸器-')]
+            
+            if not baqi_windows:
+                self.status_label.setText(f'未找到以"霸器-"开头的窗口 (共找到 {len(windows_list)} 个窗口)')
+                self.log_text.append(f'⚠️ 未找到霸器窗口,共找到 {len(windows_list)} 个窗口')
+                return
+            
+            # 按标题排序
+            baqi_windows.sort(key=lambda x: x[1])
+            
+            for hwnd, title in baqi_windows:
+                item = QListWidgetItem(title)
+                item.setData(Qt.UserRole, hwnd)
+                visible = self.is_window_visible(hwnd)
+                status_text = " ✓" if visible else " ✗"
+                item.setText(title + status_text)
+                item.setData(Qt.UserRole + 1, visible)
+                self.window_list.addItem(item)
+                self.window_handles.append(hwnd)
+            
+            visible_count = sum(1 for _, title in baqi_windows if self.is_window_visible(_))
+            hidden_count = len(baqi_windows) - visible_count
+            
+            self.status_label.setText(f'找到 {len(baqi_windows)} 个"霸器-"窗口 (可见: {visible_count}, 隐藏: {hidden_count})')
+            self.log_text.append(f'✅ 加载完成,找到 {len(baqi_windows)} 个霸器窗口')
+            
+            # 应用显示模式过滤
+            self.apply_show_mode()
+            self.update_selected_count()
+            
+        except Exception as e:
+            QMessageBox.critical(self, '错误', f'加载进程失败: {str(e)}')
+            self.status_label.setText('加载失败')
+            self.log_text.append(f'❌ 加载失败: {str(e)}')
+    
+    def apply_show_mode(self):
+        """应用显示模式过滤"""
+        mode = self.show_mode_combo.currentText()
+        for i in range(self.window_list.count()):
+            item = self.window_list.item(i)
+            visible = item.data(Qt.UserRole + 1)
+            
+            if mode == '所有窗口':
+                item.setHidden(False)
+            elif mode == '仅可见窗口':
+                item.setHidden(not visible)
+            elif mode == '仅隐藏窗口':
+                item.setHidden(visible)
+        self.update_selected_count()
+    
+    def on_show_mode_changed(self):
+        """显示模式改变时重新过滤"""
+        self.apply_show_mode()
+    
+    def filter_list(self):
+        """根据输入过滤列表"""
+        filter_text = self.filter_input.text().lower()
+        for i in range(self.window_list.count()):
+            item = self.window_list.item(i)
+            title = item.text().replace(' ✓', '').replace(' ✗', '')
+            if filter_text in title.lower():
+                item.setHidden(False)
+            else:
+                item.setHidden(True)
+        self.apply_show_mode()
+    
+    def get_selected_windows(self):
+        """获取所有选中的窗口"""
+        selected_items = self.window_list.selectedItems()
+        windows = []
+        for item in selected_items:
+            hwnd = item.data(Qt.UserRole)
+            title = item.text().replace(' ✓', '').replace(' ✗', '')
+            windows.append((hwnd, title))
+        return windows
+    
+    def show_selected_window(self):
+        """显示选中的窗口(只显示第一个选中的)"""
+        selected = self.get_selected_windows()
+        if not selected:
+            QMessageBox.warning(self, '警告', '请先选择窗口')
+            return
+        
+        hwnd, title = selected[0]
+        try:
+            win32gui.ShowWindow(hwnd, win32con.SW_SHOW)
+            win32gui.SetForegroundWindow(hwnd)
+            if win32gui.IsIconic(hwnd):
+                win32gui.ShowWindow(hwnd, win32con.SW_RESTORE)
+            
+            self.status_label.setText(f'已显示窗口: {title}')
+            self.log_text.append(f'👁️ 显示窗口: {title}')
+            self.refresh_window_visibility()
+            
+        except Exception as e:
+            QMessageBox.critical(self, '错误', f'显示窗口失败: {str(e)}')
+    
+    def refresh_window_visibility(self):
+        """刷新窗口中显示的可见状态"""
+        for i in range(self.window_list.count()):
+            item = self.window_list.item(i)
+            hwnd = item.data(Qt.UserRole)
+            title = item.text().replace(' ✓', '').replace(' ✗', '')
+            visible = self.is_window_visible(hwnd)
+            status_text = " ✓" if visible else " ✗"
+            item.setText(title + status_text)
+            item.setData(Qt.UserRole + 1, visible)
+        
+        self.apply_show_mode()
+    
+    def minimize_window(self, hwnd):
+        """最小化窗口"""
+        try:
+            win32gui.ShowWindow(hwnd, win32con.SW_MINIMIZE)
+            self.signals.status.emit(f'📉 已最小化窗口')
+            return True
+        except Exception as e:
+            self.signals.status.emit(f'❌ 最小化失败: {str(e)}')
+            return False
+    
+    def click_at_position(self, hwnd, x_offset, y_offset, button='left'):
+        """
+        封装的点击函数
+        :param hwnd: 窗口句柄
+        :param x_offset: X轴偏移量
+        :param y_offset: Y轴偏移量
+        :param button: 'left' 或 'right'
+        """
+        # 获取窗口位置
+        rect = win32gui.GetWindowRect(hwnd)
+        window_x = rect[0]
+        window_y = rect[1]
+        
+        # 计算点击位置
+        click_x = window_x + x_offset
+        click_y = window_y + y_offset
+        
+        # 移动鼠标到目标位置
+        win32api.SetCursorPos((click_x, click_y))
+        time.sleep(0.2)
+        
+        # 模拟鼠标点击
+        if button == 'left':
+            win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN, click_x, click_y, 0, 0)
+            time.sleep(0.1)
+            win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP, click_x, click_y, 0, 0)
+            self.signals.status.emit(f'🖱️ 左键点击: ({click_x}, {click_y})')
+        elif button == 'right':
+            win32api.mouse_event(win32con.MOUSEEVENTF_RIGHTDOWN, click_x, click_y, 0, 0)
+            time.sleep(0.1)
+            win32api.mouse_event(win32con.MOUSEEVENTF_RIGHTUP, click_x, click_y, 0, 0)
+            self.signals.status.emit(f'🖱️ 右键点击: ({click_x}, {click_y})')
+        
+        return click_x, click_y
+    
+    def get_pixel_color(self, hwnd, x_offset, y_offset):
+        try:
+            # 获取窗口位置
+            rect = win32gui.GetWindowRect(hwnd)
+            window_x = rect[0]
+            window_y = rect[1]
+            
+            # 计算屏幕坐标
+            screen_x = window_x + x_offset
+            screen_y = window_y + y_offset
+            
+            # 获取桌面DC
+            hdc = win32gui.GetDC(0)  # 0 表示桌面
+            
+            color_ref = win32gui.GetPixel(hdc, screen_x, screen_y)
+            
+            win32gui.ReleaseDC(0, hdc)
+            
+            red = color_ref & 0xFF
+            green = (color_ref >> 8) & 0xFF
+            blue = (color_ref >> 16) & 0xFF
+            
+            return f"{red:02X}{green:02X}{blue:02X}"
+            
+        except Exception as e:
+            self.log_text.append(f'❌ 取色失败: {str(e)}')
+            return "000000"
+    
+    def check_and_click(self, hwnd, check_x, check_y, target_color, 
+                        click_x, click_y, button='left', check_type='equal'):
+        """
+        检查颜色并执行点击
+        
+        :param hwnd: 窗口句柄
+        :param check_x: 检查颜色的X偏移
+        :param check_y: 检查颜色的Y偏移
+        :param target_color: 目标颜色 (如 "FFFFFF")
+        :param click_x: 点击的X偏移
+        :param click_y: 点击的Y偏移
+        :param button: 'left' 或 'right'
+        :param check_type: 'equal' 或 'not_equal'
+        :return: 是否执行了点击
+        """
+        # 获取当前颜色
+        current_color = self.get_pixel_color(hwnd, check_x, check_y)
+        
+        self.log_text.append(f'🎨 检测位置({check_x},{check_y})颜色: {current_color} (目标: {target_color})')
+        
+        should_click = False
+        if check_type == 'equal':
+            should_click = (current_color == target_color)
+        elif check_type == 'not_equal':
+            should_click = (current_color != target_color)
+        
+        if should_click:
+            self.log_text.append(f'✅ 条件满足,执行点击 ({click_x}, {click_y})')
+            self.click_at_position(hwnd, click_x, click_y, button)
+            return True
+        else:
+            self.log_text.append(f'❌ 条件不满足,跳过点击')
+            return False
+    
+    def start_auto_click(self):
+        """开始自动点击 - 批量执行"""
+        if self.is_running:
+            return
+        
+        # 获取所有选中的窗口
+        selected_windows = self.get_selected_windows()
+        if not selected_windows:
+            QMessageBox.warning(self, '警告', '请至少选择一个窗口')
+            return
+        
+        # 检查是否至少选择了一个功能
+        if not (self.cb_molong.isChecked() or self.cb_feicui.isChecked() or 
+                self.cb_jinyan.isChecked() or self.cb_chiyan.isChecked()):
+            QMessageBox.warning(self, '警告', '请至少选择一个功能')
+            return
+        
+        self.is_running = True
+        self.should_stop = False
+        self.start_btn.setEnabled(False)
+        self.stop_btn.setEnabled(True)
+        
+        # 记录选择的选项
+        selected = []
+        if self.cb_molong.isChecked(): selected.append('争夺魔龙之心')
+        if self.cb_feicui.isChecked(): selected.append('翡翠荣耀旗')
+        if self.cb_jinyan.isChecked(): selected.append('金岩霸者棋')
+        if self.cb_chiyan.isChecked(): selected.append('炽焰战神棋')
+        
+        # 记录皇宫占位选择
+        if self.radio_random.isChecked():
+            palace_pos = '随机'
+            palace_y = 444
+        elif self.radio_door.isChecked():
+            palace_pos = '门口'
+            palace_y = 457
+        else:  # corner
+            palace_pos = '角落'
+            palace_y = 470
+        
+        self.log_text.append(f'▶️ 开始批量执行,共 {len(selected_windows)} 个窗口')
+        self.log_text.append(f'📋 已选功能: {", ".join(selected)}')
+        self.log_text.append(f'🏰 皇宫占位: {palace_pos} (Y+{palace_y})')
+        self.log_text.append('=' * 50)
+        
+        # 在新线程中执行
+        thread = threading.Thread(target=self.batch_auto_click_worker, 
+                                 args=(selected_windows, palace_y))
+        thread.daemon = True
+        thread.start()
+    
+    def batch_auto_click_worker(self, selected_windows, palace_y):
+        """批量自动点击工作线程"""
+        total = len(selected_windows)
+        
+        for idx, (hwnd, title) in enumerate(selected_windows, 1):
+            # 检查是否被停止
+            if self.should_stop:
+                self.signals.status.emit(f'⏹️ 用户停止执行')
+                break
+            
+            self.signals.status.emit(f'📌 正在处理 [{idx}/{total}] {title}')
+            self.signals.progress.emit(idx, total)
+            
+            try:
+                # 执行单个窗口的点击流程
+                self.execute_single_window(hwnd, title, palace_y)
+                
+                # 等待5秒后最小化窗口
+                self.signals.status.emit(f'⏳ 等待5秒后最小化窗口...')
+                time.sleep(5)
+                
+                # 最小化窗口
+                self.signals.status.emit(f'📉 最小化窗口: {title}')
+                self.minimize_window(hwnd)
+                
+                # 窗口间间隔1秒
+                if idx < total and not self.should_stop:
+                    self.signals.status.emit(f'⏳ 等待1秒后处理下一个窗口...')
+                    time.sleep(1)
+                    
+            except Exception as e:
+                self.signals.status.emit(f'❌ 处理窗口失败: {str(e)}')
+                import traceback
+                self.log_text.append(traceback.format_exc())
+                # 出错后继续下一个窗口
+        
+        if not self.should_stop:
+            self.signals.status.emit(f'✅ 批量执行完成!共处理 {idx if idx < total else total} 个窗口')
+        self.signals.finished.emit()
+    
+    def execute_single_window(self, hwnd, title, palace_y):
+        """执行单个窗口的点击流程"""
+        # 1. 激活窗口
+        self.signals.status.emit(f'🔄 正在激活窗口: {title}')
+        
+        # 显示窗口
+        win32gui.ShowWindow(hwnd, win32con.SW_SHOW)
+        if win32gui.IsIconic(hwnd):
+            win32gui.ShowWindow(hwnd, win32con.SW_RESTORE)
+        
+        # 激活窗口
+        win32gui.SetForegroundWindow(hwnd)
+        time.sleep(2)  # 等待窗口激活
+        
+        # 步骤1: 左键点击 X+63, Y+81
+        self.click_at_position(hwnd, 29, 41, 'left')
+        time.sleep(1)
+        self.signals.status.emit(f'📍 步骤1: 左键点击 X+63, Y+81')
+        self.click_at_position(hwnd, 63, 81, 'left')
+        
+        # 等待4秒
+        self.signals.status.emit(f'⏳ 等待2秒...')
+        time.sleep(6)
+        
+        # 步骤2: 右键点击 X+413, Y+168
+        self.signals.status.emit(f'📍 步骤2: 右键点击 X+413, Y+168')
+        self.click_at_position(hwnd, 413, 168, 'right')
+        
+        # 等待6秒
+        self.signals.status.emit(f'⏳ 等待6秒...')
+        time.sleep(6)
+        
+        # 处理"争夺魔龙之心"
+        if self.cb_molong.isChecked():
+            self.signals.status.emit(f'📍 检查争夺魔龙之心')
+            self.check_and_click(
+                hwnd=hwnd,
+                check_x=642,
+                check_y=439,
+                target_color="FFFFFF",
+                click_x=642,
+                click_y=439,
+                button='left',
+                check_type='equal'
+            )
+        else:
+            self.signals.status.emit(f'📍 未选争夺魔龙之心,执行相反逻辑')
+            self.check_and_click(
+                hwnd=hwnd,
+                check_x=642,
+                check_y=437,
+                target_color="FFFFFF",
+                click_x=642,
+                click_y=437,
+                button='left',
+                check_type='not_equal'
+            )
+        
+        # 等待1秒
+        time.sleep(0.2)
+        
+        # 处理"翡翠荣耀旗"
+        if self.cb_feicui.isChecked():
+            self.signals.status.emit(f'📍 检查翡翠荣耀旗')
+            self.check_and_click(
+                hwnd=hwnd,
+                check_x=769,
+                check_y=386,
+                target_color="FFFFFF",
+                click_x=769,
+                click_y=386,
+                button='left',
+                check_type='equal'
+            )
+        else:
+            self.signals.status.emit(f'📍 未选翡翠荣耀旗,执行相反逻辑')
+            self.check_and_click(
+                hwnd=hwnd,
+                check_x=769,
+                check_y=386,
+                target_color="FFFFFF",
+                click_x=769,
+                click_y=386,
+                button='left',
+                check_type='not_equal'
+            )
+        
+        # 等待1秒
+        time.sleep(0.2)
+        
+        # 处理"金岩霸者棋"
+        if self.cb_jinyan.isChecked():
+            self.signals.status.emit(f'📍 检查金岩霸者棋')
+            self.check_and_click(
+                hwnd=hwnd,
+                check_x=769,
+                check_y=400,
+                target_color="FFFFFF",
+                click_x=769,
+                click_y=400,
+                button='left',
+                check_type='equal'
+            )
+        else:
+            self.signals.status.emit(f'📍 未选金岩霸者棋,执行相反逻辑')
+            self.check_and_click(
+                hwnd=hwnd,
+                check_x=769,
+                check_y=400,
+                target_color="FFFFFF",
+                click_x=769,
+                click_y=400,
+                button='left',
+                check_type='not_equal'
+            )
+        
+        # 等待1秒
+        time.sleep(0.2)
+        
+        # 处理"炽焰战神棋"
+        if self.cb_chiyan.isChecked():
+            self.signals.status.emit(f'📍 检查炽焰战神棋')
+            self.check_and_click(
+                hwnd=hwnd,
+                check_x=767,
+                check_y=415,
+                target_color="FFFFFF",
+                click_x=767,
+                click_y=415,
+                button='left',
+                check_type='equal'
+            )
+        else:
+            self.signals.status.emit(f'📍 未选炽焰战神棋,执行相反逻辑')
+            self.check_and_click(
+                hwnd=hwnd,
+                check_x=767,
+                check_y=415,
+                target_color="FFFFFF",
+                click_x=767,
+                click_y=415,
+                button='left',
+                check_type='not_equal'
+            )
+        
+        # 等待1秒
+        time.sleep(0.2)
+        
+        # 最后点击 X+871, Y+266
+        self.signals.status.emit(f'📍 最后点击: X+871, Y+266')
+        self.click_at_position(hwnd, 871, 266, 'left')
+
+        time.sleep(1)
+        self.click_at_position(hwnd, 671, 70, 'right')
+
+        time.sleep(1)
+        self.click_at_position(hwnd, 714, 426, 'left')
+
+        time.sleep(1)
+        
+        # 皇宫占位点击 - 根据选择的选项
+        self.signals.status.emit(f'🏰 皇宫占位点击: X+716, Y+{palace_y}')
+        self.click_at_position(hwnd, 716, palace_y, 'left')
+        
+        time.sleep(1)
+        self.click_at_position(hwnd, 901, 311, 'left')
+
+        time.sleep(1)
+        self.click_at_position(hwnd, 1133, 8, 'left')
+        
+        self.signals.status.emit(f'✅ 窗口 {title} 执行完成')
+    
+    def stop_auto_click(self):
+        """停止自动点击"""
+        self.should_stop = True
+        self.is_running = False
+        self.start_btn.setEnabled(True)
+        self.stop_btn.setEnabled(False)
+        self.status_label.setText('正在停止...')
+        self.log_text.append('⏹️ 正在停止执行...')
+    
+    def update_status(self, message):
+        """更新状态"""
+        self.status_label.setText(message)
+        self.log_text.append(message)
+        # 滚动到底部
+        self.log_text.verticalScrollBar().setValue(
+            self.log_text.verticalScrollBar().maximum()
+        )
+    
+    def update_progress(self, current, total):
+        """更新进度"""
+        self.status_label.setText(f'进度: {current}/{total}')
+    
+    def on_finished(self):
+        """完成回调"""
+        self.is_running = False
+        self.start_btn.setEnabled(True)
+        self.stop_btn.setEnabled(False)
+        self.status_label.setText('就绪')
+        self.log_text.append('=' * 50)
+
+
+def main():
+    app = QApplication(sys.argv)
+    app.setStyle('Fusion')
+    
+    window = WindowManagerApp()
+    window.show()
+    
+    sys.exit(app.exec_())
+
+
+if __name__ == '__main__':
+    main()

+ 1 - 1
test.py

@@ -11,7 +11,7 @@ headers = {
     "referer": "https://cppaidan.com/paidan/system/user"
 }
 
-password = 10303
+password = 67745
 while password < 1000000:
     try:
         response = requests.post(

+ 68 - 8
控制vm虚拟机.py

@@ -627,10 +627,40 @@ class VmControlGUI:
                 self.press_key(0xBB, False)
                 time.sleep(wait_time)
                 
-                # 按 小键盘2 等待2-2.7秒
-                wait_time = random.uniform(2.0, 2.7)
-                self.log(f"  按 小键盘2 (延迟 {wait_time:.1f}秒)")
-                self.press_key(0x62, True)
+                # 按 3 等待300-500ms
+                wait_time = random.uniform(0.3, 0.5)
+                self.log(f"  按 3 (延迟 {wait_time*1000:.0f}ms)")
+                self.press_key(0x33, False)
+                time.sleep(wait_time)
+                
+                # 按 3 等待2-2.5秒
+                wait_time = random.uniform(2.0, 2.5)
+                self.log(f"  按 3 (延迟 {wait_time:.1f}秒)")
+                self.press_key(0x33, False)
+                time.sleep(wait_time)
+                
+                # 按 4 等待300-500ms
+                wait_time = random.uniform(0.3, 0.5)
+                self.log(f"  按 4 (延迟 {wait_time*1000:.0f}ms)")
+                self.press_key(0x34, False)
+                time.sleep(wait_time)
+                
+                # 按 4 等待2-2.5秒
+                wait_time = random.uniform(2.0, 2.5)
+                self.log(f"  按 4 (延迟 {wait_time:.1f}秒)")
+                self.press_key(0x34, False)
+                time.sleep(wait_time)
+                
+                # 按 5 等待300-500ms
+                wait_time = random.uniform(0.3, 0.5)
+                self.log(f"  按 5 (延迟 {wait_time*1000:.0f}ms)")
+                self.press_key(0x35, False)
+                time.sleep(wait_time)
+                
+                # 按 5 等待5-10秒
+                wait_time = random.uniform(5.0, 10.0)
+                self.log(f"  按 5 (延迟 {wait_time:.1f}秒)")
+                self.press_key(0x35, False)
                 time.sleep(wait_time)
                 
                 # 按 ESC
@@ -751,10 +781,40 @@ class VmControlGUI:
                 self.press_key(0xBB, False)
                 time.sleep(wait_time)
                 
-                # 按 小键盘2 等待2-2.7秒
-                wait_time = random.uniform(2.0, 2.7)
-                self.log(f"  按 小键盘2 (延迟 {wait_time:.1f}秒)")
-                self.press_key(0x62, True)
+                # 按 3 等待300-500ms
+                wait_time = random.uniform(0.3, 0.5)
+                self.log(f"  按 3 (延迟 {wait_time*1000:.0f}ms)")
+                self.press_key(0x33, False)
+                time.sleep(wait_time)
+                
+                # 按 3 等待2-2.5秒
+                wait_time = random.uniform(2.0, 2.5)
+                self.log(f"  按 3 (延迟 {wait_time:.1f}秒)")
+                self.press_key(0x33, False)
+                time.sleep(wait_time)
+                
+                # 按 4 等待300-500ms
+                wait_time = random.uniform(0.3, 0.5)
+                self.log(f"  按 4 (延迟 {wait_time*1000:.0f}ms)")
+                self.press_key(0x34, False)
+                time.sleep(wait_time)
+                
+                # 按 4 等待2-2.5秒
+                wait_time = random.uniform(2.0, 2.5)
+                self.log(f"  按 4 (延迟 {wait_time:.1f}秒)")
+                self.press_key(0x34, False)
+                time.sleep(wait_time)
+                
+                # 按 5 等待300-500ms
+                wait_time = random.uniform(0.3, 0.5)
+                self.log(f"  按 5 (延迟 {wait_time*1000:.0f}ms)")
+                self.press_key(0x35, False)
+                time.sleep(wait_time)
+                
+                # 按 5 等待5-10秒
+                wait_time = random.uniform(5.0, 10.0)
+                self.log(f"  按 5 (延迟 {wait_time:.1f}秒)")
+                self.press_key(0x35, False)
                 time.sleep(wait_time)
                 
                 # 按 ESC

+ 541 - 188
解密机大侠.py

@@ -5,12 +5,17 @@ import os
 import re
 import uuid
 import hashlib
+import time
+import threading
+from concurrent.futures import ThreadPoolExecutor, as_completed
 from Crypto.Cipher import AES
 from Crypto.Util.Padding import unpad
 from typing import Optional, List, Dict, Any
 from PyQt5.QtWidgets import *
 from PyQt5.QtCore import *
 from PyQt5.QtGui import *
+from requests.adapters import HTTPAdapter
+from urllib3.util.retry import Retry
 
 # --- 核心功能类 ---
 class OrderManager:
@@ -37,6 +42,26 @@ class OrderManager:
         }
         self.key = b"S9u978T13NLCGc5W"
         self.iv = b"X83yWMD9iKhLxfwX"
+        
+        # 缓存
+        self.order_detail_cache = {}
+        self.ocr_cache = {}
+        self.cache_lock = threading.Lock()
+        
+        # 创建会话并配置连接池
+        self.session = requests.Session()
+        retry_strategy = Retry(
+            total=2,
+            backoff_factor=0.5,
+            status_forcelist=[429, 500, 502, 503, 504],
+        )
+        adapter = HTTPAdapter(
+            pool_connections=20,
+            pool_maxsize=20,
+            max_retries=retry_strategy
+        )
+        self.session.mount('http://', adapter)
+        self.session.mount('https://', adapter)
 
     def decrypt_order_data(self, hex_string):
         """解密订单数据"""
@@ -62,7 +87,7 @@ class OrderManager:
         }
         
         try:
-            response = requests.post(url, headers=self.headers, json=payload, timeout=30)
+            response = self.session.post(url, headers=self.headers, json=payload, timeout=30)
             response.raise_for_status()
             result = response.json()
             
@@ -74,13 +99,18 @@ class OrderManager:
             return None, str(e)
 
     def get_order_detail(self, order_no):
-        """获取订单详情"""
+        """获取订单详情(带缓存)"""
+        # 检查缓存
+        with self.cache_lock:
+            if order_no in self.order_detail_cache:
+                return self.order_detail_cache[order_no], None
+        
         # 获取BiddingNo
         snatch_url = "https://wirelessgate.aihuishou.com/jdx-rc-service/front/bid/snatch"
         snatch_payload = {"orderNo": order_no}
         
         try:
-            snatch_response = requests.post(snatch_url, headers=self.headers, json=snatch_payload, timeout=30)
+            snatch_response = self.session.post(snatch_url, headers=self.headers, json=snatch_payload, timeout=15)
             snatch_response.raise_for_status()
             snatch_data = snatch_response.json()
             
@@ -98,7 +128,7 @@ class OrderManager:
         detail_url = f"https://wirelessgate.aihuishou.com/jdx-qa-service/app/ka/v3/order/detail?orderNo={order_no}&biddingNo={bidding_no}"
         
         try:
-            detail_response = requests.get(detail_url, headers=self.headers, timeout=30)
+            detail_response = self.session.get(detail_url, headers=self.headers, timeout=15)
             detail_response.raise_for_status()
             detail_data = detail_response.json()
             
@@ -112,90 +142,82 @@ class OrderManager:
             decrypted_json_string = self.decrypt_order_data(encrypted_hex)
             order_detail = json.loads(decrypted_json_string)
             
+            # 存入缓存
+            with self.cache_lock:
+                self.order_detail_cache[order_no] = order_detail
+            
             return order_detail, None
             
         except Exception as e:
             return None, str(e)
 
     def find_original_image_by_notice(self, json_data, notice_keyword):
-        """查找图片链接"""
+        """优化的图片查找(使用栈迭代)"""
         if isinstance(json_data, str):
             try:
                 json_data = json.loads(json_data)
             except json.JSONDecodeError:
                 return None
-
-        def recursive_search(obj):
+        
+        # 使用栈迭代而非递归
+        stack = [json_data]
+        keyword_lower = notice_keyword.lower()
+        
+        while stack:
+            obj = stack.pop()
+            
             if isinstance(obj, dict):
+                # 检查当前字典
                 if 'notice' in obj and 'originalImage' in obj:
-                    if notice_keyword.lower() in obj['notice'].lower():
+                    if keyword_lower in obj['notice'].lower():
                         return obj['originalImage']
                 
+                # 将所有值加入栈
                 for value in obj.values():
-                    result = recursive_search(value)
-                    if result:
-                        return result
-            
+                    stack.append(value)
+                    
             elif isinstance(obj, list):
-                for item in obj:
-                    result = recursive_search(item)
-                    if result:
-                        return result
-            
-            return None
-
-        return recursive_search(json_data)
+                # 将列表元素加入栈
+                stack.extend(obj)
+        
+        return None
 
     def get_all_image_notices(self, json_data):
         """获取所有图片的notice信息"""
         notices = []
+        stack = [json_data]
         
-        def recursive_search(obj):
+        while stack:
+            obj = stack.pop()
+            
             if isinstance(obj, dict):
                 if 'notice' in obj and 'image' in obj:
                     notices.append(obj['notice'])
                 for value in obj.values():
-                    recursive_search(value)
+                    stack.append(value)
             elif isinstance(obj, list):
-                for item in obj:
-                    recursive_search(item)
+                stack.extend(obj)
         
-        recursive_search(json_data)
-        return list(set(notices))  # 去重
+        return list(set(notices))
 
-    def download_and_ocr_image(self, image_url):
-        """下载图片并OCR识别"""
+    def ocr_by_url(self, image_url):
+        """通过URL进行OCR识别(带缓存)"""
+        # 检查缓存
+        cache_key = hashlib.md5(image_url.encode()).hexdigest()
+        with self.cache_lock:
+            if cache_key in self.ocr_cache:
+                return self.ocr_cache[cache_key]
+        
         try:
-            # 下载图片
-            response = requests.get(image_url, timeout=30)
-            response.raise_for_status()
-            
-            # 创建temp目录
-            os.makedirs('./temp', exist_ok=True)
-            
-            # 保存临时文件
-            url_path = image_url.split('?')[0]
-            file_ext = os.path.splitext(url_path)[1]
-            if not file_ext:
-                file_ext = '.jpg'
-            random_filename = f"{uuid.uuid4().hex}{file_ext}"
-            temp_path = os.path.join('./temp', random_filename)
+            payload = {"url": image_url}
+            response = self.session.post(self.ocr_url, json=payload, timeout=30)
             
-            with open(temp_path, 'wb') as f:
-                f.write(response.content)
-            
-            # 发送到OCR服务
-            with open(temp_path, 'rb') as f:
-                files = {'image': (random_filename, f, 'image/jpeg')}
-                ocr_response = requests.post(self.ocr_url, files=files, timeout=60)
-            
-            # 清理临时文件
-            if os.path.exists(temp_path):
-                os.remove(temp_path)
-            
-            if ocr_response.status_code == 200:
-                ocr_result = ocr_response.json()
+            if response.status_code == 200:
+                ocr_result = response.json()
                 if ocr_result.get('success'):
+                    # 存入缓存
+                    with self.cache_lock:
+                        self.ocr_cache[cache_key] = ocr_result
                     return ocr_result
             return None
             
@@ -217,24 +239,128 @@ class OrderManager:
 
     def extract_charge_count(self, text):
         """提取充电次数"""
-        pattern = r'充电次数(\d+)次'
-        match = re.search(pattern, text)
+        # 匹配(232次循环)格式
+        pattern1 = r'[((]\s*(\d+)\s*次\s*循环\s*[))]'
+        match = re.search(pattern1, text)
+        if match:
+            return int(match.group(1))
+        
+        # 匹配充电次数123次格式
+        pattern2 = r'充电次数(\d+)次'
+        match = re.search(pattern2, text)
+        if match:
+            return int(match.group(1))
+        
+        # 匹配单独的 数字+次 格式(如 71477次)
+        pattern3 = r'(\d+)次'
+        match = re.search(pattern3, text)
         if match:
             return int(match.group(1))
+        
         return None
+    
+    def find_best_image(self, order_detail):
+        """查找最佳验机图片(按优先级)"""
+        keywords = [
+            "苹果沙漏验机报告",
+            "沙漏图", 
+            "苹果验机软件",
+            "苹果沙漏验机基本信息",
+            "验机照片",
+            "验机报告"
+        ]
+        
+        for keyword in keywords:
+            img = self.find_original_image_by_notice(order_detail, keyword)
+            if img:
+                return img
+        return None
+
+# --- 后台工作线程 ---
+class OrderListWorker(QThread):
+    finished = pyqtSignal(list)
+    error = pyqtSignal(str)
 
-# --- GUI主窗口 ---
+    def __init__(self, order_manager):
+        super().__init__()
+        self.order_manager = order_manager
+
+    def run(self):
+        try:
+            order_list, error = self.order_manager.get_pending_order_list(100)
+            if error:
+                self.error.emit(error)
+            else:
+                self.finished.emit(order_list)
+        except Exception as e:
+            self.error.emit(str(e))
+
+class ChargeQueryWorker(QThread):
+    finished = pyqtSignal(int, object, object, list)
+    error = pyqtSignal(int, str)
+
+    def __init__(self, order_manager, order_no, index):
+        super().__init__()
+        self.order_manager = order_manager
+        self.order_no = order_no
+        self.index = index
+
+    def run(self):
+        try:
+            # 获取订单详情
+            order_detail, error = self.order_manager.get_order_detail(self.order_no)
+            if error:
+                self.error.emit(self.index, error)
+                return
+            
+            # 查找所有图片的notice
+            image_notices = self.order_manager.get_all_image_notices(order_detail)
+            
+            # 查找验机图片
+            report_img = self.order_manager.find_best_image(order_detail)
+            
+            if not report_img:
+                self.finished.emit(self.index, None, None, image_notices)
+                return
+            
+            # OCR识别
+            ocr_result = self.order_manager.ocr_by_url(report_img)
+            if not ocr_result:
+                self.finished.emit(self.index, None, None, image_notices)
+                return
+            
+            # 提取充电次数
+            ocr_text = self.order_manager.extract_text_from_ocr_result(ocr_result)
+            charge_count = self.order_manager.extract_charge_count(ocr_text)
+            
+            self.finished.emit(self.index, charge_count, ocr_result, image_notices)
+            
+        except Exception as e:
+            self.error.emit(self.index, str(e))
+
+# --- 主窗口 ---
 class MainWindow(QMainWindow):
     def __init__(self):
         super().__init__()
         self.order_manager = None
         self.order_list = []
-        self.order_details_cache = {}  # 缓存订单详情
-        self.charge_counts = {}  # 缓存充电次数
+        self.order_details_cache = {}
+        self.charge_counts = {}
+        self.is_batch_processing = False
+        self.batch_queue = []
+        self.current_batch_index = 0
+        
+        # 并行处理相关
+        self.thread_pool = ThreadPoolExecutor(max_workers=5)
+        self.future_tasks = {}
+        self.processing_lock = threading.Lock()
+        self.completed_count = 0
+        self.total_tasks = 0
+        
         self.init_ui()
 
     def init_ui(self):
-        self.setWindowTitle('爱回收订单查询工具')
+        self.setWindowTitle('爱回收订单查询工具 (优化版)')
         self.setGeometry(100, 100, 1200, 800)
         
         # 设置样式
@@ -268,6 +394,15 @@ class MainWindow(QMainWindow):
             QPushButton:disabled {
                 background-color: #cccccc;
             }
+            QPushButton#batch_btn {
+                background-color: #28a745;
+            }
+            QPushButton#batch_btn:hover {
+                background-color: #218838;
+            }
+            QPushButton#batch_btn:disabled {
+                background-color: #cccccc;
+            }
             QLineEdit, QTextEdit {
                 border: 1px solid #ccc;
                 border-radius: 4px;
@@ -297,9 +432,17 @@ class MainWindow(QMainWindow):
                 font-family: 'Consolas', 'Courier New', monospace;
                 font-size: 11px;
             }
+            QProgressBar {
+                border: 1px solid #ccc;
+                border-radius: 4px;
+                text-align: center;
+            }
+            QProgressBar::chunk {
+                background-color: #0078d7;
+                border-radius: 4px;
+            }
         """)
 
-        # 中央部件
         central_widget = QWidget()
         self.setCentralWidget(central_widget)
         main_layout = QVBoxLayout(central_widget)
@@ -309,26 +452,29 @@ class MainWindow(QMainWindow):
         config_group = QGroupBox("配置")
         config_layout = QGridLayout()
         
-        # Token输入
         config_layout.addWidget(QLabel("Access Token:"), 0, 0)
         self.token_input = QLineEdit()
         self.token_input.setText("jdx9ce52336abee4cf78db56567dc259512")
         self.token_input.setEchoMode(QLineEdit.Password)
         config_layout.addWidget(self.token_input, 0, 1)
         
-        # 显示/隐藏Token按钮
         self.show_token_btn = QPushButton("显示")
         self.show_token_btn.setFixedWidth(60)
         self.show_token_btn.clicked.connect(self.toggle_token_visibility)
         config_layout.addWidget(self.show_token_btn, 0, 2)
         
-        # OCR地址输入
         config_layout.addWidget(QLabel("OCR地址:"), 1, 0)
         self.ocr_input = QLineEdit()
-        self.ocr_input.setText("http://192.168.0.110:10001/ocr")
+        self.ocr_input.setText("http://111.229.45.38:19100/ocr/url")
         config_layout.addWidget(self.ocr_input, 1, 1, 1, 2)
         
-        # 操作按钮
+        # 并发数设置
+        config_layout.addWidget(QLabel("并发数:"), 2, 0)
+        self.thread_count_combo = QComboBox()
+        self.thread_count_combo.addItems(["3", "5", "8", "10", "15"])
+        self.thread_count_combo.setCurrentText("5")
+        config_layout.addWidget(self.thread_count_combo, 2, 1)
+        
         btn_layout = QHBoxLayout()
         self.search_btn = QPushButton("🔍 搜索订单")
         self.search_btn.clicked.connect(self.search_orders)
@@ -340,8 +486,15 @@ class MainWindow(QMainWindow):
         self.refresh_btn.setFixedHeight(35)
         btn_layout.addWidget(self.refresh_btn)
         
+        self.batch_btn = QPushButton("⚡ 并行查询充电次数")
+        self.batch_btn.setObjectName("batch_btn")
+        self.batch_btn.clicked.connect(self.batch_check_charge_counts_parallel)
+        self.batch_btn.setFixedHeight(35)
+        self.batch_btn.setEnabled(False)
+        btn_layout.addWidget(self.batch_btn)
+        
         btn_layout.addStretch()
-        config_layout.addLayout(btn_layout, 2, 0, 1, 3)
+        config_layout.addLayout(btn_layout, 3, 0, 1, 3)
         
         config_group.setLayout(config_layout)
         main_layout.addWidget(config_group)
@@ -350,7 +503,10 @@ class MainWindow(QMainWindow):
         list_group = QGroupBox("订单列表")
         list_layout = QVBoxLayout()
         
-        # 表格
+        self.progress_bar = QProgressBar()
+        self.progress_bar.setVisible(False)
+        list_layout.addWidget(self.progress_bar)
+        
         self.table = QTableWidget()
         self.table.setColumnCount(7)
         self.table.setHorizontalHeaderLabels(["序号", "订单号", "商品信息", "IMEI", "预估价格", "门店", "充电次数"])
@@ -372,14 +528,12 @@ class MainWindow(QMainWindow):
         log_group = QGroupBox("日志")
         log_layout = QVBoxLayout()
         
-        # 日志文本框
         self.log_text = QTextEdit()
         self.log_text.setObjectName("log_text")
         self.log_text.setReadOnly(True)
         self.log_text.setMaximumHeight(200)
         log_layout.addWidget(self.log_text)
         
-        # 日志控制按钮
         log_btn_layout = QHBoxLayout()
         self.clear_log_btn = QPushButton("清空日志")
         self.clear_log_btn.clicked.connect(self.clear_log)
@@ -391,13 +545,11 @@ class MainWindow(QMainWindow):
         log_group.setLayout(log_layout)
         main_layout.addWidget(log_group)
 
-        # --- 底部状态栏 ---
         self.status_bar = QStatusBar()
         self.setStatusBar(self.status_bar)
         self.status_bar.showMessage("就绪")
 
     def toggle_token_visibility(self):
-        """切换Token显示/隐藏"""
         if self.token_input.echoMode() == QLineEdit.Password:
             self.token_input.setEchoMode(QLineEdit.Normal)
             self.show_token_btn.setText("隐藏")
@@ -406,27 +558,24 @@ class MainWindow(QMainWindow):
             self.show_token_btn.setText("显示")
 
     def log_message(self, message, level="INFO"):
-        """添加日志消息"""
         timestamp = QDateTime.currentDateTime().toString("yyyy-MM-dd hh:mm:ss")
         color_map = {
             "INFO": "#d4d4d4",
             "WARNING": "#ffa500",
             "ERROR": "#ff4444",
-            "SUCCESS": "#4caf50"
+            "SUCCESS": "#4caf50",
+            "PROGRESS": "#00bcd4"
         }
         color = color_map.get(level, "#d4d4d4")
         html = f'<span style="color: #888888;">[{timestamp}]</span> <span style="color: {color};">[{level}]</span> {message}<br>'
         self.log_text.append(html)
-        # 滚动到底部
         scrollbar = self.log_text.verticalScrollBar()
         scrollbar.setValue(scrollbar.maximum())
 
     def clear_log(self):
-        """清空日志"""
         self.log_text.clear()
 
     def search_orders(self):
-        """搜索订单"""
         access_token = self.token_input.text().strip()
         ocr_url = self.ocr_input.text().strip()
         
@@ -440,74 +589,62 @@ class MainWindow(QMainWindow):
         
         self.log_message("开始搜索订单...", "INFO")
         
-        # 初始化订单管理器
         self.order_manager = OrderManager(access_token, ocr_url)
-        
-        # 清空缓存
         self.order_details_cache = {}
         self.charge_counts = {}
+        self.batch_btn.setEnabled(False)
         
         self.status_bar.showMessage("正在获取订单列表...")
         self.search_btn.setEnabled(False)
         
-        # 在后台线程执行
         self.worker = OrderListWorker(self.order_manager)
         self.worker.finished.connect(self.on_orders_loaded)
         self.worker.error.connect(self.on_error)
         self.worker.start()
 
     def refresh_orders(self):
-        """刷新订单列表"""
         if self.order_manager:
             self.search_orders()
         else:
             QMessageBox.warning(self, "提示", "请先搜索订单")
 
     def on_orders_loaded(self, order_list):
-        """订单列表加载完成"""
         self.order_list = order_list
         self.display_orders()
         self.search_btn.setEnabled(True)
+        self.batch_btn.setEnabled(True)
         self.status_bar.showMessage(f"成功加载 {len(order_list)} 条订单")
         self.log_message(f"成功加载 {len(order_list)} 条订单", "SUCCESS")
 
     def on_error(self, error_msg):
-        """错误处理"""
         self.search_btn.setEnabled(True)
+        self.batch_btn.setEnabled(False)
         self.status_bar.showMessage("加载失败")
         self.log_message(f"获取订单列表失败: {error_msg}", "ERROR")
         QMessageBox.critical(self, "错误", f"获取订单列表失败:\n{error_msg}")
 
     def display_orders(self):
-        """显示订单列表"""
         self.table.setRowCount(len(self.order_list))
         
         for i, order in enumerate(self.order_list):
-            # 序号
             self.table.setItem(i, 0, QTableWidgetItem(str(i + 1)))
             
-            # 订单号
             order_no = order.get('orderNo', '')
             self.table.setItem(i, 1, QTableWidgetItem(order_no))
             
-            # 商品信息
             brand_info = order.get('brandInfo', '')
             self.table.setItem(i, 2, QTableWidgetItem(brand_info))
             
-            # IMEI
             imei = order.get('imei', '')
             self.table.setItem(i, 3, QTableWidgetItem(imei))
             
-            # 预估价格
             price = order.get('price', '')
             price_str = str(price) if price else '待定'
             self.table.setItem(i, 4, QTableWidgetItem(price_str))
             
-            # 门店
             title = order.get('title', '')
             self.table.setItem(i, 5, QTableWidgetItem(title))
             
-            # 充电次数(初始为空,显示"查询"按钮)
             charge_widget = QWidget()
             charge_layout = QHBoxLayout(charge_widget)
             charge_layout.setContentsMargins(5, 2, 5, 2)
@@ -527,7 +664,6 @@ class MainWindow(QMainWindow):
             self.table.setCellWidget(i, 6, charge_widget)
 
     def check_charge_count(self, index):
-        """查询充电次数"""
         if not self.order_manager:
             return
         
@@ -539,47 +675,333 @@ class MainWindow(QMainWindow):
             return
         
         self.log_message(f"开始查询订单 {order_no} 的充电次数", "INFO")
-        
-        # 检查是否已缓存
-        if order_no in self.charge_counts:
-            count = self.charge_counts[order_no]
-            self.update_charge_display(index, count)
-            self.log_message(f"订单 {order_no} 充电次数: {count} (来自缓存)", "INFO")
-            return
-        
         self.status_bar.showMessage(f"正在查询订单 {order_no} 的充电次数...")
         
-        # 禁用按钮
         btn = self.findChild(QPushButton, f"check_btn_{index}")
         if btn:
             btn.setEnabled(False)
             btn.setText("查询中")
         
-        # 在后台线程执行
         self.charge_worker = ChargeQueryWorker(self.order_manager, order_no, index)
         self.charge_worker.finished.connect(self.on_charge_queried)
         self.charge_worker.error.connect(self.on_charge_error)
         self.charge_worker.start()
 
+    # ===== 并行批量查询 =====
+    def batch_check_charge_counts_parallel(self):
+        """并行批量查询充电次数"""
+        if not self.order_list:
+            QMessageBox.warning(self, "提示", "请先搜索订单")
+            return
+        
+        if self.is_batch_processing:
+            return
+        
+        # 检查哪些订单还没查询
+        pending_indices = []
+        for i, order in enumerate(self.order_list):
+            order_no = order.get('orderNo')
+            if order_no not in self.charge_counts:
+                pending_indices.append(i)
+        
+        if not pending_indices:
+            self.log_message("所有订单已查询完成", "SUCCESS")
+            QMessageBox.information(self, "提示", "所有订单已查询完成")
+            return
+        
+        # 关闭旧的线程池
+        if hasattr(self, 'thread_pool'):
+            self.thread_pool.shutdown(wait=False)
+        
+        # 获取并发数
+        max_workers = int(self.thread_count_combo.currentText())
+        self.thread_pool = ThreadPoolExecutor(max_workers=max_workers)
+        
+        self.is_batch_processing = True
+        self.completed_count = 0
+        self.total_tasks = len(pending_indices)
+        self.future_tasks = {}
+        
+        # 禁用按钮
+        self.batch_btn.setEnabled(False)
+        self.search_btn.setEnabled(False)
+        self.refresh_btn.setEnabled(False)
+        
+        # 显示进度条
+        self.progress_bar.setVisible(True)
+        self.progress_bar.setMaximum(len(pending_indices))
+        self.progress_bar.setValue(0)
+        
+        self.log_message(f"开始并行查询 {len(pending_indices)} 个订单 (并发数: {max_workers})", "PROGRESS")
+        
+        # 提交所有任务
+        token = self.token_input.text().strip()
+        ocr_url = self.ocr_input.text().strip()
+        
+        for idx in pending_indices:
+            order_no = self.order_list[idx].get('orderNo')
+            
+            # 提交任务到线程池
+            future = self.thread_pool.submit(
+                self.process_single_order_parallel,
+                token,
+                ocr_url,
+                order_no,
+                idx
+            )
+            self.future_tasks[future] = idx
+            
+            # 更新UI按钮状态
+            btn = self.findChild(QPushButton, f"check_btn_{idx}")
+            if btn:
+                btn.setEnabled(False)
+                btn.setText("查询中")
+        
+        # 启动结果收集线程
+        collector = threading.Thread(target=self.collect_parallel_results)
+        collector.daemon = True
+        collector.start()
+
+    def process_single_order_parallel(self, token, ocr_url, order_no, index):
+        """在独立线程中处理单个订单"""
+        try:
+            # 每个线程创建独立的OrderManager实例
+            order_manager = OrderManager(token, ocr_url)
+            
+            # 获取订单详情
+            order_detail, error = order_manager.get_order_detail(order_no)
+            if error:
+                return index, None, error, None
+            
+            # 查找验机图片
+            report_img = order_manager.find_best_image(order_detail)
+            if not report_img:
+                return index, None, "未找到验机图片", None
+            
+            # OCR识别
+            ocr_result = order_manager.ocr_by_url(report_img)
+            if not ocr_result:
+                return index, None, "OCR识别失败", None
+            
+            # 提取充电次数
+            ocr_text = order_manager.extract_text_from_ocr_result(ocr_result)
+            charge_count = order_manager.extract_charge_count(ocr_text)
+            
+            return index, charge_count, None, ocr_result
+            
+        except Exception as e:
+            return index, None, str(e), None
+
+    def collect_parallel_results(self):
+        """收集并行处理的结果"""
+        for future in as_completed(self.future_tasks):
+            index = self.future_tasks[future]
+            try:
+                idx, charge_count, error, ocr_result = future.result(timeout=60)
+                
+                # 更新UI(在主线程)
+                QMetaObject.invokeMethod(self, "update_parallel_result",
+                    Qt.QueuedConnection,
+                    Q_ARG(int, idx),
+                    Q_ARG(object, charge_count),
+                    Q_ARG(str, error or ""),
+                    Q_ARG(object, ocr_result)
+                )
+                
+            except Exception as e:
+                QMetaObject.invokeMethod(self, "update_parallel_result",
+                    Qt.QueuedConnection,
+                    Q_ARG(int, index),
+                    Q_ARG(object, None),
+                    Q_ARG(str, str(e)),
+                    Q_ARG(object, None)
+                )
+            
+            self.completed_count += 1
+            
+            # 更新进度(在主线程)
+            QMetaObject.invokeMethod(self, "update_batch_progress",
+                Qt.QueuedConnection,
+                Q_ARG(int, self.completed_count)
+            )
+        
+        # 全部完成
+        QMetaObject.invokeMethod(self, "on_batch_complete_parallel",
+            Qt.QueuedConnection
+        )
+
+    @pyqtSlot(int, object, str, object)
+    def update_parallel_result(self, index, charge_count, error, ocr_result):
+        """更新单个结果(在主线程执行)"""
+        order_no = self.order_list[index].get('orderNo')
+        
+        # 缓存结果
+        if charge_count is not None:
+            self.charge_counts[order_no] = charge_count
+            self.update_charge_display(index, charge_count)
+            self.log_message(f"订单 {order_no} 充电次数: {charge_count}", "SUCCESS")
+        else:
+            self.charge_counts[order_no] = None
+            self.update_charge_display(index, None)
+            if error:
+                self.log_message(f"订单 {order_no} 查询失败: {error}", "ERROR")
+        
+        # 恢复按钮
+        btn = self.findChild(QPushButton, f"check_btn_{index}")
+        if btn:
+            btn.setEnabled(True)
+            btn.setText("查询")
+
+    @pyqtSlot(int)
+    def update_batch_progress(self, completed):
+        """更新进度(在主线程执行)"""
+        self.progress_bar.setValue(completed)
+        self.status_bar.showMessage(f"并行处理中: {completed}/{self.total_tasks}")
+
+    @pyqtSlot()
+    def on_batch_complete_parallel(self):
+        """并行批量完成(在主线程执行)"""
+        self.is_batch_processing = False
+        self.future_tasks.clear()
+        
+        # 恢复按钮
+        self.batch_btn.setEnabled(True)
+        self.search_btn.setEnabled(True)
+        self.refresh_btn.setEnabled(True)
+        
+        # 隐藏进度条
+        self.progress_bar.setVisible(False)
+        
+        # 统计结果
+        success_count = sum(1 for v in self.charge_counts.values() if v is not None)
+        total_count = len(self.charge_counts)
+        
+        self.status_bar.showMessage(f"批量查询完成,成功 {success_count}/{total_count}")
+        self.log_message(f"批量查询完成,成功 {success_count}/{total_count}", "SUCCESS")
+        
+        QMessageBox.information(self, "完成", 
+            f"并行批量查询完成!\n\n成功找到充电次数: {success_count}/{total_count}\n总订单: {total_count}")
+
+    # ===== 原有的串行查询(保留作为备选) =====
+    def batch_check_charge_counts(self):
+        """串行批量查询(保留)"""
+        if not self.order_list:
+            QMessageBox.warning(self, "提示", "请先搜索订单")
+            return
+        
+        if self.is_batch_processing:
+            return
+        
+        pending_indices = []
+        for i, order in enumerate(self.order_list):
+            order_no = order.get('orderNo')
+            if order_no not in self.charge_counts:
+                pending_indices.append(i)
+        
+        if not pending_indices:
+            self.log_message("所有订单已查询完成", "SUCCESS")
+            QMessageBox.information(self, "提示", "所有订单已查询完成")
+            return
+        
+        self.is_batch_processing = True
+        self.batch_queue = pending_indices
+        self.current_batch_index = 0
+        
+        self.batch_btn.setEnabled(False)
+        self.search_btn.setEnabled(False)
+        self.refresh_btn.setEnabled(False)
+        
+        self.progress_bar.setVisible(True)
+        self.progress_bar.setMaximum(len(pending_indices))
+        self.progress_bar.setValue(0)
+        
+        self.log_message(f"开始串行查询 {len(pending_indices)} 个订单", "PROGRESS")
+        self.process_next_batch()
+
+    def process_next_batch(self):
+        if self.current_batch_index >= len(self.batch_queue):
+            self.on_batch_complete()
+            return
+        
+        index = self.batch_queue[self.current_batch_index]
+        order_no = self.order_list[index].get('orderNo')
+        
+        self.progress_bar.setValue(self.current_batch_index + 1)
+        self.status_bar.showMessage(f"串行处理中: {self.current_batch_index + 1}/{len(self.batch_queue)}")
+        
+        btn = self.findChild(QPushButton, f"check_btn_{index}")
+        if btn:
+            btn.setEnabled(False)
+            btn.setText("查询中")
+        
+        self.batch_worker = ChargeQueryWorker(self.order_manager, order_no, index)
+        self.batch_worker.finished.connect(self.on_batch_item_complete)
+        self.batch_worker.error.connect(self.on_batch_item_error)
+        self.batch_worker.start()
+
+    def on_batch_item_complete(self, index, charge_count, ocr_result, image_notices):
+        order_no = self.order_list[index].get('orderNo')
+        
+        self.charge_counts[order_no] = charge_count
+        self.update_charge_display(index, charge_count)
+        
+        btn = self.findChild(QPushButton, f"check_btn_{index}")
+        if btn:
+            btn.setEnabled(True)
+            btn.setText("查询")
+        
+        if charge_count is not None:
+            self.log_message(f"[{self.current_batch_index + 1}/{len(self.batch_queue)}] 订单 {order_no} 充电次数: {charge_count}", "SUCCESS")
+        else:
+            self.log_message(f"[{self.current_batch_index + 1}/{len(self.batch_queue)}] 订单 {order_no} 未找到充电次数", "WARNING")
+        
+        self.current_batch_index += 1
+        QTimer.singleShot(300, self.process_next_batch)
+
+    def on_batch_item_error(self, index, error_msg):
+        order_no = self.order_list[index].get('orderNo')
+        self.log_message(f"[{self.current_batch_index + 1}/{len(self.batch_queue)}] 订单 {order_no} 查询失败: {error_msg}", "ERROR")
+        
+        btn = self.findChild(QPushButton, f"check_btn_{index}")
+        if btn:
+            btn.setEnabled(True)
+            btn.setText("查询")
+        
+        self.current_batch_index += 1
+        QTimer.singleShot(300, self.process_next_batch)
+
+    def on_batch_complete(self):
+        self.is_batch_processing = False
+        self.batch_queue = []
+        
+        self.batch_btn.setEnabled(True)
+        self.search_btn.setEnabled(True)
+        self.refresh_btn.setEnabled(True)
+        
+        self.progress_bar.setVisible(False)
+        
+        success_count = sum(1 for v in self.charge_counts.values() if v is not None)
+        total_count = len(self.charge_counts)
+        
+        self.status_bar.showMessage(f"串行查询完成,成功 {success_count}/{total_count}")
+        self.log_message(f"串行查询完成,成功 {success_count}/{total_count}", "SUCCESS")
+        
+        QMessageBox.information(self, "完成", f"串行查询完成!\n\n成功找到充电次数: {success_count}/{total_count}")
+
+    # ===== 通用方法 =====
     def on_charge_queried(self, index, charge_count, ocr_result, image_notices):
-        """充电次数查询完成"""
         order_no = self.order_list[index].get('orderNo')
         
-        # 如果没有找到充电次数,输出详细信息
         if charge_count is None:
             if ocr_result:
                 self.log_message(f"订单 {order_no} 未找到充电次数,OCR识别结果:", "WARNING")
                 ocr_text = self.order_manager.extract_text_from_ocr_result(ocr_result)
                 if ocr_text:
-                    self.log_message(f"OCR文本: {ocr_text[:200]}...", "INFO")
+                    self.log_message(f"OCR文本: {ocr_text}", "INFO")
                 else:
                     self.log_message(f"OCR文本为空", "WARNING")
-            else:
-                self.log_message(f"订单 {order_no} OCR识别失败", "ERROR")
             
-            if image_notices:
-                self.log_message(f"订单 {order_no} 找到的图片notice: {', '.join(image_notices)}", "INFO")
-            else:
+            if not image_notices:
                 self.log_message(f"订单 {order_no} 未找到验机图片", "ERROR")
         else:
             self.log_message(f"订单 {order_no} 充电次数: {charge_count}", "SUCCESS")
@@ -598,7 +1020,6 @@ class MainWindow(QMainWindow):
             self.status_bar.showMessage(f"订单 {order_no} 未找到充电次数信息")
 
     def on_charge_error(self, index, error_msg):
-        """充电次数查询错误"""
         order_no = self.order_list[index].get('orderNo')
         self.log_message(f"订单 {order_no} 查询失败: {error_msg}", "ERROR")
         
@@ -611,7 +1032,6 @@ class MainWindow(QMainWindow):
         QMessageBox.warning(self, "查询失败", f"获取订单详情失败:\n{error_msg}")
 
     def update_charge_display(self, index, charge_count):
-        """更新充电次数显示"""
         widget = self.table.cellWidget(index, 6)
         if widget:
             label = widget.findChild(QLabel)
@@ -622,85 +1042,18 @@ class MainWindow(QMainWindow):
                 else:
                     label.setText("未找到")
                     label.setStyleSheet("color: #dc3545;")
-
-# --- 后台工作线程 ---
-class OrderListWorker(QThread):
-    finished = pyqtSignal(list)
-    error = pyqtSignal(str)
-
-    def __init__(self, order_manager):
-        super().__init__()
-        self.order_manager = order_manager
-
-    def run(self):
-        try:
-            order_list, error = self.order_manager.get_pending_order_list()
-            if error:
-                self.error.emit(error)
-            else:
-                self.finished.emit(order_list)
-        except Exception as e:
-            self.error.emit(str(e))
-
-class ChargeQueryWorker(QThread):
-    finished = pyqtSignal(int, object, object, list)  # index, charge_count, ocr_result, image_notices
-    error = pyqtSignal(int, str)
-
-    def __init__(self, order_manager, order_no, index):
-        super().__init__()
-        self.order_manager = order_manager
-        self.order_no = order_no
-        self.index = index
-
-    def run(self):
-        try:
-            # 获取订单详情
-            order_detail, error = self.order_manager.get_order_detail(self.order_no)
-            if error:
-                self.error.emit(self.index, error)
-                return
-            
-            # 查找所有图片的notice
-            image_notices = self.order_manager.get_all_image_notices(order_detail)
-            
-            # 查找验机图片
-            report_img = self.order_manager.find_original_image_by_notice(order_detail, "苹果沙漏验机报告")
-            if report_img is None:
-                report_img = self.order_manager.find_original_image_by_notice(order_detail, "苹果沙漏验机基本信息")
-            if report_img is None:
-                report_img = self.order_manager.find_original_image_by_notice(order_detail, "验机照片")
-            if report_img is None:
-                report_img = self.order_manager.find_original_image_by_notice(order_detail, "苹果验机软件")
-            
-            if not report_img:
-                # 没有找到图片,返回notice列表
-                self.finished.emit(self.index, None, None, image_notices)
-                return
-            
-            # OCR识别
-            ocr_result = self.order_manager.download_and_ocr_image(report_img)
-            if not ocr_result:
-                # OCR失败,返回notice列表
-                self.finished.emit(self.index, None, None, image_notices)
-                return
-            
-            # 提取充电次数
-            ocr_text = self.order_manager.extract_text_from_ocr_result(ocr_result)
-            charge_count = self.order_manager.extract_charge_count(ocr_text)
-            
-            self.finished.emit(self.index, charge_count, ocr_result, image_notices)
-            
-        except Exception as e:
-            self.error.emit(self.index, str(e))
+    
+    def closeEvent(self, event):
+        """关闭窗口时清理线程池"""
+        if hasattr(self, 'thread_pool'):
+            self.thread_pool.shutdown(wait=False)
+        event.accept()
 
 # --- 主程序入口 ---
 def main():
     app = QApplication(sys.argv)
     app.setStyle('Fusion')
     
-    # 设置应用图标
-    app.setWindowIcon(QIcon())
-    
     window = MainWindow()
     window.show()
     

+ 292 - 0
采矿记录.py

@@ -0,0 +1,292 @@
+import tkinter as tk
+from tkinter import ttk, scrolledtext
+import pyautogui
+import pyperclip
+import time
+import threading
+import csv
+import os
+import re
+from datetime import datetime
+
+class AutoClickerApp:
+    def __init__(self, root):
+        self.root = root
+        self.root.title("自动点击采集工具")
+        self.root.geometry("900x700")
+        
+        # 状态变量
+        self.is_running = False
+        self.thread = None
+        self.stop_event = threading.Event()
+        
+        # 数据存储
+        self.coord_data = []  # 存储CSV数据
+        self.last_coords = []  # 存储每次采集到的坐标 (用于去重)
+        
+        self.setup_ui()
+        self.load_csv_data()
+        
+    def setup_ui(self):
+        # 控制按钮框架
+        control_frame = tk.Frame(self.root)
+        control_frame.pack(pady=10)
+        
+        self.start_btn = tk.Button(control_frame, text="开始", command=self.start_action, 
+                                   bg="green", fg="white", font=("Arial", 12), width=10)
+        self.start_btn.pack(side=tk.LEFT, padx=5)
+        
+        self.stop_btn = tk.Button(control_frame, text="停止", command=self.stop_action, 
+                                  bg="red", fg="white", font=("Arial", 12), width=10)
+        self.stop_btn.pack(side=tk.LEFT, padx=5)
+        
+        # 表格框架
+        table_frame = tk.Frame(self.root)
+        table_frame.pack(pady=10, padx=10, fill=tk.BOTH, expand=True)
+        
+        # 创建表格
+        self.tree = ttk.Treeview(table_frame, columns=('x_range', 'y_range', 'collect_time'), show='headings', height=15)
+        
+        # 设置列
+        self.tree.heading('x_range', text='X坐标范围')
+        self.tree.heading('y_range', text='Y坐标范围')
+        self.tree.heading('collect_time', text='采集时间')
+        
+        self.tree.column('x_range', width=150, anchor='center')
+        self.tree.column('y_range', width=150, anchor='center')
+        self.tree.column('collect_time', width=250, anchor='center')
+        
+        # 添加滚动条
+        scrollbar = ttk.Scrollbar(table_frame, orient=tk.VERTICAL, command=self.tree.yview)
+        self.tree.configure(yscrollcommand=scrollbar.set)
+        
+        self.tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
+        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
+        
+        # 日志框架
+        log_frame = tk.Frame(self.root)
+        log_frame.pack(pady=10, padx=10, fill=tk.BOTH, expand=True)
+        
+        tk.Label(log_frame, text="日志:", font=("Arial", 10, "bold")).pack(anchor='w')
+        
+        self.log_text = scrolledtext.ScrolledText(log_frame, height=10, wrap=tk.WORD)
+        self.log_text.pack(fill=tk.BOTH, expand=True)
+        
+    def load_csv_data(self):
+        """加载CSV文件数据"""
+        csv_file = "coordinates.csv"
+        
+        if not os.path.exists(csv_file):
+            self.log_message(f"未找到 {csv_file} 文件,请创建示例文件")
+            self.create_sample_csv()
+            return
+            
+        try:
+            with open(csv_file, 'r', encoding='utf-8') as file:
+                csv_reader = csv.reader(file)
+                self.coord_data = list(csv_reader)
+                
+            # 清空表格
+            for item in self.tree.get_children():
+                self.tree.delete(item)
+                
+            # 填充表格
+            for row in self.coord_data:
+                if len(row) >= 3:
+                    self.tree.insert('', 'end', values=(row[0], row[1], row[2]))
+                    
+            self.log_message(f"成功加载 {len(self.coord_data)} 条坐标数据")
+            
+        except Exception as e:
+            self.log_message(f"加载CSV文件失败: {str(e)}")
+            
+    def create_sample_csv(self):
+        """创建示例CSV文件"""
+        sample_data = [
+            ['62-42', '225-73', ''],
+            ['240-300', '100-150', ''],
+            ['180-220', '50-90', '']
+        ]
+        
+        try:
+            with open('coordinates.csv', 'w', newline='', encoding='utf-8') as file:
+                writer = csv.writer(file)
+                writer.writerows(sample_data)
+            self.log_message("已创建示例CSV文件: coordinates.csv")
+            self.load_csv_data()
+        except Exception as e:
+            self.log_message(f"创建示例CSV文件失败: {str(e)}")
+            
+    def log_message(self, message):
+        """添加日志消息"""
+        timestamp = datetime.now().strftime("%H:%M:%S")
+        self.log_text.insert(tk.END, f"[{timestamp}] {message}\n")
+        self.log_text.see(tk.END)
+        
+    def update_table_time(self, x, y):
+        """根据坐标更新表格中的时间"""
+        try:
+            x_float = float(x)
+            y_float = float(y)
+            
+            # 从最后一行开始向前匹配(只匹配最新的)
+            for i in range(len(self.coord_data) - 1, -1, -1):
+                row = self.coord_data[i]
+                if len(row) >= 2:
+                    # 解析X范围
+                    x_range = row[0].strip()
+                    if '-' in x_range:
+                        x_start, x_end = x_range.split('-')
+                        try:
+                            x_start = float(x_start.strip())
+                            x_end = float(x_end.strip())
+                            
+                            if x_start <= x_float <= x_end:
+                                # 解析Y范围
+                                y_range = row[1].strip()
+                                if '-' in y_range:
+                                    y_start, y_end = y_range.split('-')
+                                    y_start = float(y_start.strip())
+                                    y_end = float(y_end.strip())
+                                    
+                                    if y_start <= y_float <= y_end:
+                                        # 匹配成功,更新时间
+                                        current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
+                                        row[2] = current_time
+                                        
+                                        # 更新表格显示
+                                        children = self.tree.get_children()
+                                        if i < len(children):
+                                            self.tree.delete(children[i])
+                                            self.tree.insert('', i, values=(row[0], row[1], row[2]))
+                                        
+                                        self.log_message(f"坐标 ({x_float}, {y_float}) 匹配成功,时间已更新为 {current_time}")
+                                        return True
+                        except ValueError:
+                            continue
+                            
+            self.log_message(f"坐标 ({x_float}, {y_float}) 未匹配到任何范围")
+            return False
+                
+        except ValueError:
+            self.log_message(f"坐标格式错误: {x}, {y}")
+            return False
+            
+    def parse_clipboard_content(self, content):
+        """解析剪贴板内容,提取坐标"""
+        lines = content.strip().split('\n')
+        found_coords = None
+        
+        # 查找"发现真武矿采集物坐标"的行
+        for i, line in enumerate(lines):
+            if '发现真武矿采集物坐标' in line:
+                # 检查前一行是否包含"挖着了"
+                if i > 0 and '挖着了' in lines[i-1]:
+                    # 提取坐标
+                    match = re.search(r'发现真武矿采集物坐标:([\d.]+),([\d.]+)', line)
+                    if match:
+                        x = match.group(1)
+                        y = match.group(2)
+                        found_coords = (x, y)
+                        self.log_message(f"发现有效坐标: {x}, {y}")
+                        break
+        
+        if found_coords:
+            x, y = found_coords
+            # 检查是否与上次采集的坐标相同(去重)
+            if (x, y) not in self.last_coords:
+                self.last_coords.append((x, y))
+                # 只保留最近10条记录
+                if len(self.last_coords) > 10:
+                    self.last_coords.pop(0)
+                self.update_table_time(x, y)
+            else:
+                self.log_message(f"坐标 ({x}, {y}) 已存在,跳过重复更新")
+        else:
+            self.log_message("未找到有效的采集坐标")
+            
+    def perform_actions(self):
+        """执行主要操作"""
+        self.log_message("开始执行操作序列...")
+        
+        try:
+            # 1. 右键点击指定位置
+            pyautogui.click(1730, 300, button='right')
+            self.log_message("已右键点击 (1730, 300)")
+            time.sleep(0.5)
+            
+            # 2. 按键 A
+            pyautogui.press('a')
+            self.log_message("已按键 A")
+            time.sleep(0.5)
+            
+            # 3. 按键 Ctrl+C
+            pyautogui.hotkey('ctrl', 'c')
+            self.log_message("已执行 Ctrl+C")
+            time.sleep(0.5)
+            
+            # 4. 获取剪贴板内容
+            clipboard_content = pyperclip.paste()
+            if clipboard_content:
+                self.log_message("成功获取剪贴板内容")
+                self.parse_clipboard_content(clipboard_content)
+            else:
+                self.log_message("剪贴板内容为空")
+                
+        except Exception as e:
+            self.log_message(f"操作执行失败: {str(e)}")
+            
+    def worker(self):
+        """工作线程函数"""
+        self.log_message("程序已启动,每10秒执行一次操作")
+        
+        while not self.stop_event.is_set():
+            if not self.is_running:
+                break
+                
+            self.perform_actions()
+            
+            # 等待10秒,但每秒检查一次停止信号
+            for _ in range(10):
+                if self.stop_event.is_set() or not self.is_running:
+                    break
+                time.sleep(1)
+                
+        self.log_message("操作线程已停止")
+        
+    def start_action(self):
+        """启动按钮回调"""
+        if self.is_running:
+            self.log_message("程序已在运行中")
+            return
+            
+        # 清空上次的坐标记录
+        self.last_coords = []
+        self.stop_event.clear()
+        self.is_running = True
+        
+        self.start_btn.config(state=tk.DISABLED)
+        self.stop_btn.config(state=tk.NORMAL)
+        
+        self.thread = threading.Thread(target=self.worker, daemon=True)
+        self.thread.start()
+        
+    def stop_action(self):
+        """停止按钮回调"""
+        if not self.is_running:
+            return
+            
+        self.log_message("正在停止程序...")
+        self.is_running = False
+        self.stop_event.set()
+        
+        self.start_btn.config(state=tk.NORMAL)
+        self.stop_btn.config(state=tk.DISABLED)
+
+def main():
+    root = tk.Tk()
+    app = AutoClickerApp(root)
+    root.mainloop()
+
+if __name__ == "__main__":
+    main()