Просмотр исходного кода

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

PUGE 3 недель назад
Родитель
Сommit
169575bad2
5 измененных файлов с 1808 добавлено и 197 удалено
  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"
     "referer": "https://cppaidan.com/paidan/system/user"
 }
 }
 
 
-password = 10303
+password = 67745
 while password < 1000000:
 while password < 1000000:
     try:
     try:
         response = requests.post(
         response = requests.post(

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

@@ -627,10 +627,40 @@ class VmControlGUI:
                 self.press_key(0xBB, False)
                 self.press_key(0xBB, False)
                 time.sleep(wait_time)
                 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)
                 time.sleep(wait_time)
                 
                 
                 # 按 ESC
                 # 按 ESC
@@ -751,10 +781,40 @@ class VmControlGUI:
                 self.press_key(0xBB, False)
                 self.press_key(0xBB, False)
                 time.sleep(wait_time)
                 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)
                 time.sleep(wait_time)
                 
                 
                 # 按 ESC
                 # 按 ESC

+ 541 - 188
解密机大侠.py

@@ -5,12 +5,17 @@ import os
 import re
 import re
 import uuid
 import uuid
 import hashlib
 import hashlib
+import time
+import threading
+from concurrent.futures import ThreadPoolExecutor, as_completed
 from Crypto.Cipher import AES
 from Crypto.Cipher import AES
 from Crypto.Util.Padding import unpad
 from Crypto.Util.Padding import unpad
 from typing import Optional, List, Dict, Any
 from typing import Optional, List, Dict, Any
 from PyQt5.QtWidgets import *
 from PyQt5.QtWidgets import *
 from PyQt5.QtCore import *
 from PyQt5.QtCore import *
 from PyQt5.QtGui import *
 from PyQt5.QtGui import *
+from requests.adapters import HTTPAdapter
+from urllib3.util.retry import Retry
 
 
 # --- 核心功能类 ---
 # --- 核心功能类 ---
 class OrderManager:
 class OrderManager:
@@ -37,6 +42,26 @@ class OrderManager:
         }
         }
         self.key = b"S9u978T13NLCGc5W"
         self.key = b"S9u978T13NLCGc5W"
         self.iv = b"X83yWMD9iKhLxfwX"
         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):
     def decrypt_order_data(self, hex_string):
         """解密订单数据"""
         """解密订单数据"""
@@ -62,7 +87,7 @@ class OrderManager:
         }
         }
         
         
         try:
         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()
             response.raise_for_status()
             result = response.json()
             result = response.json()
             
             
@@ -74,13 +99,18 @@ class OrderManager:
             return None, str(e)
             return None, str(e)
 
 
     def get_order_detail(self, order_no):
     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
         # 获取BiddingNo
         snatch_url = "https://wirelessgate.aihuishou.com/jdx-rc-service/front/bid/snatch"
         snatch_url = "https://wirelessgate.aihuishou.com/jdx-rc-service/front/bid/snatch"
         snatch_payload = {"orderNo": order_no}
         snatch_payload = {"orderNo": order_no}
         
         
         try:
         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_response.raise_for_status()
             snatch_data = snatch_response.json()
             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}"
         detail_url = f"https://wirelessgate.aihuishou.com/jdx-qa-service/app/ka/v3/order/detail?orderNo={order_no}&biddingNo={bidding_no}"
         
         
         try:
         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_response.raise_for_status()
             detail_data = detail_response.json()
             detail_data = detail_response.json()
             
             
@@ -112,90 +142,82 @@ class OrderManager:
             decrypted_json_string = self.decrypt_order_data(encrypted_hex)
             decrypted_json_string = self.decrypt_order_data(encrypted_hex)
             order_detail = json.loads(decrypted_json_string)
             order_detail = json.loads(decrypted_json_string)
             
             
+            # 存入缓存
+            with self.cache_lock:
+                self.order_detail_cache[order_no] = order_detail
+            
             return order_detail, None
             return order_detail, None
             
             
         except Exception as e:
         except Exception as e:
             return None, str(e)
             return None, str(e)
 
 
     def find_original_image_by_notice(self, json_data, notice_keyword):
     def find_original_image_by_notice(self, json_data, notice_keyword):
-        """查找图片链接"""
+        """优化的图片查找(使用栈迭代)"""
         if isinstance(json_data, str):
         if isinstance(json_data, str):
             try:
             try:
                 json_data = json.loads(json_data)
                 json_data = json.loads(json_data)
             except json.JSONDecodeError:
             except json.JSONDecodeError:
                 return None
                 return None
-
-        def recursive_search(obj):
+        
+        # 使用栈迭代而非递归
+        stack = [json_data]
+        keyword_lower = notice_keyword.lower()
+        
+        while stack:
+            obj = stack.pop()
+            
             if isinstance(obj, dict):
             if isinstance(obj, dict):
+                # 检查当前字典
                 if 'notice' in obj and 'originalImage' in obj:
                 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']
                         return obj['originalImage']
                 
                 
+                # 将所有值加入栈
                 for value in obj.values():
                 for value in obj.values():
-                    result = recursive_search(value)
-                    if result:
-                        return result
-            
+                    stack.append(value)
+                    
             elif isinstance(obj, list):
             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):
     def get_all_image_notices(self, json_data):
         """获取所有图片的notice信息"""
         """获取所有图片的notice信息"""
         notices = []
         notices = []
+        stack = [json_data]
         
         
-        def recursive_search(obj):
+        while stack:
+            obj = stack.pop()
+            
             if isinstance(obj, dict):
             if isinstance(obj, dict):
                 if 'notice' in obj and 'image' in obj:
                 if 'notice' in obj and 'image' in obj:
                     notices.append(obj['notice'])
                     notices.append(obj['notice'])
                 for value in obj.values():
                 for value in obj.values():
-                    recursive_search(value)
+                    stack.append(value)
             elif isinstance(obj, list):
             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:
         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'):
                 if ocr_result.get('success'):
+                    # 存入缓存
+                    with self.cache_lock:
+                        self.ocr_cache[cache_key] = ocr_result
                     return ocr_result
                     return ocr_result
             return None
             return None
             
             
@@ -217,24 +239,128 @@ class OrderManager:
 
 
     def extract_charge_count(self, text):
     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:
         if match:
             return int(match.group(1))
             return int(match.group(1))
+        
         return None
         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):
 class MainWindow(QMainWindow):
     def __init__(self):
     def __init__(self):
         super().__init__()
         super().__init__()
         self.order_manager = None
         self.order_manager = None
         self.order_list = []
         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()
         self.init_ui()
 
 
     def init_ui(self):
     def init_ui(self):
-        self.setWindowTitle('爱回收订单查询工具')
+        self.setWindowTitle('爱回收订单查询工具 (优化版)')
         self.setGeometry(100, 100, 1200, 800)
         self.setGeometry(100, 100, 1200, 800)
         
         
         # 设置样式
         # 设置样式
@@ -268,6 +394,15 @@ class MainWindow(QMainWindow):
             QPushButton:disabled {
             QPushButton:disabled {
                 background-color: #cccccc;
                 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 {
             QLineEdit, QTextEdit {
                 border: 1px solid #ccc;
                 border: 1px solid #ccc;
                 border-radius: 4px;
                 border-radius: 4px;
@@ -297,9 +432,17 @@ class MainWindow(QMainWindow):
                 font-family: 'Consolas', 'Courier New', monospace;
                 font-family: 'Consolas', 'Courier New', monospace;
                 font-size: 11px;
                 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()
         central_widget = QWidget()
         self.setCentralWidget(central_widget)
         self.setCentralWidget(central_widget)
         main_layout = QVBoxLayout(central_widget)
         main_layout = QVBoxLayout(central_widget)
@@ -309,26 +452,29 @@ class MainWindow(QMainWindow):
         config_group = QGroupBox("配置")
         config_group = QGroupBox("配置")
         config_layout = QGridLayout()
         config_layout = QGridLayout()
         
         
-        # Token输入
         config_layout.addWidget(QLabel("Access Token:"), 0, 0)
         config_layout.addWidget(QLabel("Access Token:"), 0, 0)
         self.token_input = QLineEdit()
         self.token_input = QLineEdit()
         self.token_input.setText("jdx9ce52336abee4cf78db56567dc259512")
         self.token_input.setText("jdx9ce52336abee4cf78db56567dc259512")
         self.token_input.setEchoMode(QLineEdit.Password)
         self.token_input.setEchoMode(QLineEdit.Password)
         config_layout.addWidget(self.token_input, 0, 1)
         config_layout.addWidget(self.token_input, 0, 1)
         
         
-        # 显示/隐藏Token按钮
         self.show_token_btn = QPushButton("显示")
         self.show_token_btn = QPushButton("显示")
         self.show_token_btn.setFixedWidth(60)
         self.show_token_btn.setFixedWidth(60)
         self.show_token_btn.clicked.connect(self.toggle_token_visibility)
         self.show_token_btn.clicked.connect(self.toggle_token_visibility)
         config_layout.addWidget(self.show_token_btn, 0, 2)
         config_layout.addWidget(self.show_token_btn, 0, 2)
         
         
-        # OCR地址输入
         config_layout.addWidget(QLabel("OCR地址:"), 1, 0)
         config_layout.addWidget(QLabel("OCR地址:"), 1, 0)
         self.ocr_input = QLineEdit()
         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(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()
         btn_layout = QHBoxLayout()
         self.search_btn = QPushButton("🔍 搜索订单")
         self.search_btn = QPushButton("🔍 搜索订单")
         self.search_btn.clicked.connect(self.search_orders)
         self.search_btn.clicked.connect(self.search_orders)
@@ -340,8 +486,15 @@ class MainWindow(QMainWindow):
         self.refresh_btn.setFixedHeight(35)
         self.refresh_btn.setFixedHeight(35)
         btn_layout.addWidget(self.refresh_btn)
         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()
         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)
         config_group.setLayout(config_layout)
         main_layout.addWidget(config_group)
         main_layout.addWidget(config_group)
@@ -350,7 +503,10 @@ class MainWindow(QMainWindow):
         list_group = QGroupBox("订单列表")
         list_group = QGroupBox("订单列表")
         list_layout = QVBoxLayout()
         list_layout = QVBoxLayout()
         
         
-        # 表格
+        self.progress_bar = QProgressBar()
+        self.progress_bar.setVisible(False)
+        list_layout.addWidget(self.progress_bar)
+        
         self.table = QTableWidget()
         self.table = QTableWidget()
         self.table.setColumnCount(7)
         self.table.setColumnCount(7)
         self.table.setHorizontalHeaderLabels(["序号", "订单号", "商品信息", "IMEI", "预估价格", "门店", "充电次数"])
         self.table.setHorizontalHeaderLabels(["序号", "订单号", "商品信息", "IMEI", "预估价格", "门店", "充电次数"])
@@ -372,14 +528,12 @@ class MainWindow(QMainWindow):
         log_group = QGroupBox("日志")
         log_group = QGroupBox("日志")
         log_layout = QVBoxLayout()
         log_layout = QVBoxLayout()
         
         
-        # 日志文本框
         self.log_text = QTextEdit()
         self.log_text = QTextEdit()
         self.log_text.setObjectName("log_text")
         self.log_text.setObjectName("log_text")
         self.log_text.setReadOnly(True)
         self.log_text.setReadOnly(True)
         self.log_text.setMaximumHeight(200)
         self.log_text.setMaximumHeight(200)
         log_layout.addWidget(self.log_text)
         log_layout.addWidget(self.log_text)
         
         
-        # 日志控制按钮
         log_btn_layout = QHBoxLayout()
         log_btn_layout = QHBoxLayout()
         self.clear_log_btn = QPushButton("清空日志")
         self.clear_log_btn = QPushButton("清空日志")
         self.clear_log_btn.clicked.connect(self.clear_log)
         self.clear_log_btn.clicked.connect(self.clear_log)
@@ -391,13 +545,11 @@ class MainWindow(QMainWindow):
         log_group.setLayout(log_layout)
         log_group.setLayout(log_layout)
         main_layout.addWidget(log_group)
         main_layout.addWidget(log_group)
 
 
-        # --- 底部状态栏 ---
         self.status_bar = QStatusBar()
         self.status_bar = QStatusBar()
         self.setStatusBar(self.status_bar)
         self.setStatusBar(self.status_bar)
         self.status_bar.showMessage("就绪")
         self.status_bar.showMessage("就绪")
 
 
     def toggle_token_visibility(self):
     def toggle_token_visibility(self):
-        """切换Token显示/隐藏"""
         if self.token_input.echoMode() == QLineEdit.Password:
         if self.token_input.echoMode() == QLineEdit.Password:
             self.token_input.setEchoMode(QLineEdit.Normal)
             self.token_input.setEchoMode(QLineEdit.Normal)
             self.show_token_btn.setText("隐藏")
             self.show_token_btn.setText("隐藏")
@@ -406,27 +558,24 @@ class MainWindow(QMainWindow):
             self.show_token_btn.setText("显示")
             self.show_token_btn.setText("显示")
 
 
     def log_message(self, message, level="INFO"):
     def log_message(self, message, level="INFO"):
-        """添加日志消息"""
         timestamp = QDateTime.currentDateTime().toString("yyyy-MM-dd hh:mm:ss")
         timestamp = QDateTime.currentDateTime().toString("yyyy-MM-dd hh:mm:ss")
         color_map = {
         color_map = {
             "INFO": "#d4d4d4",
             "INFO": "#d4d4d4",
             "WARNING": "#ffa500",
             "WARNING": "#ffa500",
             "ERROR": "#ff4444",
             "ERROR": "#ff4444",
-            "SUCCESS": "#4caf50"
+            "SUCCESS": "#4caf50",
+            "PROGRESS": "#00bcd4"
         }
         }
         color = color_map.get(level, "#d4d4d4")
         color = color_map.get(level, "#d4d4d4")
         html = f'<span style="color: #888888;">[{timestamp}]</span> <span style="color: {color};">[{level}]</span> {message}<br>'
         html = f'<span style="color: #888888;">[{timestamp}]</span> <span style="color: {color};">[{level}]</span> {message}<br>'
         self.log_text.append(html)
         self.log_text.append(html)
-        # 滚动到底部
         scrollbar = self.log_text.verticalScrollBar()
         scrollbar = self.log_text.verticalScrollBar()
         scrollbar.setValue(scrollbar.maximum())
         scrollbar.setValue(scrollbar.maximum())
 
 
     def clear_log(self):
     def clear_log(self):
-        """清空日志"""
         self.log_text.clear()
         self.log_text.clear()
 
 
     def search_orders(self):
     def search_orders(self):
-        """搜索订单"""
         access_token = self.token_input.text().strip()
         access_token = self.token_input.text().strip()
         ocr_url = self.ocr_input.text().strip()
         ocr_url = self.ocr_input.text().strip()
         
         
@@ -440,74 +589,62 @@ class MainWindow(QMainWindow):
         
         
         self.log_message("开始搜索订单...", "INFO")
         self.log_message("开始搜索订单...", "INFO")
         
         
-        # 初始化订单管理器
         self.order_manager = OrderManager(access_token, ocr_url)
         self.order_manager = OrderManager(access_token, ocr_url)
-        
-        # 清空缓存
         self.order_details_cache = {}
         self.order_details_cache = {}
         self.charge_counts = {}
         self.charge_counts = {}
+        self.batch_btn.setEnabled(False)
         
         
         self.status_bar.showMessage("正在获取订单列表...")
         self.status_bar.showMessage("正在获取订单列表...")
         self.search_btn.setEnabled(False)
         self.search_btn.setEnabled(False)
         
         
-        # 在后台线程执行
         self.worker = OrderListWorker(self.order_manager)
         self.worker = OrderListWorker(self.order_manager)
         self.worker.finished.connect(self.on_orders_loaded)
         self.worker.finished.connect(self.on_orders_loaded)
         self.worker.error.connect(self.on_error)
         self.worker.error.connect(self.on_error)
         self.worker.start()
         self.worker.start()
 
 
     def refresh_orders(self):
     def refresh_orders(self):
-        """刷新订单列表"""
         if self.order_manager:
         if self.order_manager:
             self.search_orders()
             self.search_orders()
         else:
         else:
             QMessageBox.warning(self, "提示", "请先搜索订单")
             QMessageBox.warning(self, "提示", "请先搜索订单")
 
 
     def on_orders_loaded(self, order_list):
     def on_orders_loaded(self, order_list):
-        """订单列表加载完成"""
         self.order_list = order_list
         self.order_list = order_list
         self.display_orders()
         self.display_orders()
         self.search_btn.setEnabled(True)
         self.search_btn.setEnabled(True)
+        self.batch_btn.setEnabled(True)
         self.status_bar.showMessage(f"成功加载 {len(order_list)} 条订单")
         self.status_bar.showMessage(f"成功加载 {len(order_list)} 条订单")
         self.log_message(f"成功加载 {len(order_list)} 条订单", "SUCCESS")
         self.log_message(f"成功加载 {len(order_list)} 条订单", "SUCCESS")
 
 
     def on_error(self, error_msg):
     def on_error(self, error_msg):
-        """错误处理"""
         self.search_btn.setEnabled(True)
         self.search_btn.setEnabled(True)
+        self.batch_btn.setEnabled(False)
         self.status_bar.showMessage("加载失败")
         self.status_bar.showMessage("加载失败")
         self.log_message(f"获取订单列表失败: {error_msg}", "ERROR")
         self.log_message(f"获取订单列表失败: {error_msg}", "ERROR")
         QMessageBox.critical(self, "错误", f"获取订单列表失败:\n{error_msg}")
         QMessageBox.critical(self, "错误", f"获取订单列表失败:\n{error_msg}")
 
 
     def display_orders(self):
     def display_orders(self):
-        """显示订单列表"""
         self.table.setRowCount(len(self.order_list))
         self.table.setRowCount(len(self.order_list))
         
         
         for i, order in enumerate(self.order_list):
         for i, order in enumerate(self.order_list):
-            # 序号
             self.table.setItem(i, 0, QTableWidgetItem(str(i + 1)))
             self.table.setItem(i, 0, QTableWidgetItem(str(i + 1)))
             
             
-            # 订单号
             order_no = order.get('orderNo', '')
             order_no = order.get('orderNo', '')
             self.table.setItem(i, 1, QTableWidgetItem(order_no))
             self.table.setItem(i, 1, QTableWidgetItem(order_no))
             
             
-            # 商品信息
             brand_info = order.get('brandInfo', '')
             brand_info = order.get('brandInfo', '')
             self.table.setItem(i, 2, QTableWidgetItem(brand_info))
             self.table.setItem(i, 2, QTableWidgetItem(brand_info))
             
             
-            # IMEI
             imei = order.get('imei', '')
             imei = order.get('imei', '')
             self.table.setItem(i, 3, QTableWidgetItem(imei))
             self.table.setItem(i, 3, QTableWidgetItem(imei))
             
             
-            # 预估价格
             price = order.get('price', '')
             price = order.get('price', '')
             price_str = str(price) if price else '待定'
             price_str = str(price) if price else '待定'
             self.table.setItem(i, 4, QTableWidgetItem(price_str))
             self.table.setItem(i, 4, QTableWidgetItem(price_str))
             
             
-            # 门店
             title = order.get('title', '')
             title = order.get('title', '')
             self.table.setItem(i, 5, QTableWidgetItem(title))
             self.table.setItem(i, 5, QTableWidgetItem(title))
             
             
-            # 充电次数(初始为空,显示"查询"按钮)
             charge_widget = QWidget()
             charge_widget = QWidget()
             charge_layout = QHBoxLayout(charge_widget)
             charge_layout = QHBoxLayout(charge_widget)
             charge_layout.setContentsMargins(5, 2, 5, 2)
             charge_layout.setContentsMargins(5, 2, 5, 2)
@@ -527,7 +664,6 @@ class MainWindow(QMainWindow):
             self.table.setCellWidget(i, 6, charge_widget)
             self.table.setCellWidget(i, 6, charge_widget)
 
 
     def check_charge_count(self, index):
     def check_charge_count(self, index):
-        """查询充电次数"""
         if not self.order_manager:
         if not self.order_manager:
             return
             return
         
         
@@ -539,47 +675,333 @@ class MainWindow(QMainWindow):
             return
             return
         
         
         self.log_message(f"开始查询订单 {order_no} 的充电次数", "INFO")
         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} 的充电次数...")
         self.status_bar.showMessage(f"正在查询订单 {order_no} 的充电次数...")
         
         
-        # 禁用按钮
         btn = self.findChild(QPushButton, f"check_btn_{index}")
         btn = self.findChild(QPushButton, f"check_btn_{index}")
         if btn:
         if btn:
             btn.setEnabled(False)
             btn.setEnabled(False)
             btn.setText("查询中")
             btn.setText("查询中")
         
         
-        # 在后台线程执行
         self.charge_worker = ChargeQueryWorker(self.order_manager, order_no, index)
         self.charge_worker = ChargeQueryWorker(self.order_manager, order_no, index)
         self.charge_worker.finished.connect(self.on_charge_queried)
         self.charge_worker.finished.connect(self.on_charge_queried)
         self.charge_worker.error.connect(self.on_charge_error)
         self.charge_worker.error.connect(self.on_charge_error)
         self.charge_worker.start()
         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):
     def on_charge_queried(self, index, charge_count, ocr_result, image_notices):
-        """充电次数查询完成"""
         order_no = self.order_list[index].get('orderNo')
         order_no = self.order_list[index].get('orderNo')
         
         
-        # 如果没有找到充电次数,输出详细信息
         if charge_count is None:
         if charge_count is None:
             if ocr_result:
             if ocr_result:
                 self.log_message(f"订单 {order_no} 未找到充电次数,OCR识别结果:", "WARNING")
                 self.log_message(f"订单 {order_no} 未找到充电次数,OCR识别结果:", "WARNING")
                 ocr_text = self.order_manager.extract_text_from_ocr_result(ocr_result)
                 ocr_text = self.order_manager.extract_text_from_ocr_result(ocr_result)
                 if ocr_text:
                 if ocr_text:
-                    self.log_message(f"OCR文本: {ocr_text[:200]}...", "INFO")
+                    self.log_message(f"OCR文本: {ocr_text}", "INFO")
                 else:
                 else:
                     self.log_message(f"OCR文本为空", "WARNING")
                     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")
                 self.log_message(f"订单 {order_no} 未找到验机图片", "ERROR")
         else:
         else:
             self.log_message(f"订单 {order_no} 充电次数: {charge_count}", "SUCCESS")
             self.log_message(f"订单 {order_no} 充电次数: {charge_count}", "SUCCESS")
@@ -598,7 +1020,6 @@ class MainWindow(QMainWindow):
             self.status_bar.showMessage(f"订单 {order_no} 未找到充电次数信息")
             self.status_bar.showMessage(f"订单 {order_no} 未找到充电次数信息")
 
 
     def on_charge_error(self, index, error_msg):
     def on_charge_error(self, index, error_msg):
-        """充电次数查询错误"""
         order_no = self.order_list[index].get('orderNo')
         order_no = self.order_list[index].get('orderNo')
         self.log_message(f"订单 {order_no} 查询失败: {error_msg}", "ERROR")
         self.log_message(f"订单 {order_no} 查询失败: {error_msg}", "ERROR")
         
         
@@ -611,7 +1032,6 @@ class MainWindow(QMainWindow):
         QMessageBox.warning(self, "查询失败", f"获取订单详情失败:\n{error_msg}")
         QMessageBox.warning(self, "查询失败", f"获取订单详情失败:\n{error_msg}")
 
 
     def update_charge_display(self, index, charge_count):
     def update_charge_display(self, index, charge_count):
-        """更新充电次数显示"""
         widget = self.table.cellWidget(index, 6)
         widget = self.table.cellWidget(index, 6)
         if widget:
         if widget:
             label = widget.findChild(QLabel)
             label = widget.findChild(QLabel)
@@ -622,85 +1042,18 @@ class MainWindow(QMainWindow):
                 else:
                 else:
                     label.setText("未找到")
                     label.setText("未找到")
                     label.setStyleSheet("color: #dc3545;")
                     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():
 def main():
     app = QApplication(sys.argv)
     app = QApplication(sys.argv)
     app.setStyle('Fusion')
     app.setStyle('Fusion')
     
     
-    # 设置应用图标
-    app.setWindowIcon(QIcon())
-    
     window = MainWindow()
     window = MainWindow()
     window.show()
     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()