From caa563e7708e7e314a0031010f523c244941c9e0 Mon Sep 17 00:00:00 2001 From: KenanZhu <3471685733@qq.com> Date: Tue, 26 May 2026 20:52:52 +0800 Subject: [PATCH] =?UTF-8?q?refactor(pages):=20=E7=BB=9F=E4=B8=80=E5=91=BD?= =?UTF-8?q?=E5=90=8D=E8=A7=84=E8=8C=83=E5=B9=B6=E4=BF=AE=E5=A4=8D=20SeatMa?= =?UTF-8?q?pOverlay=20=E5=85=83=E7=B4=A0=E7=AD=89=E5=BE=85=E7=9B=AE?= =?UTF-8?q?=E6=A0=87=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - AutoLibPages → AutoLib(移除实现细节后缀) - ReserveValidator → ReserveChecker(与 RecordChecker 命名一致) - CaptchaHandler → CaptchaSolver(语义更准确,职责是"求解"验证码) - ReserveChecker.validate() → check()(与 RecordChecker 风格统一) - 修复 SeatMapOverlay.selectSeat() 中 _waitClickable 等待页面全局 而非具体 seat_link 元素的时序缺陷 - ALMainWorkers 切换为 pages.AutoLib 新版实现 Co-Authored-By: Claude Opus 4.7 --- src/gui/ALMainWorkers.py | 2 +- src/pages/{AutoLibPages.py => AutoLib.py} | 18 +++++++++--------- src/pages/__init__.py | 4 ++-- src/pages/components/SeatMapOverlay.py | 10 ++++++++-- src/pages/flows/CheckinFlow.py | 3 --- src/pages/flows/RenewFlow.py | 8 -------- src/pages/flows/_helpers.py | 2 -- .../{CaptchaHandler.py => CaptchaSolver.py} | 2 +- .../{ReserveValidator.py => ReserveChecker.py} | 7 ++----- src/pages/services/__init__.py | 8 ++++---- 10 files changed, 27 insertions(+), 37 deletions(-) rename src/pages/{AutoLibPages.py => AutoLib.py} (96%) rename src/pages/services/{CaptchaHandler.py => CaptchaSolver.py} (99%) rename src/pages/services/{ReserveValidator.py => ReserveChecker.py} (99%) diff --git a/src/gui/ALMainWorkers.py b/src/gui/ALMainWorkers.py index 9f684fa..66d5319 100644 --- a/src/gui/ALMainWorkers.py +++ b/src/gui/ALMainWorkers.py @@ -16,7 +16,7 @@ from PySide6.QtCore import ( ) from base.MsgBase import MsgBase -from operators.AutoLib import AutoLib +from pages.AutoLib import AutoLib from utils.JSONReader import JSONReader from autoscript import createEngine diff --git a/src/pages/AutoLibPages.py b/src/pages/AutoLib.py similarity index 96% rename from src/pages/AutoLibPages.py rename to src/pages/AutoLib.py index 3c73ff0..bfac01a 100644 --- a/src/pages/AutoLibPages.py +++ b/src/pages/AutoLib.py @@ -24,12 +24,12 @@ from pages.MainShell import MainShell from pages.flows.ReserveFlow import ReserveFlow, ReserveContext from pages.flows.CheckinFlow import CheckinFlow from pages.flows.RenewFlow import RenewFlow -from pages.services.CaptchaHandler import CaptchaHandler -from pages.services.ReserveValidator import ReserveValidator +from pages.services.CaptchaSolver import CaptchaSolver +from pages.services.ReserveChecker import ReserveChecker from pages.services.RecordChecker import RecordChecker -class AutoLibPages(MsgBase): +class AutoLib(MsgBase): def __init__( self, @@ -46,9 +46,9 @@ class AutoLibPages(MsgBase): self.__driver_path: str = "" self.__login_page: LoginPage = None self.__shell: MainShell = None - self.__captcha_handler: CaptchaHandler = None + self.__captcha_solver: CaptchaSolver = None self.__record_checker: RecordChecker = None - self.__reserve_validator: ReserveValidator = None + self.__reserve_checker: ReserveChecker = None self.__reserve_flow: ReserveFlow = None self.__checkin_flow: CheckinFlow = None self.__renew_flow: RenewFlow = None @@ -189,7 +189,7 @@ class AutoLibPages(MsgBase): self._showTrace("浏览器驱动未初始化, 请先初始化浏览器驱动 !", self.TraceLevel.WARNING) return self.__shell = MainShell(self.__driver) - self.__captcha_handler = CaptchaHandler( + self.__captcha_solver = CaptchaSolver( input_queue=self._input_queue, output_queue=self._output_queue, ) @@ -197,7 +197,7 @@ class AutoLibPages(MsgBase): input_queue=self._input_queue, output_queue=self._output_queue, ) - self.__reserve_validator = ReserveValidator( + self.__reserve_checker = ReserveChecker( input_queue=self._input_queue, output_queue=self._output_queue, ) @@ -242,7 +242,7 @@ class AutoLibPages(MsgBase): if not self.__login_page.login( username, password, - captcha_solver=self.__captcha_handler.solveCaptcha, + captcha_solver=self.__captcha_solver.solveCaptcha, auto_captcha=auto_captcha, max_attempts=login_config.get("max_attempt", 3), ): @@ -256,7 +256,7 @@ class AutoLibPages(MsgBase): # reserve if run_mode["auto_reserve"]: if self.__record_checker.canReserve(self.__shell, reserve_info.get("date")): - if self.__reserve_validator.validate(reserve_info): + if self.__reserve_checker.check(reserve_info): ctx = ReserveContext( username=username, date=reserve_info["date"], diff --git a/src/pages/__init__.py b/src/pages/__init__.py index 391cc81..e8ad247 100644 --- a/src/pages/__init__.py +++ b/src/pages/__init__.py @@ -7,7 +7,7 @@ This software is provided "as is", without any warranty of any kind. You may use, modify, and distribute this file under the terms of the MIT License. See the LICENSE file for details. """ -from pages.AutoLibPages import AutoLibPages +from pages.AutoLib import AutoLib from pages.LoginPage import LoginPage from pages.MainShell import MainShell from pages.ReserveView import ReserveView @@ -19,7 +19,7 @@ from pages.components.CheckinResultDialog import CheckinResultDialog from pages.components.RenewDialog import RenewDialog __all__ = [ - "AutoLibPages", + "AutoLib", "LoginPage", "MainShell", "ReserveView", diff --git a/src/pages/components/SeatMapOverlay.py b/src/pages/components/SeatMapOverlay.py index 42f1137..1512a36 100644 --- a/src/pages/components/SeatMapOverlay.py +++ b/src/pages/components/SeatMapOverlay.py @@ -15,6 +15,8 @@ from selenium.common.exceptions import ( ) from selenium.webdriver.common.by import By from selenium.webdriver.remote.webdriver import WebDriver +from selenium.webdriver.support.ui import WebDriverWait +from selenium.webdriver.support import expected_conditions as EC from pages.components.Overlay import Overlay @@ -48,7 +50,9 @@ class SeatMapOverlay(Overlay): try: seat_el = self._find(By.ID, f"seat_{int(seat_id):03d}") seat_link = seat_el.find_element(By.TAG_NAME, "a") - self._waitClickable((By.TAG_NAME, "a")) + WebDriverWait(self._driver, 2).until( + EC.element_to_be_clickable(seat_link) + ) seat_link.click() return seat_link.get_attribute("title") except (NoSuchElementException, ValueError, TimeoutException, @@ -63,7 +67,9 @@ class SeatMapOverlay(Overlay): if not seat_id_upper == seat.text.lstrip('0'): continue seat_link = seat.find_element(By.TAG_NAME, "a") - self._waitClickable((By.TAG_NAME, "a")) + WebDriverWait(self._driver, 2).until( + EC.element_to_be_clickable(seat_link) + ) seat_link.click() return seat_link.get_attribute("title") return None diff --git a/src/pages/flows/CheckinFlow.py b/src/pages/flows/CheckinFlow.py index fc0a7c6..bb0689e 100644 --- a/src/pages/flows/CheckinFlow.py +++ b/src/pages/flows/CheckinFlow.py @@ -42,16 +42,13 @@ class CheckinFlow(MsgBase): if not self._shell.waitCheckinButton(): self._showTrace(f"用户 {username} 签到界面加载失败 !", self.TraceLevel.ERROR) return False - if self._shell.isCheckinButtonDisabled(): self._showTrace("签到按钮不可用, 可能不在场馆内, 正在尝试启用......") if not self._shell.enableCheckinButtonByJS(): self._showTrace(f"签到按钮启用失败 !", self.TraceLevel.ERROR) return False self._showTrace("签到按钮已启用") - self._shell.clickCheckinButton() - try: with CheckinResultDialog(self._driver) as dialog: result_msg = dialog.getResultMessage() diff --git a/src/pages/flows/RenewFlow.py b/src/pages/flows/RenewFlow.py index abde378..4d4a36a 100644 --- a/src/pages/flows/RenewFlow.py +++ b/src/pages/flows/RenewFlow.py @@ -53,23 +53,18 @@ class RenewFlow(MsgBase): prefer_earlier = renew_info["prefer_early"] end_time = record["time"]["end"] target_renew_mins = timeStrToMins(end_time) + renew_info["expect_duration"] * 60 - if not self._validateRenewTime(end_time, target_renew_mins): return False - if not self._shell.waitExtendButton(): self._showTrace(f"用户 {username} 续约界面加载失败 !", self.TraceLevel.ERROR) return False - if self._shell.isExtendButtonDisabled(): self._showTrace( f"用户 {username} 续约按钮不可用, 可能不在场馆内, " f"请连接图书馆网络后重试" ) return False - self._shell.clickExtendButton() - try: with RenewDialog(self._driver) as dialog: if not dialog.waitUntilReady(): @@ -82,14 +77,12 @@ class RenewFlow(MsgBase): self._shell.refresh() self._showTrace(f"用户 {username} 续约失败 !", self.TraceLevel.ERROR) return False - renew_ok_btn = dialog.getOkButton() renew_time_opts = dialog.getTimeOptions() if not renew_time_opts: self._showTrace("当前未查询到可用续约时间 !", self.TraceLevel.WARNING) self._shell.refresh() return False - best_opt, best_text, actual_diff, free_times = findBestTimeOption( renew_time_opts, target_renew_mins, max_diff, prefer_earlier, is_reserve=False, @@ -111,7 +104,6 @@ class RenewFlow(MsgBase): renew_ok_btn.click() self._shell.refresh() return True - self._showTrace( "无法选择最近的可用续约时间 ! " f"所有可选时间与目标时间相差都超过了 {max_diff} 分钟 !", diff --git a/src/pages/flows/_helpers.py b/src/pages/flows/_helpers.py index 1b2fcf5..488615a 100644 --- a/src/pages/flows/_helpers.py +++ b/src/pages/flows/_helpers.py @@ -68,7 +68,6 @@ def findBestTimeOption( ) actual_diff = time_val - target_time abs_diff = abs(actual_diff) - if abs_diff < best_time_diff or ( abs_diff == best_time_diff and ( @@ -79,7 +78,6 @@ def findBestTimeOption( best_time_diff = abs_diff best_actual_diff = actual_diff best_time_opt = time_opt - if best_time_opt is not None: return (best_time_opt, best_time_opt.text.strip(), best_actual_diff, free_times) return (None, None, None, free_times) diff --git a/src/pages/services/CaptchaHandler.py b/src/pages/services/CaptchaSolver.py similarity index 99% rename from src/pages/services/CaptchaHandler.py rename to src/pages/services/CaptchaSolver.py index 1e941bd..1544bee 100644 --- a/src/pages/services/CaptchaHandler.py +++ b/src/pages/services/CaptchaSolver.py @@ -20,7 +20,7 @@ from base.MsgBase import MsgBase from pages.LoginPage import LoginPage -class CaptchaHandler(MsgBase): +class CaptchaSolver(MsgBase): def __init__( self, diff --git a/src/pages/services/ReserveValidator.py b/src/pages/services/ReserveChecker.py similarity index 99% rename from src/pages/services/ReserveValidator.py rename to src/pages/services/ReserveChecker.py index c458dc0..f38f449 100644 --- a/src/pages/services/ReserveValidator.py +++ b/src/pages/services/ReserveChecker.py @@ -15,7 +15,7 @@ from pages.ReserveView import ReserveView from pages.flows._helpers import timeStrToMins, minsToTimeStr -class ReserveValidator(MsgBase): +class ReserveChecker(MsgBase): def __init__( self, @@ -150,7 +150,6 @@ class ReserveValidator(MsgBase): end_time = reserve_info["end_time"] begin_mins = timeStrToMins(begin_time["time"]) end_mins = timeStrToMins(end_time["time"]) - if end_mins < begin_mins and reserve_info["satisfy_duration"] is False: self._showTrace( f"结束时间 {end_time['time']} 早于开始时间 {begin_time['time']}, " @@ -161,7 +160,6 @@ class ReserveValidator(MsgBase): begin_time, end_time = end_time, begin_time begin_mins = timeStrToMins(begin_time["time"]) end_mins = timeStrToMins(end_time["time"]) - max_end_mins = timeStrToMins("23:30") if end_mins > max_end_mins: self._showTrace( @@ -170,7 +168,6 @@ class ReserveValidator(MsgBase): ) reserve_info["end_time"]["time"] = "23:30" end_mins = max_end_mins - if reserve_info["satisfy_duration"]: if reserve_info["expect_duration"] > 8: self._showTrace( @@ -191,7 +188,7 @@ class ReserveValidator(MsgBase): reserve_info["end_time"]["time"] = minsToTimeStr(begin_mins + 8 * 60) return True - def validate( + def check( self, reserve_info: dict, ) -> bool: diff --git a/src/pages/services/__init__.py b/src/pages/services/__init__.py index 8545fc0..cee361c 100644 --- a/src/pages/services/__init__.py +++ b/src/pages/services/__init__.py @@ -7,12 +7,12 @@ This software is provided "as is", without any warranty of any kind. You may use, modify, and distribute this file under the terms of the MIT License. See the LICENSE file for details. """ -from pages.services.CaptchaHandler import CaptchaHandler -from pages.services.ReserveValidator import ReserveValidator +from pages.services.CaptchaSolver import CaptchaSolver +from pages.services.ReserveChecker import ReserveChecker from pages.services.RecordChecker import RecordChecker __all__ = [ - "CaptchaHandler", - "ReserveValidator", + "CaptchaSolver", + "ReserveChecker", "RecordChecker", ]