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

refactor: 时间选择逻辑下沉至 Dialog、Worker 模板方法抽象、配置访问安全化与代码风格统一

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-27 19:54:26 +08:00
parent 345cb95b98
commit e77c561685
18 changed files with 599 additions and 465 deletions
+16 -21
View File
@@ -20,7 +20,7 @@ from base.MsgBase import MsgBase
from pages.MainShell import MainShell
from pages.components.RenewDialog import RenewDialog
from pages.flows._helpers import timeStrToMins, minsToTimeStr
from pages.strategies.timeSelectMaker import TimeSelectMaker
from pages.strategies.TimeSelectMaker import TimeSelectMaker
class RenewFlow(MsgBase):
@@ -46,10 +46,10 @@ class RenewFlow(MsgBase):
renew_info: dict,
) -> bool:
max_diff = renew_info["max_diff"]
prefer_earlier = renew_info["prefer_early"]
max_diff = renew_info.get("max_diff", 30)
prefer_earlier = renew_info.get("prefer_early", True)
end_time = record["time"]["end"]
target_renew_mins = timeStrToMins(end_time) + renew_info["expect_duration"] * 60
target_renew_mins = timeStrToMins(end_time) + renew_info.get("expect_duration", 2) * 60
if not self._validateRenewTime(end_time, target_renew_mins):
return False
if not self._shell.waitExtendButton():
@@ -74,20 +74,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
result = TimeSelectMaker.forRenew().decide(
renew_time_opts,
result = dialog.selectBestTime(
target_renew_mins,
max_diff,
prefer_earlier
prefer_earlier,
)
if result.selected_index >= 0:
renew_time_opts[result.selected_index].click()
abs_diff = abs(result.actual_diff)
if result.actual_diff < 0:
relation = f"早了 {abs_diff} 分钟"
@@ -100,15 +92,18 @@ class RenewFlow(MsgBase):
f"与期望续约时间相比 {relation}"
)
record["time"]["end"] = result.display_text.strip()
renew_ok_btn.click()
dialog.clickOk()
self._shell.refresh()
return True
self._showTrace(
"无法选择最近的可用续约时间 ! "
f"所有可选时间与目标时间相差都超过了 {max_diff} 分钟 !",
self.TraceLevel.WARNING,
)
self._showTrace(f"当前可供续约的时间有: {result.free_times}")
if not result.free_times:
self._showTrace("当前未查询到可用续约时间 !", self.TraceLevel.WARNING)
else:
self._showTrace(
"无法选择最近的可用续约时间 ! "
f"所有可选时间与目标时间相差都超过了 {max_diff} 分钟 !",
self.TraceLevel.WARNING,
)
self._showTrace(f"当前可供续约的时间有: {result.free_times}")
self._shell.refresh()
return False
except (NoSuchElementException, TimeoutException) as e:
+39 -150
View File
@@ -9,7 +9,6 @@ See the LICENSE file for details.
"""
import queue
from dataclasses import dataclass
from typing import Optional
from selenium.common.exceptions import (
ElementNotInteractableException,
@@ -20,8 +19,7 @@ from selenium.webdriver.remote.webdriver import WebDriver
from base.MsgBase import MsgBase
from pages.MainShell import MainShell
from pages.flows._helpers import timeStrToMins, minsToTimeStr
from pages.strategies.timeSelectMaker import TimeSelectMaker
from pages.strategies.TimeSelectMaker import TimeSelectMaker
from pages.ReserveView import ReserveView
from pages.components.ReserveResultDialog import ReserveResultDialog
from pages.components.TimeSelectDialog import TimeSelectDialog
@@ -60,14 +58,12 @@ class ReserveFlow(MsgBase):
super().__init__(input_queue, output_queue)
self._driver: WebDriver = driver
self._shell: MainShell = shell
self._ctx: Optional[ReserveContext] = None
def execute(
self,
ctx: ReserveContext,
) -> bool:
self._ctx = ctx
submit_reserve = False
reserve_success = False
have_hover_on_page = False
@@ -93,13 +89,13 @@ class ReserveFlow(MsgBase):
self._showTrace(f"选择楼层失败 ! : {display_floor} 不可用", self.TraceLevel.ERROR)
return False
self._showTrace(f"楼层 {ReserveView.FLOOR_MAP.get(ctx.floor)} 选择成功 !")
if not view.selectRoom(ctx.room):
seat_map = view.selectRoom(ctx.room)
if seat_map is None:
display_room = ReserveView.ROOM_MAP.get(ctx.room, ctx.room)
self._showTrace(f"选择房间失败 ! : {display_room} 不可用", self.TraceLevel.ERROR)
return False
self._showTrace(f"房间 {ReserveView.ROOM_MAP.get(ctx.room)} 选择成功 !")
have_hover_on_page = True
seat_map = view.openSeatMap()
seat_status = seat_map.selectSeat(ctx.seat_id)
if seat_status is None:
self._showTrace(
@@ -108,41 +104,44 @@ class ReserveFlow(MsgBase):
)
else:
self._showTrace(f"座位 {ctx.seat_id} 选择成功 ! : 当前状态 - '{seat_status}'")
time_dialog = TimeSelectDialog(self._driver)
select_time_ok = self._selectSeatTime(time_dialog)
if not select_time_ok:
self._showTrace("选择时间失败 !", self.TraceLevel.ERROR)
try:
time_dialog = TimeSelectDialog(self._driver, tracer=self._showTrace)
except TimeoutException:
self._showTrace("时间选择面板未出现 !", self.TraceLevel.ERROR)
else:
try:
view.submitReserve()
submit_reserve = True
with ReserveResultDialog(self._driver) as result:
if result.isFailure():
self._showTrace("预约失败", self.TraceLevel.ERROR)
elif result.isSuccess():
details = result.getDetailTexts()
if len(details) >= 6:
self._showTrace(
f"\n"
f" 预约成功 !\n"
f" {details[1]}\n"
f" {details[2]}\n"
f" {details[3]}\n"
f" 签到时间 {details[5]}"
)
if not time_dialog.selectSeatTime(ctx):
self._showTrace("选择时间失败 !", self.TraceLevel.ERROR)
else:
try:
view.submitReserve()
submit_reserve = True
with ReserveResultDialog(self._driver) as result:
if result.isFailure():
self._showTrace("预约失败", self.TraceLevel.ERROR)
elif result.isSuccess():
details = result.getDetailTexts()
if len(details) >= 6:
self._showTrace(
f"\n"
f" 预约成功 !\n"
f" {details[1]}\n"
f" {details[2]}\n"
f" {details[3]}\n"
f" 签到时间 {details[5]}"
)
else:
self._showTrace(
"\n"
" 预约成功 !\n"
" 未找获取到详细信息"
)
reserve_success = True
else:
self._showTrace(
"\n"
" 预约成功 !\n"
" 未找获取到详细信息"
)
reserve_success = True
else:
self._showTrace("预约结果加载失败 !", self.TraceLevel.ERROR)
except (TimeoutException, ElementNotInteractableException):
self._showTrace("预约提交失败 !", self.TraceLevel.ERROR)
except Exception:
self._showTrace("预约提交失败 !", self.TraceLevel.ERROR)
self._showTrace("预约结果加载失败 !", self.TraceLevel.ERROR)
except (TimeoutException, ElementNotInteractableException):
self._showTrace("预约提交失败 !", self.TraceLevel.ERROR)
except Exception:
self._showTrace("预约提交失败 !", self.TraceLevel.ERROR)
if not submit_reserve and have_hover_on_page:
view.refresh()
if reserve_success:
@@ -150,113 +149,3 @@ class ReserveFlow(MsgBase):
else:
self._showTrace(f"用户 {ctx.username} 预约失败 !", self.TraceLevel.ERROR)
return reserve_success
def _selectSeatTime(
self,
time_dialog: TimeSelectDialog,
) -> bool:
ctx = self._ctx
exp_beg_tm_str = ctx.begin_time
exp_end_tm_str = ctx.end_time
exp_beg_mins = timeStrToMins(exp_beg_tm_str)
exp_end_mins = timeStrToMins(exp_end_tm_str)
act_beg_mins = exp_beg_mins
act_beg_tm_str = exp_beg_tm_str
act_end_mins = exp_end_mins
act_end_tm_str = exp_end_tm_str
act_beg_mins = self._selectNearestTime(
time_dialog,
time_id="startTime",
time_type="开始时间",
target_time=exp_beg_mins,
max_time_diff=ctx.begin_max_diff,
prefer_earlier=ctx.begin_prefer_early,
)
if act_beg_mins == -1:
return False
act_beg_tm_str = minsToTimeStr(act_beg_mins)
if ctx.satisfy_duration:
exp_end_mins = self._calcEndTime(act_beg_mins, ctx.expect_duration)
exp_end_tm_str = minsToTimeStr(exp_end_mins)
self._showTrace(
f"需要满足期望预约持续时间: {ctx.expect_duration} 小时, "
f"根据开始时间 {act_beg_tm_str} 计算结束时间: {exp_end_tm_str}"
)
act_end_mins = self._selectNearestTime(
time_dialog,
time_id="endTime",
time_type="结束时间",
target_time=exp_end_mins,
max_time_diff=ctx.end_max_diff,
prefer_earlier=ctx.end_prefer_early,
)
if act_end_mins == -1:
return False
act_end_tm_str = minsToTimeStr(act_end_mins)
self._showTrace(
f"期望预约时间段: {exp_beg_tm_str} - {exp_end_tm_str}, "
f"实际预约时间段: {act_beg_tm_str} - {act_end_tm_str}"
)
return True
def _selectNearestTime(
self,
time_dialog: TimeSelectDialog,
time_id: str,
time_type: str,
target_time: int,
max_time_diff: int,
prefer_earlier: bool,
) -> int:
all_time_opts = time_dialog.getTimeOptions(time_id)
if not all_time_opts:
self._showTrace(
f"{time_type} 选择失败 ! : 当前未查询到可用时间", self.TraceLevel.ERROR
)
return -1
result = TimeSelectMaker.forReserve().decide(
all_time_opts,
target_time,
max_time_diff,
prefer_earlier
)
if result.selected_index >= 0:
all_time_opts[result.selected_index].click()
abs_diff = abs(result.actual_diff)
if result.actual_diff < 0:
relation = f"早了 {abs_diff} 分钟"
elif result.actual_diff > 0:
relation = f"晚了 {abs_diff} 分钟"
else:
relation = f"正好等于 {time_type}"
self._showTrace(
f"选择距离期望 {time_type} 最近的 {result.display_text}, "
f"与期望 {time_type} 相比 {relation}"
)
return target_time + result.actual_diff
target_time_str = minsToTimeStr(target_time)
self._showTrace(
f"无法选择最近的 {time_type} {target_time_str}, "
f"所有可选时间与目标时间相差都超过 {max_time_diff} 分钟",
self.TraceLevel.WARNING,
)
self._showTrace(f"当前可供预约的 {time_type} 有: {result.free_times}")
return -1
def _calcEndTime(
self,
begin_mins: int,
duration: int,
) -> int:
expect_end_mins = int(begin_mins + duration*60)
if expect_end_mins > self.LIBRARY_CLOSE_MINS:
expect_end_mins = self.LIBRARY_CLOSE_MINS
self._showTrace(
f"预约持续时间 {duration} 小时, 超过最大预约时间 23:30, "
f"自动调整为 23:30",
self.TraceLevel.WARNING,
)
return expect_end_mins
+8 -11
View File
@@ -7,16 +7,13 @@ 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.
"""
def timeStrToMins(
time_str: str,
) -> int:
from pages.strategies.TimeSelectMaker import (
minsToTimeStr,
timeStrToMins
)
hour, minute = map(int, time_str.split(":"))
return hour * 60 + minute
def minsToTimeStr(
mins: int,
) -> str:
hour, minute = divmod(int(mins), 60)
return f"{hour:02d}:{minute:02d}"
__all__ = [
"minsToTimeStr",
"timeStrToMins",
]