PUGE před 1 týdnem
rodič
revize
dcbe58641e
8 změnil soubory, kde provedl 1148 přidání a 250 odebrání
  1. binární
      bumen_result.bin
  2. 1 0
      parsed_select_1.txt
  3. 1 0
      query_results.txt
  4. 250 24
      test.py
  5. 169 60
      定时按某键.py
  6. 87 0
      抢单.py
  7. 245 60
      按照数据搜索整理文件.py
  8. 395 106
      自动扫码.py

binární
bumen_result.bin


+ 1 - 0
parsed_select_1.txt

@@ -0,0 +1 @@
+Access violation at address 0040884E in module 'scktsrvr.exe'. Read of address 030064FC

+ 1 - 0
query_results.txt

@@ -0,0 +1 @@
+Access violation at address 00620F37 in module 'scktsrvr.exe'. Read of address 00000000

+ 250 - 24
test.py

@@ -1,34 +1,260 @@
-# task_launcher.py - 通过计划任务在用户会话中启动
-import subprocess
+import socket
 import time
-import os
 
-def launch_monitor_in_session1():
-    """通过计划任务在用户桌面会话中启动程序"""
-    exe_path = r"C:\monitorBrowser10S.exe"
-    task_name = "Temp_Monitor_Launcher"
+def replay_full_session():
+    """完整回放新抓包中的所有通信(保持顺序和时间间隔)"""
+    HOST = "47.108.151.236"
+    PORT = 211
     
-    # 创建计划任务(关键参数:/IT 允许交互,/SC ONLOGON 在登录时)
-    create_cmd = f'schtasks /create /tn "{task_name}" /tr "{exe_path}" /sc ONLOGON /it /f /rl HIGHEST'
+    # 从新抓包中提取的所有客户端发送的包(按时间顺序)
+    packets = [
+        # 14:23:27.392 - 握手
+        bytes.fromhex(
+            "04 da 00 00 54 00 00 00 08 00 00 00 26 00 00 00"
+            "7b 00 43 00 34 00 35 00 46 00 44 00 33 00 33 00"
+            "43 00 2d 00 37 00 39 00 30 00 46 00 2d 00 34 00"
+            "34 00 45 00 41 00 2d 00 42 00 33 00 41 00 44 00"
+            "2d 00 34 00 30 00 30 00 36 00 41 00 44 00 35 00"
+            "31 00 36 00 38 00 37 00 37 00 7d 00"
+        ),
+        # 14:23:27.641 - id02
+        bytes.fromhex(
+            "03 da 00 00 18 00 00 00 03 00 00 00 00 00 00 00"
+            "08 00 00 00 04 00 00 00 69 00 64 00 30 00 32 00"
+        ),
+        # 14:23:27.760 - 复杂联查
+        bytes.fromhex(
+            "02 da 00 00 72 03 00 00 03 00 00 00 00 00 00 00"
+            "03 00 00 00 2e 01 00 00 03 00 00 00 01 00 00 00"
+            "0b 00 00 00 ff ff 03 00 00 00 02 00 00 00 03 00"
+            "00 00 00 00 00 08 40 00 00 9a 01 00 00 73 00 65"
+            "00 6c 00 65 00 63 00 74 00 20 00 64 00 2e 00 6d"
+            "00 64 00 6d 00 63 00 2c 00 62 00 2e 00 67 00 6b"
+            "00 78 00 6d 00 2c 00 62 00 2e 00 6b 00 61 00 68"
+            "00 61 00 6f 00 2c 00 63 00 2e 00 6e 00 61 00 6d"
+            "00 65 00 2c 00 63 00 2e 00 6b 00 61 00 68 00 61"
+            "00 6f 00 20 00 61 00 73 00 20 00 79 00 67 00 6b"
+            "00 68 00 2c 00 61 00 2e 00 63 00 63 00 79 00 69"
+            "00 64 00 2c 00 61 00 2e 00 64 00 6a 00 69 00 64"
+            "00 2c 00 61 00 2e 00 73 00 68 00 72 00 2c 00 61"
+            "00 2e 00 62 00 65 00 69 00 7a 00 68 00 75 00 2c"
+            "00 61 00 2e 00 64 00 6a 00 6c 00 78 00 2c 00 61"
+            "00 2e 00 72 00 69 00 71 00 69 00 2c 00 61 00 2e"
+            "00 7a 00 73 00 6c 00 2c 00 61 00 2e 00 7a 00 6a"
+            "00 65 00 2c 00 61 00 2e 00 77 00 66 00 6b 00 2c"
+            "00 61 00 2e 00 7a 00 6b 00 6a 00 65 00 2c 00 61"
+            "00 2e 00 73 00 72 00 2c 00 61 00 2e 00 63 00 7a"
+            "00 6b 00 6a 00 73 00 2c 00 61 00 2e 00 64 00 78"
+            "00 69 00 64 00 2c 00 61 00 2e 00 64 00 6a 00 6c"
+            "00 78 00 2c 00 61 00 2e 00 6b 00 6d 00 6d 00 63"
+            "00 20 00 66 00 72 00 6f 00 6d 00 20 00 79 00 77"
+            "00 20 00 61 00 20 00 6a 00 6f 00 69 00 6e 00 20"
+            "00 67 00 75 00 6b 00 65 00 20 00 62 00 20 00 6f"
+            "00 6e 00 20 00 61 00 2e 00 64 00 78 00 69 00 64"
+            "00 3d 00 62 00 2e 00 6b 00 61 00 68 00 61 00 6f"
+            "00 20 00 6a 00 6f 00 69 00 6e 00 20 00 79 00 75"
+            "00 61 00 6e 00 67 00 6f 00 6e 00 67 00 20 00 63"
+            "00 20 00 6f 00 6e 00 20 00 61 00 2e 00 79 00 77"
+            "00 79 00 69 00 64 00 3d 00 63 00 2e 00 6b 00 61"
+            "00 68 00 61 00 6f 00 20 00 6a 00 6f 00 69 00 6e"
+            "00 20 00 62 00 75 00 6d 00 65 00 6e 00 20 00 64"
+            "00 20 00 6f 00 6e 00 20 00 72 00 69 00 67 00 68"
+            "00 74 00 28 00 61 00 2e 00 64 00 6a 00 69 00 64"
+            "00 2c 00 31 00 29 00 3d 00 64 00 2e 00 62 00 75"
+            "00 6d 00 65 00 6e 00 69 00 64 00 20 00 77 00 68"
+            "00 65 00 72 00 65 00 20 00 73 00 75 00 62 00 73"
+            "00 74 00 72 00 69 00 6e 00 67 00 28 00 61 00 2e"
+            "00 64 00 6a 00 69 00 64 00 2c 00 31 00 30 00 2c"
+            "00 31 00 29 00 3d 00 27 00 4c 00 27 00 20 00 61"
+            "00 6e 00 64 00 20 00 61 00 2e 00 72 00 69 00 71"
+            "00 69 00 20 00 62 00 65 00 74 00 77 00 65 00 65"
+            "00 6e 00 20 00 27 00 32 00 30 00 32 00 36 00 2d"
+            "00 30 00 36 00 2d 00 30 00 38 00 27 00 20 00 61"
+            "00 6e 00 64 00 20 00 27 00 32 00 30 00 32 00 36"
+            "00 2d 00 30 00 36 00 2d 00 30 00 38 00 27 00 20"
+            "00 61 00 6e 00 64 00 20 00 72 00 69 00 67 00 68"
+            "00 74 00 28 00 61 00 2e 00 64 00 6a 00 69 00 64"
+            "00 2c 00 31 00 29 00 3d 00 27 00 41 00 27 00 20"
+            "00 6f 00 72 00 64 00 65 00 72 00 20 00 62 00 79"
+            "00 20 00 61 00 2e 00 72 00 69 00 71 00 69 00 20"
+            "00 64 00 65 00 73 00 63 00 2c 00 61 00 2e 00 64"
+            "00 6a 00 69 00 64 00 20 00 64 00 65 00 73 00 63"
+            "00 03 00 00 00 63 00 00 00"
+        ),
+        # 14:23:29.643 - yuangang 查询
+        bytes.fromhex(
+            "02 da 00 00 b2 00 00 00 03 00 00 00 00 00 00 00"
+            "03 00 00 00 2e 01 00 00 03 00 00 00 01 00 00 00"
+            "0b 00 00 00 ff ff 03 00 00 00 02 00 00 00 03 00"
+            "00 00 00 00 00 08 00 00 00 3a 00 00 00 73 00 65"
+            "00 6c 00 65 00 63 00 74 00 20 00 64 00 69 00 73"
+            "00 74 00 69 00 6e 00 63 00 74 00 20 00 6e 00 61"
+            "00 6d 00 65 00 20 00 66 00 72 00 6f 00 6d 00 20"
+            "00 79 00 75 00 61 00 6e 00 67 00 6f 00 6e 00 67"
+            "00 20 00 77 00 68 00 65 00 72 00 65 00 20 00 79"
+            "00 78 00 62 00 6a 00 3d 00 30 00 20 00 6f 00 72"
+            "00 64 00 65 00 72 00 20 00 62 00 79 00 20 00 31"
+            "00 03 00 00 00 63 00 00 00"
+        ),
+        # 14:23:29.871 - bumen 查询
+        bytes.fromhex(
+            "02 da 00 00 80 00 00 00 03 00 00 00 00 00 00 00"
+            "03 00 00 00 2e 01 00 00 03 00 00 00 01 00 00 00"
+            "0b 00 00 00 ff ff 03 00 00 00 02 00 00 00 03 00"
+            "00 00 00 00 00 08 40 00 00 21 00 00 00 73 00 65"
+            "00 6c 00 65 00 63 00 74 00 20 00 6d 00 64 00 6d"
+            "00 63 00 20 00 66 00 72 00 6f 00 6d 00 20 00 62"
+            "00 75 00 6d 00 65 00 6e 00 20 00 6f 00 72 00 64"
+            "00 65 00 72 00 20 00 62 00 79 00 20 00 31 00 03"
+            "00 00 00 63 00 00 00"
+        ),
+        # 14:23:30.112 - pinpai 查询
+        bytes.fromhex(
+            "02 da 00 00 bc 00 00 00 03 00 00 00 00 00 00 00"
+            "03 00 00 00 2e 01 00 00 03 00 00 00 01 00 00 00"
+            "0b 00 00 00 ff ff 03 00 00 00 02 00 00 00 03 00"
+            "00 00 00 00 00 08 40 00 00 3f 00 00 00 73 00 65"
+            "00 6c 00 65 00 63 00 74 00 20 00 64 00 69 00 73"
+            "00 74 00 69 00 6e 00 63 00 74 00 20 00 70 00 69"
+            "00 6e 00 70 00 61 00 69 00 20 00 66 00 72 00 6f"
+            "00 6d 00 20 00 71 00 69 00 61 00 6e 00 7a 00 68"
+            "00 75 00 69 00 5f 00 6a 00 70 00 20 00 77 00 68"
+            "00 65 00 72 00 65 00 20 00 79 00 78 00 62 00 6a"
+            "00 3d 00 30 00 20 00 6f 00 72 00 64 00 65 00 72"
+            "00 20 00 62 00 79 00 20 00 31 00 03 00 00 00 63"
+            "00 00 00"
+        ),
+    ]
     
-    result = subprocess.run(create_cmd, shell=True, capture_output=True, text=True)
-    if result.returncode != 0:
-        print(f"创建任务失败: {result.stderr}")
-        return False
+    # 抓包中的时间间隔(秒)
+    intervals = [
+        0,          # 第一个包
+        0.249,      # 27.392 -> 27.641
+        0.119,      # 27.641 -> 27.760
+        1.883,      # 27.760 -> 29.643
+        0.228,      # 29.643 -> 29.871
+        0.241,      # 29.871 -> 30.112
+    ]
     
-    # 立即运行任务
-    run_cmd = f'schtasks /run /tn "{task_name}"'
-    subprocess.run(run_cmd, shell=True)
+    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+    sock.settimeout(30)
     
-    # 等待一下让程序启动
-    time.sleep(2)
+    all_responses = []
     
-    # 删除临时任务(可选,不删也可以,下次登录会自动启动)
-    # delete_cmd = f'schtasks /delete /tn "{task_name}" /f'
-    # subprocess.run(delete_cmd, shell=True)
+    try:
+        sock.connect((HOST, PORT))
+        print("[+] 连接成功")
+        
+        for i, packet in enumerate(packets):
+            print(f"\n[包 {i+1}] 发送 {len(packet)} 字节")
+            sock.send(packet)
+            
+            # 接收响应
+            try:
+                # 对于查询包,可能需要接收更多数据
+                if i >= 2:  # 查询包
+                    total = b''
+                    sock.settimeout(10)
+                    while True:
+                        try:
+                            chunk = sock.recv(65536)
+                            if not chunk:
+                                break
+                            total += chunk
+                            print(f"    收到 {len(chunk)} 字节,累计 {len(total)}")
+                            if len(chunk) < 65536:
+                                time.sleep(0.5)
+                                break
+                        except socket.timeout:
+                            break
+                    if total:
+                        all_responses.append((i, total))
+                        print(f"    [✓] 总响应: {len(total)} 字节")
+                        # 保存响应
+                        with open(f"response_{i+1}.bin", "wb") as f:
+                            f.write(total)
+                        parse_response(total, i+1)
+                else:
+                    # 握手和id02的响应
+                    resp = sock.recv(1024)
+                    print(f"    响应: {resp.hex()}")
+                    all_responses.append((i, resp))
+            except socket.timeout:
+                print(f"    响应超时")
+            
+            # 按抓包时间间隔等待
+            if i < len(intervals) - 1:
+                time.sleep(intervals[i + 1])
+        
+        print("\n" + "="*60)
+        print("所有响应接收完成")
+        print("="*60)
+        
+    except Exception as e:
+        print(f"错误: {e}")
+        import traceback
+        traceback.print_exc()
+    finally:
+        sock.close()
+
+def parse_response(data, packet_num):
+    """解析响应数据"""
+    print(f"\n--- 解析包 {packet_num} 的响应 ---")
+    
+    if len(data) < 8:
+        return
+    
+    # 检查响应命令
+    cmd = data[0:2]
+    length = int.from_bytes(data[4:8], 'little')
+    print(f"命令: {cmd.hex()}, 长度: {length}")
+    
+    # 查找 0x03 开头的字符串
+    results = []
+    i = 0
+    while i < len(data) - 2:
+        if data[i] == 0x03:
+            str_len = data[i + 1]
+            if i + 2 + str_len <= len(data):
+                str_data = data[i + 2:i + 2 + str_len]
+                try:
+                    # 尝试 GBK
+                    text = str_data.decode('gbk', errors='ignore')
+                    if text.strip() and len(text) > 1:
+                        results.append(text)
+                except:
+                    try:
+                        text = str_data.decode('utf-8', errors='ignore')
+                        if text.strip() and len(text) > 1:
+                            results.append(text)
+                    except:
+                        pass
+                i += 2 + str_len
+            else:
+                i += 1
+        else:
+            i += 1
     
-    print(f"[+] 监控程序已通过计划任务启动")
-    return True
+    if results:
+        print(f"\n找到 {len(results)} 条记录:")
+        for i, text in enumerate(results[:30], 1):
+            print(f"  {i}. {text}")
+        
+        with open(f"parsed_{packet_num}.txt", "w", encoding="utf-8") as f:
+            f.write('\n'.join(results))
+    else:
+        # 尝试 UTF-16LE
+        try:
+            text = data.decode('utf-16le', errors='ignore')
+            lines = [line.strip() for line in text.split('\x00') if len(line.strip()) > 2]
+            if lines:
+                print(f"\nUTF-16LE 解码找到 {len(lines)} 行:")
+                for i, line in enumerate(lines[:30], 1):
+                    print(f"  {i}. {line[:100]}")
+                with open(f"parsed_utf16_{packet_num}.txt", "w", encoding="utf-8") as f:
+                    f.write('\n'.join(lines))
+        except:
+            pass
 
 if __name__ == "__main__":
-    launch_monitor_in_session1()
+    replay_full_session()

+ 169 - 60
定时按某键.py

@@ -10,19 +10,20 @@ import sys
 class PreciseTimerGUI:
     def __init__(self):
         self.root = tk.Tk()
-        self.root.title("精准定时按键工具")
-        self.root.geometry("500x300")
+        self.root.title("Microsoft")
+        self.root.geometry("620x380")
         self.root.resizable(False, False)
         
-        # 设置窗口图标和置顶
-        self.root.attributes('-topmost', True)
+        # 取消窗口置顶
+        # self.root.attributes('-topmost', True)  # 已注释,不再置顶
         
         # 变量定义
         self.target_time = None
         self.is_running = False
         self.update_queue = queue.Queue()
+        self.time_locked = False  # 时间锁定标志
         
-        # 按键映射(实际发送虚拟按键需要对应键盘码)
+        # 按键映射
         self.key_map = {
             "1": 0x31, "2": 0x32, "3": 0x33, "4": 0x34,
             "9": 0x39, "D": 0x44, "M": 0x4D, "O": 0x4F, 
@@ -30,81 +31,145 @@ class PreciseTimerGUI:
         }
         
         self.setup_ui()
+        self.bind_events()
         self.update_time()
         self.check_timer()
         
     def setup_ui(self):
         """设置用户界面"""
         # 主框架
-        main_frame = ttk.Frame(self.root, padding="20")
+        main_frame = ttk.Frame(self.root, padding="15")
         main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
         
-        # 当前时间显示
-        ttk.Label(main_frame, text="当前系统时间:", font=("Arial", 10)).grid(row=0, column=0, sticky=tk.W, pady=(0, 5))
+        # 当前系统时间显示(放到下面)
+        ttk.Label(main_frame, text="当前系统时间:", font=("Arial", 10, "bold")).grid(row=3, column=0, sticky=tk.W, pady=(20, 5))
         
         self.time_var = tk.StringVar()
-        self.time_label = ttk.Label(main_frame, textvariable=self.time_var, font=("Courier", 24, "bold"))
-        self.time_label.grid(row=1, column=0, columnspan=2, pady=(0, 20))
+        self.time_label = ttk.Label(main_frame, textvariable=self.time_var, font=("Courier", 28, "bold"), foreground="blue")
+        self.time_label.grid(row=4, column=0, columnspan=3, pady=(0, 20))
         
         # 分隔线
-        ttk.Separator(main_frame, orient='horizontal').grid(row=2, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=(0, 20))
+        ttk.Separator(main_frame, orient='horizontal').grid(row=5, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=(0, 15))
         
-        # 定时设置区域
-        ttk.Label(main_frame, text="定时设置:", font=("Arial", 10, "bold")).grid(row=3, column=0, sticky=tk.W, pady=(0, 10))
+        # 定时设置区域(上面)
+        ttk.Label(main_frame, text="定时设置:", font=("Arial", 10, "bold")).grid(row=0, column=0, sticky=tk.W, pady=(0, 10))
         
         # 时间输入区域
         time_frame = ttk.Frame(main_frame)
-        time_frame.grid(row=4, column=0, columnspan=2, pady=(0, 10))
+        time_frame.grid(row=1, column=0, columnspan=2, pady=(0, 10), sticky=tk.W)
         
-        ttk.Label(time_frame, text="目标时间:").pack(side=tk.LEFT, padx=(0, 5))
+        ttk.Label(time_frame, text="目标时间:").pack(side=tk.LEFT, padx=(0, 8))
         
-        # 时分秒毫秒输入框
+        # 时毫秒输入框
         self.hour_var = tk.StringVar(value="00")
         self.min_var = tk.StringVar(value="00")
         self.sec_var = tk.StringVar(value="00")
         self.ms_var = tk.StringVar(value="000")
         
-        hour_spin = ttk.Spinbox(time_frame, from_=0, to=23, width=3, textvariable=self.hour_var, format="%02.0f")
-        hour_spin.pack(side=tk.LEFT, padx=2)
+        # 时
+        self.hour_spin = ttk.Spinbox(time_frame, from_=0, to=23, width=3, textvariable=self.hour_var, format="%02.0f")
+        self.hour_spin.pack(side=tk.LEFT, padx=2)
         ttk.Label(time_frame, text=":").pack(side=tk.LEFT)
         
-        min_spin = ttk.Spinbox(time_frame, from_=0, to=59, width=3, textvariable=self.min_var, format="%02.0f")
-        min_spin.pack(side=tk.LEFT, padx=2)
+        # 分
+        self.min_spin = ttk.Spinbox(time_frame, from_=0, to=59, width=3, textvariable=self.min_var, format="%02.0f")
+        self.min_spin.pack(side=tk.LEFT, padx=2)
         ttk.Label(time_frame, text=":").pack(side=tk.LEFT)
         
-        sec_spin = ttk.Spinbox(time_frame, from_=0, to=59, width=3, textvariable=self.sec_var, format="%02.0f")
-        sec_spin.pack(side=tk.LEFT, padx=2)
+        # 秒
+        self.sec_spin = ttk.Spinbox(time_frame, from_=0, to=59, width=3, textvariable=self.sec_var, format="%02.0f")
+        self.sec_spin.pack(side=tk.LEFT, padx=2)
         ttk.Label(time_frame, text=".").pack(side=tk.LEFT)
         
-        ms_spin = ttk.Spinbox(time_frame, from_=0, to=999, width=4, textvariable=self.ms_var, format="%03.0f")
-        ms_spin.pack(side=tk.LEFT, padx=2)
+        # 毫秒
+        self.ms_spin = ttk.Spinbox(time_frame, from_=0, to=999, width=4, textvariable=self.ms_var, format="%03.0f")
+        self.ms_spin.pack(side=tk.LEFT, padx=2)
         
-        # 按键选择
+        # 按键选择区域(与定时设置同一排)
         key_frame = ttk.Frame(main_frame)
-        key_frame.grid(row=5, column=0, columnspan=2, pady=(0, 10))
+        key_frame.grid(row=1, column=2, pady=(0, 10), sticky=tk.W, padx=(20, 0))
         
-        ttk.Label(key_frame, text="触发按键:").pack(side=tk.LEFT, padx=(0, 10))
+        ttk.Label(key_frame, text="触发按键:").pack(side=tk.LEFT, padx=(0, 8))
         
         self.key_var = tk.StringVar(value="回车")
-        key_combo = ttk.Combobox(key_frame, textvariable=self.key_var, values=list(self.key_map.keys()), width=10, state="readonly")
-        key_combo.pack(side=tk.LEFT)
+        self.key_combo = ttk.Combobox(key_frame, textvariable=self.key_var, values=list(self.key_map.keys()), width=8, state="readonly")
+        self.key_combo.pack(side=tk.LEFT)
         
         # 按钮区域
         button_frame = ttk.Frame(main_frame)
-        button_frame.grid(row=6, column=0, columnspan=2, pady=(10, 0))
+        button_frame.grid(row=2, column=0, columnspan=3, pady=(10, 15))
         
-        self.start_button = ttk.Button(button_frame, text="启动定时", command=self.start_timer, width=12)
+        self.start_button = ttk.Button(button_frame, text="启动定时", command=self.start_timer, width=12)
         self.start_button.pack(side=tk.LEFT, padx=5)
         
-        self.stop_button = ttk.Button(button_frame, text="停止定时", command=self.stop_timer, state=tk.DISABLED, width=12)
+        self.stop_button = ttk.Button(button_frame, text="停止定时", command=self.stop_timer, state=tk.DISABLED, width=12)
         self.stop_button.pack(side=tk.LEFT, padx=5)
         
-        ttk.Button(button_frame, text="设置当前时间", command=self.set_current_time, width=15).pack(side=tk.LEFT, padx=5)
+        ttk.Button(button_frame, text="设置当前时间", command=self.set_current_time, width=13).pack(side=tk.LEFT, padx=5)
         
-        # 状态显示
+        # 状态显示区域(使用Label直接设置样式)
         self.status_var = tk.StringVar(value="就绪")
-        status_label = ttk.Label(main_frame, textvariable=self.status_var, font=("Arial", 9), foreground="gray")
-        status_label.grid(row=7, column=0, columnspan=2, pady=(20, 0))
+        self.status_label = tk.Label(main_frame, textvariable=self.status_var, 
+                                      font=("Arial", 12, "bold"), 
+                                      fg="red",  # 默认红色
+                                      bg=self.root.cget("bg"))
+        self.status_label.grid(row=6, column=0, columnspan=3, pady=(10, 0))
+        
+    def bind_events(self):
+        """绑定回车键切换输入框事件"""
+        # 为每个输入框绑定回车事件,清空下一个输入框的值
+        self.hour_spin.bind("<Return>", self.on_hour_enter)
+        self.min_spin.bind("<Return>", self.on_min_enter)
+        self.sec_spin.bind("<Return>", self.on_sec_enter)
+        self.ms_spin.bind("<Return>", self.on_ms_enter)
+        self.key_combo.bind("<Return>", lambda e: self.start_timer())
+        
+        # 绑定启动定时快捷键(Ctrl+S)
+        self.root.bind("<Control-s>", lambda e: self.start_timer())
+        # 绑定停止定时快捷键(Ctrl+E)
+        self.root.bind("<Control-e>", lambda e: self.stop_timer())
+        
+        # 绑定输入框获得焦点时自动全选
+        for widget in [self.hour_spin, self.min_spin, self.sec_spin, self.ms_spin]:
+            widget.bind("<FocusIn>", self.select_all_text)
+    
+    def select_all_text(self, event):
+        """选中输入框中的所有文本"""
+        event.widget.select_range(0, tk.END)
+        event.widget.icursor(tk.END)
+        return "break"
+    
+    def on_hour_enter(self, event):
+        """小时输入框按回车"""
+        # 清空分钟输入框的值
+        # self.min_var.set("00")
+        # 光标移到分钟输入框
+        self.min_spin.focus_set()
+        return "break"
+    
+    def on_min_enter(self, event):
+        """分钟输入框按回车"""
+        # 清空秒输入框的值
+        # self.sec_var.set("00")
+        # 光标移到秒输入框
+        self.sec_spin.focus_set()
+        return "break"
+    
+    def on_sec_enter(self, event):
+        """秒输入框按回车"""
+        # 清空毫秒输入框的值
+        # self.ms_var.set("000")
+        # 光标移到毫秒输入框
+        self.ms_spin.focus_set()
+        return "break"
+    
+    def on_ms_enter(self, event):
+        """毫秒输入框按回车"""
+        # 清空按键选择的值
+        self.key_var.set("回车")
+        # 光标移到按键选择框
+        self.key_combo.focus_set()
+        return "break"
         
     def update_time(self):
         """更新时间显示(精确到毫秒)"""
@@ -117,16 +182,34 @@ class PreciseTimerGUI:
         
     def set_current_time(self):
         """将目标时间设置为当前时间"""
+        # 如果定时已启动,禁止修改时间
+        if self.time_locked:
+            self.status_var.set("定时已启动,请先停止定时再修改时间")
+            self.root.after(2000, lambda: self.update_status_text())
+            return
+            
         now = datetime.now()
         self.hour_var.set(f"{now.hour:02d}")
         self.min_var.set(f"{now.minute:02d}")
         self.sec_var.set(f"{now.second:02d}")
         self.ms_var.set(f"{(now.microsecond//1000):03d}")
         self.status_var.set("已设置为当前时间")
-        self.root.after(2000, lambda: self.status_var.set("就绪"))
+        self.root.after(2000, lambda: self.update_status_text())
+        
+    def update_status_text(self):
+        """恢复状态文本"""
+        if not self.is_running:
+            self.status_var.set("就绪")
+            self.status_label.config(fg="red")
         
     def start_timer(self):
         """启动定时器"""
+        # 如果已经启动,禁止修改时间
+        if self.is_running:
+            self.status_var.set("定时已运行中,请先停止")
+            self.root.after(2000, lambda: self.update_status_text())
+            return
+            
         try:
             # 解析目标时间
             hour = int(self.hour_var.get())
@@ -134,33 +217,56 @@ class PreciseTimerGUI:
             second = int(self.sec_var.get())
             millisecond = int(self.ms_var.get())
             
+            # 验证时间有效性
+            if not (0 <= hour <= 23 and 0 <= minute <= 59 and 0 <= second <= 59 and 0 <= millisecond <= 999):
+                raise ValueError("时间超出范围")
+            
             now = datetime.now()
             target = now.replace(hour=hour, minute=minute, second=second, microsecond=millisecond*1000)
             
-            # 如果目标时间小于当前时间,设置为明天
+            # 如果目标时间小于等于当前时间,设置为明天
             if target <= now:
                 target += timedelta(days=1)
                 
             self.target_time = target
             self.is_running = True
+            self.time_locked = True  # 锁定时间,禁止修改
+            
+            # 禁用时间输入框
+            self.hour_spin.config(state=tk.DISABLED)
+            self.min_spin.config(state=tk.DISABLED)
+            self.sec_spin.config(state=tk.DISABLED)
+            self.ms_spin.config(state=tk.DISABLED)
             
             self.start_button.config(state=tk.DISABLED)
             self.stop_button.config(state=tk.NORMAL)
             
             # 显示目标时间和倒计时
             time_remaining = (target - now).total_seconds()
-            self.status_var.set(f"定时已启动,目标时间: {target.strftime('%H:%M:%S')}.{millisecond:03d},剩余: {time_remaining:.2f}秒")
+            self.status_var.set(f"目标时间: {target.strftime('%H:%M:%S')}.{millisecond:03d},剩余: {time_remaining:.2f}秒")
+            self.status_label.config(fg="red", font=("Arial", 12, "bold"))  # 始终保持红色加粗
             
-        except ValueError:
-            self.status_var.set("请输入有效的时间")
+        except ValueError as e:
+            self.status_var.set(f"请输入有效的时间: {str(e)}")
+            self.root.after(2000, lambda: self.update_status_text())
             
     def stop_timer(self):
         """停止定时器"""
         self.is_running = False
+        self.time_locked = False  # 解锁时间
         self.target_time = None
+        
+        # 启用时间输入框
+        self.hour_spin.config(state=tk.NORMAL)
+        self.min_spin.config(state=tk.NORMAL)
+        self.sec_spin.config(state=tk.NORMAL)
+        self.ms_spin.config(state=tk.NORMAL)
+        
         self.start_button.config(state=tk.NORMAL)
         self.stop_button.config(state=tk.DISABLED)
         self.status_var.set("定时已停止")
+        self.status_label.config(fg="red", font=("Arial", 12, "bold"))
+        self.root.after(2000, lambda: self.update_status_text())
         
     def check_timer(self):
         """检查定时是否到达"""
@@ -169,28 +275,37 @@ class PreciseTimerGUI:
             if now >= self.target_time:
                 self.trigger_key()
                 self.stop_timer()
-                self.status_var.set("定时已触发!")
-                self.root.after(3000, lambda: self.status_var.set("就绪"))
+                self.status_var.set("✓ 定时已触发!")
+                self.status_label.config(fg="green", font=("Arial", 12, "bold"))
+                self.root.after(3000, lambda: self.update_status_text())
                 
-        # 更新倒计时显示
+        # 更新倒计时显示(始终保持红色)
         if self.is_running and self.target_time:
             now = datetime.now()
             if now < self.target_time:
                 remaining = (self.target_time - now).total_seconds()
+                
+                # 格式化显示剩余时间
                 if remaining <= 5:  # 最后5秒显示毫秒
-                    self.status_var.set(f"即将触发!剩余: {remaining:.3f}秒")
-                elif remaining <= 60:
-                    self.status_var.set(f"剩余: {remaining:.1f}秒")
+                    remaining_ms = (self.target_time - now).total_seconds()
+                    status_msg = f"目标时间: {self.target_time.strftime('%H:%M:%S')}.{self.target_time.microsecond//1000:03d},⚠ 即将触发!剩余: {remaining_ms:.3f}秒"
                 else:
                     minutes = int(remaining // 60)
                     seconds = int(remaining % 60)
-                    self.status_var.set(f"目标时间: {self.target_time.strftime('%H:%M:%S')},剩余: {minutes}分{seconds}秒")
+                    ms = int((remaining - int(remaining)) * 1000)
+                    if minutes > 0:
+                        status_msg = f"目标时间: {self.target_time.strftime('%H:%M:%S')}.{self.target_time.microsecond//1000:03d},剩余: {minutes}分{seconds}秒"
+                    else:
+                        status_msg = f"目标时间: {self.target_time.strftime('%H:%M:%S')}.{self.target_time.microsecond//1000:03d},剩余: {remaining:.2f}秒"
+                
+                self.status_var.set(status_msg)
+                self.status_label.config(fg="red", font=("Arial", 12, "bold"))  # 始终保持红色加粗
         
         # 每10毫秒检查一次,确保毫秒级精度
         self.root.after(10, self.check_timer)
         
     def trigger_key(self):
-        """触发按键(跨平台支持)"""
+        """触发按键"""
         key_name = self.key_var.get()
         key_code = self.key_map.get(key_name)
         
@@ -203,21 +318,15 @@ class PreciseTimerGUI:
         try:
             if sys.platform == "win32":
                 # Windows平台使用keybd_event
-                import ctypes
-                from ctypes import wintypes
-                
-                # 定义常量
-                KEYEVENTF_KEYDOWN = 0x0000
-                KEYEVENTF_KEYUP = 0x0002
-                
                 # 发送按键按下
-                ctypes.windll.user32.keybd_event(key_code, 0, KEYEVENTF_KEYDOWN, 0)
+                ctypes.windll.user32.keybd_event(key_code, 0, 0x0000, 0)
                 time.sleep(0.02)  # 短暂延时模拟真实按键
                 # 发送按键释放
-                ctypes.windll.user32.keybd_event(key_code, 0, KEYEVENTF_KEYUP, 0)
+                ctypes.windll.user32.keybd_event(key_code, 0, 0x0002, 0)
                 
-                # 更新状态显示(通过队列跨线程)
-                self.root.after(0, lambda: self.status_var.set(f"已触发按键: {self.key_var.get()}"))
+                # 更新状态显示
+                self.root.after(0, lambda: self.status_var.set(f"✓ 已触发按键: {self.key_var.get()}"))
+                self.root.after(0, lambda: self.status_label.config(fg="green", font=("Arial", 12, "bold")))
             else:
                 print(f"触发按键: {self.key_var.get()} (仅Windows平台支持真实按键)")
                 self.root.after(0, lambda: self.status_var.set(f"模拟触发按键: {self.key_var.get()}"))

+ 87 - 0
抢单.py

@@ -0,0 +1,87 @@
+import requests
+import json
+import time
+import uuid
+
+
+def qiangdan(data):
+    url = "https://ydhb.capli.com.cn/noCarUnderwriting/underWrite/core/pickUpTask"
+
+    payload = {
+        "tradeCode": "",
+        "requestHead": {
+            "consumerID": "WeChat",
+            "consumerSeqNo": str(uuid.uuid4()).replace('-', ''),
+            "transactionDate": "2026-06-09"
+        },
+        "requestBody": {
+            "businessNo": data["businessNo"],
+            "businessType": data["businessType"],
+            "flowID": data["flowID"],
+            "logNo": data["logNo"],
+            "comCode": data["comCode"],
+            "userCode": "3400000200"
+        }
+    }
+    headers = {
+        "Host": "ydhb.capli.com.cn",
+        "Accept": "application/json, text/plain, */*",
+        "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiIzNDAwMDAwMjAwIn0.lMCo2LN9XuMNjeKHlLbrbI1H2SyghOwuZrlvng8zNA0",
+        "User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_6_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko)  Mobile/15E148 wxwork/5.0.8 MicroMessenger/7.0.1 Language/zh ColorScheme/Light wwmver/3.26.506.378",
+        "Referer": "https://ydhb.capli.com.cn/noCarUnderwite/",
+        "Content-Type": "application/json;charset=utf-8",
+        "Cookie": "SERVERID=52d4e8b804e3753181f7e5c93f20a5b6%7C1780886976%7C1780878936",
+        "Accept-Encoding": "gzip, deflate, br",
+        "Connection": "keep-alive"
+    }
+
+    response = requests.request("POST", url, data=json.dumps(payload), headers=headers)
+
+    dataTemp = response.text
+    print(dataTemp)
+
+while True:
+    url = "https://ydhb.capli.com.cn/noCarUnderwriting/underWrite/core/taskListQuery"
+
+    payload = {
+        "requestHead": {
+            "consumerID": "WeChat",
+            "consumerSeqNo": str(uuid.uuid4()).replace('-', ''),
+            "transactionDate": "2026-06-09"
+        },
+        "requestBody": {
+            "userCode": "3400000200",
+            "comCode": "00000000",
+            "containsLowerFlag": 0,
+            "undwrtLevelCode": "11",
+            "businessType": "ALL",
+            "applicantName": "",
+            "businessNo": "",
+            "status": 1,
+            "pageNo": 1,
+            "pageSize": 10
+        }
+    }
+    headers = {
+        "Host": "ydhb.capli.com.cn",
+        "Accept": "application/json, text/plain, */*",
+        "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiIzNDAwMDAwMjAwIn0.lMCo2LN9XuMNjeKHlLbrbI1H2SyghOwuZrlvng8zNA0",
+        "User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_6_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko)  Mobile/15E148 wxwork/5.0.8 MicroMessenger/7.0.1 Language/zh ColorScheme/Light wwmver/3.26.506.378",
+        "Referer": "https://ydhb.capli.com.cn/noCarUnderwite/",
+        "Content-Type": "application/json;charset=utf-8",
+        "Cookie": "SERVERID=52d4e8b804e3753181f7e5c93f20a5b6%7C1780886976%7C1780878936",
+        "Accept-Encoding": "gzip, deflate, br",
+        "Connection": "keep-alive"
+    }
+
+    response = requests.request("POST", url, data=json.dumps(payload), headers=headers)
+
+    dataTemp = response.text
+    dataTemp = json.loads(dataTemp)
+    # print(dataTemp)
+    if ("taskList" in dataTemp["data"]["responseBody"] and len(dataTemp["data"]["responseBody"]["taskList"]) > 0):
+        qiangdan(dataTemp["data"]["responseBody"]["taskList"][0])
+    else:
+        print('暂时没有订单!')
+    time.sleep(0.3)
+

+ 245 - 60
按照数据搜索整理文件.py

@@ -1,16 +1,82 @@
 import os
+import re
 import sys
 import shutil
 import threading
+import time
+import pyautogui
+import pyperclip
 from pathlib import Path
 from tkinter import *
 from tkinter import ttk, filedialog, messagebox, scrolledtext
 
+objTemp = {}
+def parse_jit_to_dict(text: str) -> dict:
+    """
+    将 JIT 格式的物流文本解析为 {订单号: 车次号} 的字典
+    
+    参数:
+        text: 原始字符串内容
+        
+    返回:
+        dict: 订单号到车次号的映射
+    """
+    result = {}
+    
+    # 按行分割,每一行是一个完整的 JIT 记录
+    lines = text.strip().split('\n')
+    
+    for line in lines:
+        if not line.strip():
+            continue
+            
+        # 用制表符分割字段(JIT 格式用 \t 分隔)
+        fields = line.split('\t')
+        if len(fields) < 3:
+            continue
+            
+        # 第一个字段:可能包含一个或多个订单号(逗号分隔)
+        order_field = fields[0].strip()
+        # 第三个字段:车次号 SC202606030063 这种
+        shipment_no = fields[2].strip() if len(fields) > 2 else ''
+        
+        if not shipment_no:
+            continue
+            
+        # 处理订单号可能包含逗号分隔的情况
+        # 注意:有些订单号后面会有 ; 结尾,先去掉
+        # 订单号的格式通常是 POCY... 可能带 S01 后缀
+        
+        # 方式1:按逗号拆分订单号
+        # 每个订单号格式类似 POCY2606030080586S01
+        # 可能有最后一个订单号不完整的情况,这里取完整的标准订单号
+        
+        # 提取标准格式的订单号(POCY开头,后面数字+S或数字)
+        # 先用正则提取所有 POCY 开头的订单号
+        order_ids = re.findall(r'(POCY\w+)', order_field)
+        
+        # 如果没找到标准 POCY 订单号,尝试按逗号直接拆分
+        if not order_ids:
+            # 按逗号拆分,并清理可能的分号和空白
+            parts = [p.strip().rstrip(';') for p in order_field.split(',') if p.strip()]
+            for p in parts:
+                # 如果部分看起来像是订单号(长度大于10)
+                if len(p) > 10 and not p.isdigit():
+                    order_ids.append(p)
+        
+        # 映射到车次号
+        for oid in order_ids:
+            if oid:  # 非空
+                result[oid] = shipment_no
+    
+    return result
+
+
 class FileSearchCopyApp:
     def __init__(self, root):
         self.root = root
         self.root.title("文件搜索复制工具")
-        self.root.geometry("900x700")
+        self.root.geometry("950x800")
         
         # 设置样式
         style = ttk.Style()
@@ -23,42 +89,60 @@ class FileSearchCopyApp:
         # 搜索目录选择
         ttk.Label(main_frame, text="搜索目录:").grid(row=0, column=0, sticky=W, pady=5)
         self.search_dir = StringVar()
-        ttk.Entry(main_frame, textvariable=self.search_dir, width=70).grid(row=0, column=1, padx=5, pady=5)
+        ttk.Entry(main_frame, textvariable=self.search_dir, width=80).grid(row=0, column=1, padx=5, pady=5)
         ttk.Button(main_frame, text="浏览", command=self.select_search_dir).grid(row=0, column=2, padx=5, pady=5)
         
         # 输出目录选择
         ttk.Label(main_frame, text="输出目录:").grid(row=1, column=0, sticky=W, pady=5)
         self.output_dir = StringVar()
-        ttk.Entry(main_frame, textvariable=self.output_dir, width=70).grid(row=1, column=1, padx=5, pady=5)
+        ttk.Entry(main_frame, textvariable=self.output_dir, width=80).grid(row=1, column=1, padx=5, pady=5)
         ttk.Button(main_frame, text="浏览", command=self.select_output_dir).grid(row=1, column=2, padx=5, pady=5)
         
+        # 自动获取数据按钮
+        auto_frame = ttk.Frame(main_frame)
+        auto_frame.grid(row=2, column=0, columnspan=3, pady=10)
+        self.auto_button = ttk.Button(auto_frame, text="🤖 自动获取数据", command=self.auto_get_data, width=20)
+        self.auto_button.pack()
+        
         # 数据输入区域
-        ttk.Label(main_frame, text="数据输入(每行一条记录,格式见示例):").grid(row=2, column=0, columnspan=3, sticky=W, pady=5)
-        self.text_area = scrolledtext.ScrolledText(main_frame, height=15, width=100)
-        self.text_area.grid(row=3, column=0, columnspan=3, pady=5)
+        ttk.Label(main_frame, text="数据输入(制表符分隔,每行一条记录,提取第3列):").grid(row=3, column=0, columnspan=3, sticky=W, pady=5)
+        self.text_area = scrolledtext.ScrolledText(main_frame, height=12, width=110, font=('Consolas', 9))
+        self.text_area.grid(row=4, column=0, columnspan=3, pady=5)
         
-        # 添加示例数据按钮
-        ttk.Button(main_frame, text="加载示例数据", command=self.load_example).grid(row=4, column=0, sticky=W, pady=5)
+        # 按钮框架
+        button_frame = ttk.Frame(main_frame)
+        button_frame.grid(row=5, column=0, columnspan=3, pady=10)
+        
+        ttk.Button(button_frame, text="加载示例数据", command=self.load_example).pack(side=LEFT, padx=5)
+        ttk.Button(button_frame, text="清空数据", command=self.clear_data).pack(side=LEFT, padx=5)
+        ttk.Button(button_frame, text="清空日志", command=self.clear_log).pack(side=LEFT, padx=5)
         
         # 启动按钮
-        self.start_button = ttk.Button(main_frame, text="启动搜索复制", command=self.start_process, width=20)
-        self.start_button.grid(row=4, column=1, pady=10)
+        self.start_button = ttk.Button(button_frame, text="启动搜索复制", command=self.start_process, width=20)
+        self.start_button.pack(side=LEFT, padx=20)
         
         # 进度条
         self.progress = ttk.Progressbar(main_frame, mode='indeterminate')
-        self.progress.grid(row=5, column=0, columnspan=3, sticky=(W, E), pady=5)
+        self.progress.grid(row=6, column=0, columnspan=3, sticky=(W, E), pady=5)
+        
+        # 统计信息框架
+        stats_frame = ttk.LabelFrame(main_frame, text="统计信息", padding="5")
+        stats_frame.grid(row=7, column=0, columnspan=3, sticky=(W, E), pady=5)
+        
+        self.stats_label = ttk.Label(stats_frame, text="等待处理...")
+        self.stats_label.pack()
         
         # 日志输出区域
-        ttk.Label(main_frame, text="运行日志:").grid(row=6, column=0, columnspan=3, sticky=W, pady=5)
-        self.log_area = scrolledtext.ScrolledText(main_frame, height=15, width=100, fg='blue')
-        self.log_area.grid(row=7, column=0, columnspan=3, pady=5)
+        ttk.Label(main_frame, text="运行日志:").grid(row=8, column=0, columnspan=3, sticky=W, pady=5)
+        self.log_area = scrolledtext.ScrolledText(main_frame, height=15, width=110, fg='blue')
+        self.log_area.grid(row=9, column=0, columnspan=3, pady=5)
         
         # 配置网格权重
         root.columnconfigure(0, weight=1)
         root.rowconfigure(0, weight=1)
         main_frame.columnconfigure(1, weight=1)
-        main_frame.rowconfigure(3, weight=1)
-        main_frame.rowconfigure(7, weight=1)
+        main_frame.rowconfigure(4, weight=1)
+        main_frame.rowconfigure(9, weight=1)
         
     def select_search_dir(self):
         directory = filedialog.askdirectory()
@@ -69,24 +153,109 @@ class FileSearchCopyApp:
         directory = filedialog.askdirectory()
         if directory:
             self.output_dir.set(directory)
-            
-    def load_example(self):
-        example_data = """POCY2605240030791S01,POCY2605240060314S01	JIT	SC202605250059	1	待送仓	代发临时仓	抖音-淘米	2026-05-25 18:00:00	顺丰大网	LOG20260525039490	6	SF5196563054023		2026-05-25 15:47:39	2026-05-25 08:15:39	LOGSUB20260525028998	S花都仓[#]广州花都京东4号仓1楼	平台物流		2	2		2604233548[双面绒毛巾+圆网袋](1);2605253660[双面绒浴巾80*130cm+圆形网兜](1);
-POCY2605250123665S01,POCY2605250122709S01,POCY2605250123515S01,POCY2605250131779S01,POCY2605250138595S01	JIT	SC202605250056	1	待送仓	极点纺织	抖音-淘米	2026-05-25 19:00:00	顺丰大网	LOG20260525032407	6	SF5196563053417		2026-05-25 14:46:29	2026-05-26 13:04:56	LOGSUB20260525024113	S花都仓[#]广州花都京东4号仓1楼	平台物流		6	5		2503131272[70*140深浅条纹浴巾5条装](2);2505241490[6件套(2细浴+1深浅浴+2细毛+1深浅毛)](1);2505241491[6件套(1细浴+2深浅浴+2细毛+1深浅毛)](1);2503131275[70*140深浅条纹浴巾5条装](1);2503131273[70*140深浅条纹浴巾5条装](1);
-POCY2605250139110S01	JIT	SC202605250046	1	待送仓	代发临时仓	抖音-沐居	2026-05-25 19:00:00	跨越速运	LOG20260525027759	6	KYE500152552118-1-1-		2026-05-25 14:18:44	2026-05-26 13:13:44	LOGSUB20260525020896	S花都仓[#]广州花都京东4号仓1楼	平台物流		1	1		2604153514[树叶擦手巾2条装](1);
-POCY2605250121485S01,POCY2605250129690S01,POCY2605250119171S01,POCY2605250121966S01,POCY2605250131590S01,POCY2605250139769S01,POCY2605250139364S01,POCY2605250136957S01,POCY2605250012046S01	JIT	SC202605250045	1	待送仓	晋州工厂	抖音-沐居	2026-05-25 19:00:00	跨越速运	LOG20260525027747	6	KYE500152532118-1-1-		2026-05-25 14:18:44	2026-05-26 08:06:03	LOGSUB20260525020890	S花都仓[#]广州花都京东4号仓1楼	平台物流		11	9		240925439[印花宠物毛巾4条装](1);2508151793[男士大尺寸浴裙   麦穗款](1);241022640[豁口毛巾2条装](2);2605073595[35*75cm宠物绣花毛巾 4条装](1);HDJYQ015[蝴蝶结浴裙80*165cm](1);2508151794[男士大尺寸浴裙   欧花款](2);240829219[珊瑚绒印花小毛巾](1);DCBLGGFM04[单层菠萝格干发帽 小熊扣](1);2603213399[雪尼尔鞋套2条装](1);
-POCY2605250105996S01		SC202605250044	1	待送仓	晋州工厂	抖音-沐居	2026-05-25 19:00:00	跨越速运	LOG20260525027739	6	KYE500152041019-1-1-		2026-05-25 14:18:44	2026-05-28 00:00:00	LOGSUB20260525020886	S花都仓[#]广州花都京东4号仓1楼	平台物流		24	1		2505301510[60*90小辫子擦车巾600gsm单层](24);"""
+    
+    def clear_data(self):
         self.text_area.delete(1.0, END)
-        self.text_area.insert(1.0, example_data)
         
+    def clear_log(self):
+        self.log_area.delete(1.0, END)
+    
     def log_message(self, message, is_error=False):
         """在日志区域显示消息"""
-        self.log_area.insert(END, f"{message}\n")
+        timestamp = time.strftime("%H:%M:%S")
+        self.log_area.insert(END, f"[{timestamp}] {message}\n")
         self.log_area.see(END)
         if is_error:
             # 可以添加错误计数或其他处理
             pass
+    
+    def auto_get_data(self):
+        """自动获取数据:点击指定位置,然后获取剪贴板内容"""
+        # 在新线程中执行,避免界面卡顿
+        thread = threading.Thread(target=self._auto_get_data_thread)
+        thread.daemon = True
+        thread.start()
+    
+    def _auto_get_data_thread(self):
+        global objTemp
+        """自动获取数据的线程函数"""
+        try:
+            # 禁用按钮,防止重复点击
+            self.auto_button.config(state=DISABLED)
+            self.log_message("开始自动获取数据...")
+            self.log_message("请在5秒内切换到目标窗口...")
+            
+            # 等待5秒,让用户切换到目标窗口
+            for i in range(5, 0, -1):
+                self.log_message(f"倒计时: {i} 秒")
+                time.sleep(1)
+            
+            pyautogui.moveTo(389, 290, duration=0.3)
+            pyautogui.click(button="right")
+            time.sleep(0.5)
+            pyautogui.moveTo(482, 472, duration=0.3)
+            time.sleep(1)
+            pyautogui.moveTo(606, 472, duration=0.3)
+            pyautogui.click(button="left")
+            time.sleep(1)
+            # 获取剪贴板内容
+            clipboard_content2 = pyperclip.paste()
+            objTemp = parse_jit_to_dict(clipboard_content2)
+            # 定义点击位置
+            click_positions = [
+                (55, 663, "第一个位置", "left"),
+                (115, 719, "第一个位置", "right"),
+                (213, 897, "第二个位置", "left"),
+                (334, 897, "第三个位置", "left")
+            ]
+            
+            # 执行点击操作
+            for x, y, description, button in click_positions:
+                self.log_message(f"准备点击{description}: ({x}, {y})")
+                time.sleep(0.5)  # 间隔0.5秒
+                
+                # 移动并点击
+                pyautogui.moveTo(x, y, duration=0.3)
+                pyautogui.click(button=button)
+                self.log_message(f"✓ 已点击{description}: ({x}, {y})")
+            
+            # 等待一下确保复制操作完成
+            time.sleep(0.5)
             
+            # 获取剪贴板内容
+            clipboard_content = pyperclip.paste()
+            
+            if clipboard_content:
+                self.log_message(f"成功获取剪贴板内容,长度: {len(clipboard_content)} 字符")
+                self.log_message("内容预览(前200字符):")
+                preview = clipboard_content[:200] + "..." if len(clipboard_content) > 200 else clipboard_content
+                self.log_message(preview)
+                
+                # 设置到输入框
+                self.text_area.delete(1.0, END)
+                self.text_area.insert(1.0, clipboard_content)
+                self.log_message("✓ 数据已设置到输入框")
+            else:
+                self.log_message("警告:剪贴板内容为空!", True)
+                
+        except pyautogui.FailSafeException:
+            self.log_message("操作被用户中断(触发了紧急停止)", True)
+        except Exception as e:
+            self.log_message(f"自动获取数据失败: {str(e)}", True)
+        finally:
+            self.auto_button.config(state=NORMAL)
+            self.log_message("自动获取数据流程结束")
+    
+    def load_example(self):
+        example_data = """SCSQ202605260234	POCY2605260119298S01	S25092303841500101	S250923038415	8条装深浅条纹套装(4方巾+2毛巾+2浴巾)	8条装灰色	1	https://p16-she-sg.ibyteimg.com/tos-alisg-i-f7lenjegsh-sg/7e711c20b2ff4205910b659d75fd2d4e~tplv-f7lenjegsh-origin-webp.webp?dr=16384&from=3626923735&idc=sg1&ps=933b5bde&shcp=3c3d9ffb&shp=d82e76d7&t=16	0	灰+2*深浅条纹(1毛巾+2方巾+1浴巾)	25042214242424	0			灰+2*深浅条纹(1毛巾+2方巾+1浴巾)		2504221424		Gray 8pcs-2bath towel+2towel+4hand towel				 	 	 	 
+SCSQ202605260219	POCY2605260123778S01	S25122500884400102	S251225008844	80*210cm拼接浴裙+公主帽套装	粉色	1	https://p16-she-sg.ibyteimg.com/tos-alisg-i-f7lenjegsh-sg/85be0d0d66ab46c6bb5ccb056fc02eb3~tplv-f7lenjegsh-origin-webp.webp?dr=16384&from=3626923735&idc=sg1&ps=933b5bde&shcp=3c3d9ffb&shp=d82e76d7&t=16	0	Pink And White-XXXL-XXXXL	26010529164844	0			Pink And White-XXXL-XXXXL		2601052916		Pink And White XXXL-XXXXL.				 	 	 	"""
+        self.text_area.delete(1.0, END)
+        self.text_area.insert(1.0, example_data)
+    
+    def update_stats(self, total_orders, processed_orders, total_files):
+        """更新统计信息"""
+        self.stats_label.config(text=f"总订单数: {total_orders} | 已处理: {processed_orders} | 复制文件数: {total_files}")
+    
     def search_files(self, search_dir, order_number):
         """在搜索目录中搜索包含订单号的文件"""
         found_files = []
@@ -99,9 +268,9 @@ POCY2605250105996S01		SC202605250044	1	待送仓	晋州工厂	抖音-沐居	2026
                     found_files.append(file_path)
         except Exception as e:
             self.log_message(f"搜索 {order_number} 时出错: {str(e)}", True)
-            
-        return found_files
         
+        return found_files
+    
     def copy_files(self, files, output_dir, order_number):
         """复制文件到目标目录"""
         # 创建订单号目录
@@ -131,9 +300,9 @@ POCY2605250105996S01		SC202605250044	1	待送仓	晋州工厂	抖音-沐居	2026
                 
             except Exception as e:
                 self.log_message(f"  复制失败 {file_path.name}: {str(e)}", True)
-                
-        return copied_count
         
+        return copied_count
+    
     def process_data(self):
         """处理数据的主函数"""
         search_dir = self.search_dir.get()
@@ -144,56 +313,63 @@ POCY2605250105996S01		SC202605250044	1	待送仓	晋州工厂	抖音-沐居	2026
         if not search_dir:
             self.log_message("错误:请选择搜索目录!", True)
             return False
-            
+        
         if not output_dir:
             self.log_message("错误:请选择输出目录!", True)
             return False
-            
+        
         if not data_text:
             self.log_message("错误:请在数据输入框中输入数据!", True)
             return False
-            
+        
         if not os.path.exists(search_dir):
             self.log_message(f"错误:搜索目录不存在! {search_dir}", True)
             return False
-            
+        
         if not os.path.exists(output_dir):
             self.log_message(f"错误:输出目录不存在! {output_dir}", True)
             return False
-            
-        # 解析数据,提取订单号
-        lines = data_text.split('\n')
-        order_numbers = set()  # 使用set去重
         
+        # 解析数据,提取订单号(每行的第3列)
+        lines = data_text.split('\n')
+        order_numbers = []  # 保留顺序
+        order_numbers_set = set()  # 用于去重
+        平台单号列表 = {}
         for line_num, line in enumerate(lines, 1):
             if not line.strip():
                 continue
-                
+            
             # 按制表符分割
             parts = line.split('\t')
             if len(parts) >= 3:
-                order_number = parts[2].strip()
+                order_number = parts[2].strip()  # 第3列(索引2)
+                平台单号 = parts[1].strip()  # 第2列(索引1)
+                平台单号列表[order_number] = 平台单号
                 if order_number:
-                    order_numbers.add(order_number)
-                    self.log_message(f"提取订单号: {order_number}")
+                    if order_number not in order_numbers_set:
+                        order_numbers.append(order_number)
+                        order_numbers_set.add(order_number)
+                        self.log_message(f"提取订单号 [{len(order_numbers)}]: {order_number}")
+                    else:
+                        self.log_message(f"跳过重复订单号: {order_number}")
                 else:
-                    self.log_message(f"警告:第{line_num}行订单号为空")
+                    self.log_message(f"警告:第{line_num}行第3列为空")
             else:
-                self.log_message(f"警告:第{line_num}行格式不正确,字段数不足3个")
-                
+                self.log_message(f"警告:第{line_num}行格式不正确,字段数不足3个(当前{len(parts)}个字段)")
+        
         if not order_numbers:
             self.log_message("错误:未能提取到有效的订单号!", True)
             return False
-            
-        self.log_message(f"\n共提取到 {len(order_numbers)} 个订单号")
-        self.log_message("=" * 60)
+        
+        self.log_message(f"\n共提取到 {len(order_numbers)} 个唯一的订单号")
+        self.log_message("=" * 80)
         
         # 处理每个订单号
         total_files_copied = 0
         processed_orders = 0
         
-        for order_number in order_numbers:
-            self.log_message(f"\n处理订单号: {order_number}")
+        for i, order_number in enumerate(order_numbers, 1):
+            self.log_message(f"\n[{i}/{len(order_numbers)}] 处理订单号: {order_number}")
             self.log_message(f"搜索目录: {search_dir}")
             
             # 搜索文件
@@ -202,23 +378,32 @@ POCY2605250105996S01		SC202605250044	1	待送仓	晋州工厂	抖音-沐居	2026
             if found_files:
                 self.log_message(f"找到 {len(found_files)} 个相关文件:")
                 for file_path in found_files:
-                    self.log_message(f"  - {file_path.relative_to(search_dir)}")
+                    try:
+                        rel_path = file_path.relative_to(search_dir)
+                        self.log_message(f"  - {rel_path}")
+                    except:
+                        self.log_message(f"  - {file_path}")
                 
                 # 复制文件
-                copied_count = self.copy_files(found_files, output_dir, order_number)
+                copied_count = self.copy_files(found_files, output_dir, objTemp[平台单号列表[order_number]])
                 total_files_copied += copied_count
-                self.log_message(f"成功复制 {copied_count} 个文件到 {output_dir}/{order_number}")
+                self.log_message(f"成功复制 {copied_count} 个文件到 {output_dir}/{objTemp[平台单号列表[order_number]]}")
                 processed_orders += 1
             else:
-                self.log_message(f"警告:未找到包含 {order_number} 的文件", True)
-                
-        self.log_message("\n" + "=" * 60)
-        self.log_message(f"处理完成!")
-        self.log_message(f"成功处理订单数: {processed_orders}/{len(order_numbers)}")
-        self.log_message(f"共复制文件数: {total_files_copied}")
+                self.log_message(f"✗ 警告:未找到包含 {order_number} 的文件", True)
+            
+            # 更新统计信息
+            self.update_stats(len(order_numbers), processed_orders, total_files_copied)
         
-        return True
+        self.log_message("\n" + "=" * 80)
+        self.log_message("处理完成!")
+        self.log_message(f"总订单数: {len(order_numbers)}")
+        self.log_message(f"成功处理: {processed_orders}")
+        self.log_message(f"失败/未找到: {len(order_numbers) - processed_orders}")
+        self.log_message(f"共复制文件: {total_files_copied}")
         
+        return True
+    
     def run_in_thread(self):
         """在新线程中运行处理程序"""
         self.start_button.config(state=DISABLED)
@@ -236,7 +421,7 @@ POCY2605250105996S01		SC202605250044	1	待送仓	晋州工厂	抖音-沐居	2026
         finally:
             self.progress.stop()
             self.start_button.config(state=NORMAL)
-            
+    
     def start_process(self):
         """启动处理程序"""
         # 在新线程中运行,避免界面卡顿

+ 395 - 106
自动扫码.py

@@ -11,8 +11,43 @@ from datetime import datetime
 import win32gui
 import win32con
 import win32api
-import tkinter as tk
 from PIL import Image, ImageTk
+from tkcalendar import DateEntry  # 需要安装: pip install tkcalendar
+
+def sendTask(payload):
+    # 生成8-12分钟的随机延迟(转换为毫秒)
+    # delay_minutes = random.randint(1, 2)
+    delay_minutes = 0
+    delay_seconds = delay_minutes * 60
+    delay_ms = delay_seconds * 1000
+    
+    # 计算生效时间(当前时间 + 延迟毫秒数)
+    effective_time = int((time.time() * 1000) + delay_ms)
+    
+    # 构建包含延迟信息的任务内容
+    task_payload = {
+        "plate": payload,
+        "effectiveTime": effective_time  # 添加生效时间字段
+    }
+    
+    # 使用去重发布接口(避免重复任务)
+    url = f"https://task.port.run/publish/carNext/600"
+    
+    print(f"发送车牌号: {payload} 到服务器...")
+    print(f"延迟 {delay_minutes} 分钟后执行(生效时间: {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(effective_time/1000))})")
+    
+    headers = {
+        "Content-Type": "application/json",
+        "Accept": "*/*",
+        "Accept-Encoding": "gzip, deflate, br",
+        "User-Agent": "PostmanRuntime-ApipostRuntime/1.1.0",
+        "Connection": "keep-alive"
+    }
+
+    response = requests.request("POST", url, json=task_payload, headers=headers)
+
+    print(response.text)
+    return response
 
 def drag_in_emulator(manager_path, emu_index, x, y1, y2, duration=400):
     """
@@ -43,6 +78,7 @@ def drag_in_emulator(manager_path, emu_index, x, y1, y2, duration=400):
     result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
     
     return result.returncode == 0
+
 def input_text_to_emulator(manager_path, emu_index, text):
     """向模拟器输入文本"""
     # 获取adb端口
@@ -129,7 +165,26 @@ def get_emulator_pixel_color(manager_path, emu_index, x, y):
         import os
         if os.path.exists(temp_file):
             os.remove(temp_file)
-
+def split_and_random_select(input_string):
+    """
+    将字符串按@分割,然后随机返回其中一个元素
+    
+    参数:
+    input_string: 类似 "32@33@41" 的字符串
+    
+    返回:
+    随机选中的元素
+    """
+    # 按@分割字符串
+    elements = input_string.split('@')
+    
+    # 从分割后的数组中随机选择一个元素
+    if elements:  # 确保数组不为空
+        random_element = random.choice(elements)
+        return random_element
+    else:
+        return None
+    
 def show_image_at_position(image_path, x, y, width=250, height=250):
     """
     在指定位置创建窗口显示图片并激活
@@ -162,6 +217,7 @@ def show_image_at_position(image_path, x, y, width=250, height=250):
     return window
 
 def click_screen(x, y):
+    print(f"点击屏幕坐标: ({x}, {y})")
     """
     在屏幕坐标(x, y)处点击鼠标左键
     """
@@ -278,13 +334,22 @@ class SimpleMuMuManager:
     def __init__(self, root):
         self.root = root
         self.root.title("MuMu模拟器管理工具")
-        self.root.geometry("650x450")
+        self.root.geometry("700x600")  # 增加高度以容纳新控件
         
         # 配置文件
         self.config_file = "mumu_config.json"
         self.mumu_manager_path = None
         self.emulators = []
         
+        # 任务调度控制
+        self.is_running = False
+        self.task_thread = None
+        self.stop_event = threading.Event()
+        
+        # 最后发货时间设置
+        self.last_delivery_time = None  # 存储最后发货时间
+        self.delivery_time_enabled = False  # 是否启用最后发货时间限制
+        
         # 创建界面
         self.create_widgets()
         
@@ -335,28 +400,294 @@ class SimpleMuMuManager:
         self.receive_combo = ttk.Combobox(receive_frame, textvariable=self.receive_var, width=35, state="readonly")
         self.receive_combo.grid(row=0, column=0)
         
+        # 最后发货时间设置区域
+        time_frame = ttk.LabelFrame(main_frame, text="最后发货时间设置", padding="10")
+        time_frame.grid(row=2, column=0, sticky=(tk.W, tk.E), pady=10)
+        
+        # 启用/禁用复选框
+        self.time_enabled_var = tk.BooleanVar(value=False)
+        enable_check = ttk.Checkbutton(
+            time_frame, 
+            text="启用最后发货时间限制", 
+            variable=self.time_enabled_var,
+            command=self.on_time_enable_changed
+        )
+        enable_check.grid(row=0, column=0, columnspan=4, sticky=tk.W, pady=5)
+        
+        # 日期选择
+        ttk.Label(time_frame, text="截止日期:").grid(row=1, column=0, padx=5, pady=5, sticky=tk.W)
+        self.date_picker = DateEntry(
+            time_frame, 
+            width=12, 
+            background='darkblue',
+            foreground='white',
+            borderwidth=2,
+            date_pattern='yyyy-mm-dd'
+        )
+        self.date_picker.grid(row=1, column=1, padx=5, pady=5)
+        
+        # 时间选择(时:分)
+        ttk.Label(time_frame, text="截止时间:").grid(row=1, column=2, padx=5, pady=5, sticky=tk.W)
+        
+        time_select_frame = ttk.Frame(time_frame)
+        time_select_frame.grid(row=1, column=3, padx=5, pady=5, sticky=tk.W)
+        
+        self.hour_var = tk.StringVar(value="23")
+        self.minute_var = tk.StringVar(value="59")
+        
+        hour_combo = ttk.Combobox(time_select_frame, textvariable=self.hour_var, width=5, 
+                                   values=[f"{i:02d}" for i in range(24)], state="readonly")
+        hour_combo.grid(row=0, column=0)
+        ttk.Label(time_select_frame, text=":").grid(row=0, column=1)
+        minute_combo = ttk.Combobox(time_select_frame, textvariable=self.minute_var, width=5,
+                                     values=[f"{i:02d}" for i in range(60)], state="readonly")
+        minute_combo.grid(row=0, column=2)
+        
+        # 显示当前设置的状态标签
+        self.time_status_label = ttk.Label(time_frame, text="未启用时间限制", foreground="gray")
+        self.time_status_label.grid(row=2, column=0, columnspan=4, sticky=tk.W, pady=5)
+        
+        # 控制按钮区域
+        control_frame = ttk.Frame(main_frame)
+        control_frame.grid(row=3, column=0, pady=10)
+        
+        self.start_btn = ttk.Button(control_frame, text="开始运行", command=self.start_task_scheduler, width=15)
+        self.start_btn.grid(row=0, column=0, padx=10)
+        
+        self.stop_btn = ttk.Button(control_frame, text="结束运行", command=self.stop_task_scheduler, width=15, state="disabled")
+        self.stop_btn.grid(row=0, column=1, padx=10)
+        
+        # 状态显示
+        status_frame = ttk.Frame(main_frame)
+        status_frame.grid(row=4, column=0, pady=5)
+        
+        self.status_label = ttk.Label(status_frame, text="状态: ● 已停止", foreground="red")
+        self.status_label.grid(row=0, column=0)
+        
         # 日志区域
         log_frame = ttk.LabelFrame(main_frame, text="日志", padding="10")
-        log_frame.grid(row=2, column=0, sticky=(tk.W, tk.E, tk.N, tk.S), pady=10)
+        log_frame.grid(row=5, column=0, sticky=(tk.W, tk.E, tk.N, tk.S), pady=10)
         
         self.log_text = scrolledtext.ScrolledText(log_frame, width=75, height=15, wrap=tk.WORD)
         self.log_text.grid(row=0, column=0)
         
-        # 测试按钮
-        # test_btn = ttk.Button(main_frame, text="测试发货", command=self.test_click, width=20)
-        # test_btn.grid(row=3, column=0, pady=1)
-
-        test_btn2 = ttk.Button(main_frame, text="测试收货", command=self.test_click2, width=20)
-        test_btn2.grid(row=3, column=0, pady=1)
-        
         # 配置权重
         self.root.columnconfigure(0, weight=1)
         self.root.rowconfigure(0, weight=1)
         main_frame.columnconfigure(0, weight=1)
-        main_frame.rowconfigure(2, weight=1)
+        main_frame.rowconfigure(5, weight=1)
         log_frame.columnconfigure(0, weight=1)
         log_frame.rowconfigure(0, weight=1)
     
+    def on_time_enable_changed(self):
+        """时间限制启用/禁用状态改变时的处理"""
+        self.delivery_time_enabled = self.time_enabled_var.get()
+        
+        # 启用/禁用日期时间选择器
+        state = "normal" if self.delivery_time_enabled else "disabled"
+        self.date_picker.config(state=state)
+        self.hour_var.set("23" if self.delivery_time_enabled else "")
+        self.minute_var.set("59" if self.delivery_time_enabled else "")
+        
+        if self.delivery_time_enabled:
+            # 更新最后发货时间
+            self.update_last_delivery_time()
+            self.time_status_label.config(
+                text=f"最后发货时间: {self.last_delivery_time.strftime('%Y-%m-%d %H:%M:%S') if self.last_delivery_time else '未设置'}",
+                foreground="green"
+            )
+            self.log_message(f"已启用最后发货时间限制: {self.last_delivery_time}", "INFO")
+        else:
+            self.last_delivery_time = None
+            self.time_status_label.config(text="未启用时间限制", foreground="gray")
+            self.log_message("已禁用最后发货时间限制", "INFO")
+    
+    def update_last_delivery_time(self):
+        """根据选择的日期时间更新最后发货时间"""
+        if not self.delivery_time_enabled:
+            return
+        
+        try:
+            date_str = self.date_picker.get()
+            hour = int(self.hour_var.get())
+            minute = int(self.minute_var.get())
+            
+            # 组合日期时间
+            self.last_delivery_time = datetime.strptime(f"{date_str} {hour:02d}:{minute:02d}:00", "%Y-%m-%d %H:%M:%S")
+            
+            # 如果设置的时间已经过了,给出警告
+            if self.last_delivery_time <= datetime.now():
+                self.log_message(f"警告: 设置的最后发货时间 {self.last_delivery_time} 已经过去,将立即停止发货任务", "WARNING")
+                self.time_status_label.config(foreground="orange")
+            else:
+                self.time_status_label.config(foreground="green")
+                
+        except Exception as e:
+            self.log_message(f"解析时间失败: {e}", "ERROR")
+            self.last_delivery_time = None
+    
+    def is_delivery_allowed(self):
+        """检查是否允许执行发货任务"""
+        if not self.delivery_time_enabled:
+            return True
+        
+        if self.last_delivery_time is None:
+            self.update_last_delivery_time()
+        
+        current_time = datetime.now()
+        allowed = current_time <= self.last_delivery_time
+        
+        if not allowed:
+            self.log_message(f"当前时间 {current_time.strftime('%Y-%m-%d %H:%M:%S')} 已超过最后发货时间 {self.last_delivery_time.strftime('%Y-%m-%d %H:%M:%S')},跳过发货任务", "WARNING")
+        
+        return allowed
+    
+    def start_task_scheduler(self):
+        """启动任务调度器"""
+        # 检查是否选择了模拟器
+        if not self.send_var.get():
+            self.log_message("请先选择发货手机模拟器", "ERROR")
+            messagebox.showwarning("警告", "请先选择发货手机模拟器")
+            return
+        
+        if not self.receive_var.get():
+            self.log_message("请先选择收货手机模拟器", "ERROR")
+            messagebox.showwarning("警告", "请先选择收货手机模拟器")
+            return
+        
+        if self.is_running:
+            self.log_message("任务调度器已在运行中", "WARNING")
+            return
+        
+        # 检查MuMuManager路径
+        if not self.mumu_manager_path or not os.path.exists(self.mumu_manager_path):
+            self.log_message("请先设置正确的 MuMuManager.exe 路径", "ERROR")
+            messagebox.showwarning("警告", "请先设置 MuMuManager.exe 路径")
+            return
+        
+        # 如果启用了时间限制,更新时间
+        if self.delivery_time_enabled:
+            self.update_last_delivery_time()
+            if self.last_delivery_time and self.last_delivery_time <= datetime.now():
+                result = messagebox.askyesno(
+                    "确认", 
+                    f"最后发货时间 {self.last_delivery_time} 已经过去,将不会执行任何发货任务。是否继续?"
+                )
+                if not result:
+                    return
+        
+        self.is_running = True
+        self.stop_event.clear()
+        
+        # 启动任务线程
+        self.task_thread = threading.Thread(target=self.task_scheduler_loop, daemon=True)
+        self.task_thread.start()
+        
+        # 更新UI
+        self.start_btn.config(state="disabled")
+        self.stop_btn.config(state="normal")
+        self.status_label.config(text="状态: ● 运行中", foreground="green")
+        
+        time_info = f"最后发货时间: {self.last_delivery_time}" if self.delivery_time_enabled else "无时间限制"
+        self.log_message(f"任务调度器已启动,每10秒检测一次任务。{time_info}", "INFO")
+    
+    def stop_task_scheduler(self):
+        """停止任务调度器"""
+        if not self.is_running:
+            return
+        
+        self.is_running = False
+        self.stop_event.set()
+        
+        # 等待线程结束
+        if self.task_thread and self.task_thread.is_alive():
+            self.task_thread.join(timeout=3)
+        
+        # 更新UI
+        self.start_btn.config(state="normal")
+        self.stop_btn.config(state="disabled")
+        self.status_label.config(text="状态: ● 已停止", foreground="red")
+        self.log_message("任务调度器已停止", "INFO")
+    
+    def task_scheduler_loop(self):
+        """任务调度循环"""
+        while self.is_running and not self.stop_event.is_set():
+            try:
+                self.log_message("开始检测任务...", "INFO")
+                
+                # 检查发货任务(根据时间限制决定)
+                delivery_allowed = self.is_delivery_allowed()
+                
+                if delivery_allowed:
+                    # 如果允许发货,先检查发货任务
+                    has_task = self.check_and_execute_task("https://task.port.run/acquire/bdxkjzc", "发货")
+                    
+                    # 如果没有发货任务,检查收货任务
+                    if not has_task:
+                        has_task = self.check_and_execute_task("https://task.port.run/acquire/bdxkjxc", "收货")
+                else:
+                    # 不允许发货,只检查收货任务
+                    self.log_message("已超过最后发货时间,仅处理收货任务", "INFO")
+                    has_task = self.check_and_execute_task("https://task.port.run/acquire/bdxkjxc", "收货")
+                
+                if not has_task:
+                    self.log_message("暂无可用任务,10秒后继续检测", "INFO")
+                
+                # 等待10秒后继续检测
+                time.sleep(3)
+                    
+            except Exception as e:
+                self.log_message(f"任务调度循环出错: {e}", "ERROR")
+                time.sleep(5)
+    
+    def check_and_execute_task(self, url, task_type):
+        """
+        检查并执行任务
+        Returns:
+            bool: 是否执行了任务
+        """
+        try:
+            # 发送请求获取任务
+            response = requests.get(url, timeout=10)
+            response.raise_for_status()
+            
+            repData = response.json()
+            
+            # 检查是否有任务
+            if "task" in repData and "content" in repData['task']:
+                content = repData['task']["content"]
+                self.log_message(f"获取到{task_type}任务: {content.get('tableData', ['N/A'])[2] if 'tableData' in content else '未知'}", "INFO")
+                
+                # 执行任务
+                if task_type == "发货":
+                    send_selection = self.send_var.get()
+                    emu_index = self.get_emulator_index(send_selection)
+                    if emu_index:
+                        self.perform_click(emu_index, send_selection.split(" (")[0], content)
+                        return True
+                    else:
+                        self.log_message(f"无法获取发货模拟器索引", "ERROR")
+                        return False
+                else:  # 收货任务
+                    receive_selection = self.receive_var.get()
+                    emu_index = self.get_emulator_index(receive_selection)
+                    if emu_index:
+                        self.perform_click2(emu_index, receive_selection.split(" (")[0], content)
+                        return True
+                    else:
+                        self.log_message(f"无法获取收货模拟器索引", "ERROR")
+                        return False
+            else:
+                self.log_message(f"暂无{task_type}任务", "INFO")
+                return False
+                
+        except requests.RequestException as e:
+            self.log_message(f"请求{task_type}任务失败: {e}", "ERROR")
+            return False
+        except Exception as e:
+            self.log_message(f"处理{task_type}任务时出错: {e}", "ERROR")
+            return False
+    
     def browse_mumu_manager(self):
         """浏览选择MuMuManager.exe"""
         file_path = filedialog.askopenfilename(
@@ -380,7 +711,11 @@ class SimpleMuMuManager:
     
     def save_config(self):
         """保存配置"""
-        config = {'mumu_manager_path': self.mumu_manager_path}
+        config = {
+            'mumu_manager_path': self.mumu_manager_path,
+            'delivery_time_enabled': self.delivery_time_enabled,
+            'last_delivery_time': self.last_delivery_time.strftime("%Y-%m-%d %H:%M:%S") if self.last_delivery_time else None
+        }
         try:
             with open(self.config_file, 'w', encoding='utf-8') as f:
                 json.dump(config, f, indent=4)
@@ -400,6 +735,29 @@ class SimpleMuMuManager:
                         self.log_message(f"已加载配置: {path}")
                     elif path:
                         self.log_message(f"配置路径不存在: {path}", "WARNING")
+                    
+                    # 加载时间限制配置
+                    enabled = config.get('delivery_time_enabled', False)
+                    self.time_enabled_var.set(enabled)
+                    self.delivery_time_enabled = enabled
+                    
+                    last_time_str = config.get('last_delivery_time')
+                    if last_time_str and enabled:
+                        try:
+                            self.last_delivery_time = datetime.strptime(last_time_str, "%Y-%m-%d %H:%M:%S")
+                            # 设置日期选择器
+                            date_str = self.last_delivery_time.strftime("%Y-%m-%d")
+                            self.date_picker.set_date(date_str)
+                            self.hour_var.set(f"{self.last_delivery_time.hour:02d}")
+                            self.minute_var.set(f"{self.last_delivery_time.minute:02d}")
+                            self.on_time_enable_changed()
+                            self.log_message(f"已加载最后发货时间设置: {last_time_str}", "INFO")
+                        except Exception as e:
+                            self.log_message(f"加载最后发货时间失败: {e}", "WARNING")
+                    
+                    # 根据配置更新UI状态
+                    self.on_time_enable_changed()
+                    
             except Exception as e:
                 self.log_message(f"加载配置失败: {e}", "ERROR")
     
@@ -407,9 +765,14 @@ class SimpleMuMuManager:
         """添加日志"""
         timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
         log_entry = f"[{timestamp}] [{level}] {message}\n"
+        
+        # 在主线程中更新UI
+        self.root.after(0, lambda: self._append_log(log_entry))
+    
+    def _append_log(self, log_entry):
+        """实际添加日志到文本框"""
         self.log_text.insert(tk.END, log_entry)
         self.log_text.see(tk.END)
-        self.root.update()
     
     def refresh_emulators(self):
         """刷新模拟器列表"""
@@ -457,86 +820,10 @@ class SimpleMuMuManager:
             return match.group(1)
         return None
     
-    def test_click(self):
-        url = "https://task.port.run/acquire/bdxkjzc"
-
-        payload = ""
-        headers = {
-            "Content-Type": "application/json",
-            "Accept": "*/*",
-            "Accept-Encoding": "gzip, deflate, br",
-            "User-Agent": "PostmanRuntime-ApipostRuntime/1.1.0",
-            "Connection": "keep-alive"
-        }
-
-        response = requests.request("GET", url, data=payload, headers=headers)
-
-        print(response.text)
-        repData = json.loads(response.text)
-        if ("task" in repData and "content" in repData['task']):
-            """测试点击"""
-            send_selection = self.send_var.get()
-            
-            if not send_selection:
-                self.log_message("请选择发货手机模拟器", "ERROR")
-                messagebox.showwarning("警告", "请选择发货手机模拟器")
-                return
-
-            emu_index = self.get_emulator_index(send_selection)
-            if not emu_index:
-                self.log_message("无法获取模拟器索引", "ERROR")
-                return
-            
-            # 在新线程中执行点击
-            thread = threading.Thread(target=self.perform_click, args=(emu_index, send_selection.split(" (")[0], repData['task']["content"]))
-            thread.daemon = True
-            thread.start()
-        else:
-            self.log_message("未获取到有效任务数据", "ERROR")
-            messagebox.showerror("错误", "未获取到有效任务数据")
-    
-    def test_click2(self):
-        url = "https://task.port.run/acquire/bdxkjxc"
-
-        payload = ""
-        headers = {
-            "Content-Type": "application/json",
-            "Accept": "*/*",
-            "Accept-Encoding": "gzip, deflate, br",
-            "User-Agent": "PostmanRuntime-ApipostRuntime/1.1.0",
-            "Connection": "keep-alive"
-        }
-
-        response = requests.request("GET", url, data=payload, headers=headers)
-
-        print(response.text)
-        repData = json.loads(response.text)
-        if ("task" in repData and "content" in repData['task']):
-            """测试点击"""
-            receive_selection = self.receive_var.get()
-            
-            if not receive_selection:
-                self.log_message("请选择收货手机模拟器", "ERROR")
-                messagebox.showwarning("警告", "请选择收货手机模拟器")
-                return
-
-            emu_index = self.get_emulator_index(receive_selection)
-            if not emu_index:
-                self.log_message("无法获取模拟器索引", "ERROR")
-                return
-            
-            # 在新线程中执行点击
-            thread = threading.Thread(target=self.perform_click2, args=(emu_index, receive_selection.split(" (")[0], repData['task']["content"]))
-            thread.daemon = True
-            thread.start()
-        else:
-            self.log_message("未获取到有效任务数据", "ERROR")
-            messagebox.showerror("错误", "未获取到有效任务数据")
-
     def perform_click(self, emu_index, window_title, content):
         """执行点击操作"""
         x, y = 508, 252
-        self.log_message(f"开始在模拟器 {emu_index} 上点击位置 ({x}, {y})")
+        self.log_message(f"开始在模拟器 {emu_index} 上执行发货任务")
         
         try:
             manager = MuMuEmulatorManager(self.mumu_manager_path)
@@ -552,28 +839,29 @@ class SimpleMuMuManager:
                 self.log_message(f"✅ 点击成功!模拟器 {emu_index} 位置 ({x}, {y})")
                 rectX, rectY = activate_window_and_get_position(window_title)
                 time.sleep(2)  # 短暂等待窗口激活
-                click_screen(rectX + 339, rectY + 576)
+                click_screen(rectX + 241, rectY + 411)
                 time.sleep(1)
-                showImageBox = show_image_at_position("C:\\Users\\mail\\Downloads\\chepai\\" + content["tableData"][2] + ".png", rectX + 273, rectY + 549 )
+                showImageBox = show_image_at_position(".\\chepai\\" + content["tableData"][2] + ".png", rectX + 144, rectY + 355)
                 # 开始检测是否扫描
                 color1 = get_emulator_pixel_color(self.mumu_manager_path, emu_index, 523, 1114)
-                while color1 != "#007AFF":  # 假设白色表示未扫描
+                while color1 != "#007AFF" and self.is_running:
                     self.log_message(f"当前颜色: {color1}, 等待扫描完成...")
                     time.sleep(1)
                     color1 = get_emulator_pixel_color(self.mumu_manager_path, emu_index, 523, 1114)
                 # 关闭窗口
                 showImageBox.destroy()
                 self.log_message(f"关闭二维码窗口!")
-                click_screen(rectX + 323, rectY + 718)
+                manager.tap(emu_index, 323, 718, self.log_message)
                 time.sleep(1)
                 drag_in_emulator(self.mumu_manager_path, emu_index, 430, 1046, 980, 400)
                 time.sleep(0.5)
-                click_screen(rectX + 650, rectY + 743)
+                manager.tap(emu_index, 650, 743, self.log_message)
                 time.sleep(1)
-                click_screen(rectX + 358, rectY + 1112)
+                manager.tap(emu_index, 358, 1112, self.log_message)
                 time.sleep(1)
-                click_screen(rectX + 419, rectY + 760)
-                
+                manager.tap(emu_index, 419, 765, self.log_message)
+                self.log_message(f"✅ 发货任务执行完成")
+                sendTask(content["tableData"][2])
             else:
                 self.log_message(f"❌ 点击失败", "ERROR")
                 
@@ -583,7 +871,7 @@ class SimpleMuMuManager:
     def perform_click2(self, emu_index, window_title, content):
         """执行点击操作"""
         x, y = 508, 252
-        self.log_message(f"开始在模拟器 {emu_index} 上点击位置 ({x}, {y})")
+        self.log_message(f"开始在模拟器 {emu_index} 上执行收货任务")
         
         try:
             manager = MuMuEmulatorManager(self.mumu_manager_path)
@@ -599,12 +887,12 @@ class SimpleMuMuManager:
                 self.log_message(f"✅ 点击成功!模拟器 {emu_index} 位置 ({x}, {y})")
                 rectX, rectY = activate_window_and_get_position(window_title)
                 time.sleep(2)  # 短暂等待窗口激活
-                click_screen(rectX + 339, rectY + 576)
+                click_screen(rectX + 241, rectY + 411)
                 time.sleep(1)
-                showImageBox = show_image_at_position("C:\\Users\\mail\\Downloads\\chepai\\" + content["tableData"][2] + ".png", rectX + 273, rectY + 549 )
+                showImageBox = show_image_at_position(".\\chepai\\" + content["tableData"][2] + ".png", rectX + 144, rectY + 355)
                 # 开始检测是否扫描
                 color1 = get_emulator_pixel_color(self.mumu_manager_path, emu_index, 530, 1200)
-                while color1 != "#007AFF":  # 假设白色表示未扫描
+                while color1 != "#007AFF" and self.is_running:
                     self.log_message(f"当前颜色: {color1}, 等待扫描完成...")
                     time.sleep(1)
                     color1 = get_emulator_pixel_color(self.mumu_manager_path, emu_index, 530, 1200)
@@ -617,18 +905,19 @@ class SimpleMuMuManager:
                 time.sleep(1)
                 manager.tap(emu_index, 347, 918, self.log_message)
                 time.sleep(1)
-                input_text_to_emulator(self.mumu_manager_path, emu_index, random.randint(35, 36))
-                time.sleep(1)
+                input_text_to_emulator(self.mumu_manager_path, emu_index, split_and_random_select(content["tdds"]))
                 time.sleep(1)
                 manager.tap(emu_index, 530, 1200, self.log_message)
                 time.sleep(1)
                 manager.tap(emu_index, 419, 765, self.log_message)
-
+                self.log_message(f"✅ 收货任务执行完成")
+                sendTask(content["tableData"][2])
             else:
                 self.log_message(f"❌ 点击失败", "ERROR")
                 
         except Exception as e:
             self.log_message(f"❌ 执行出错: {e}", "ERROR")
+
 def main():
     root = tk.Tk()
     app = SimpleMuMuManager(root)