1
1
mirror of https://github.com/KenanZhu/AutoLibrary.git synced 2026-06-17 23:13:03 +08:00

refactor(pages): 统一命名规范并修复 SeatMapOverlay 元素等待目标错误

- AutoLibPages → AutoLib(移除实现细节后缀)
- ReserveValidator → ReserveChecker(与 RecordChecker 命名一致)
- CaptchaHandler → CaptchaSolver(语义更准确,职责是"求解"验证码)
- ReserveChecker.validate() → check()(与 RecordChecker 风格统一)
- 修复 SeatMapOverlay.selectSeat() 中 _waitClickable 等待页面全局
  <a> 而非具体 seat_link 元素的时序缺陷
- ALMainWorkers 切换为 pages.AutoLib 新版实现

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-26 20:52:52 +08:00
parent 280028259f
commit caa563e770
10 changed files with 27 additions and 37 deletions
+1 -1
View File
@@ -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
@@ -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"],
+2 -2
View File
@@ -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",
+8 -2
View File
@@ -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
-3
View File
@@ -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()
-8
View File
@@ -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} 分钟 !",
-2
View File
@@ -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)
@@ -20,7 +20,7 @@ from base.MsgBase import MsgBase
from pages.LoginPage import LoginPage
class CaptchaHandler(MsgBase):
class CaptchaSolver(MsgBase):
def __init__(
self,
@@ -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:
+4 -4
View File
@@ -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",
]