From 02463f087ede811fad07c5c0af104579f3c30f4d Mon Sep 17 00:00:00 2001 From: KenanZhu <3471685733@qq.com> Date: Wed, 18 Mar 2026 12:46:37 +0800 Subject: [PATCH] =?UTF-8?q?feat(MsgBase,=20gui,=20operators):=20=E5=A2=9E?= =?UTF-8?q?=E5=BC=BA=E6=97=A5=E5=BF=97=E8=BE=93=E5=87=BA=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 为 _showTrace 方法添加 no_log 参数,支持控制日志写入 - 在主窗口各关键操作点添加日志输出 - 优化错误信息输出策略,分离 trace 和 log 输出 - 改进配置目录初始化过程的日志记录 --- src/base/MsgBase.py | 5 +++-- src/gui/ALMainWindow.py | 11 ++++++++-- src/gui/ALMainWorkers.py | 40 +++++++++++++++++++++++++++---------- src/operators/AutoLib.py | 32 +++++++++++++++++++++-------- src/operators/LibChecker.py | 5 +++-- src/operators/LibCheckin.py | 6 +++--- src/operators/LibLogin.py | 12 +++++------ src/operators/LibRenew.py | 5 +++-- src/operators/LibReserve.py | 10 ++++++++-- src/utils/AppInitializer.py | 6 ++++++ 10 files changed, 94 insertions(+), 38 deletions(-) diff --git a/src/base/MsgBase.py b/src/base/MsgBase.py index 1df0494..d52e193 100644 --- a/src/base/MsgBase.py +++ b/src/base/MsgBase.py @@ -70,12 +70,13 @@ class MsgBase: def _showTrace( self, msg: str, - level: int = logging.INFO + level: int = logging.INFO, + no_log: bool = False ): timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3] self._output_queue.put(f"{timestamp}-[{self._class_name:<15}] : {msg}") - if self._logger: + if self._logger and not no_log: self._logger.log(level, msg) diff --git a/src/gui/ALMainWindow.py b/src/gui/ALMainWindow.py index f033382..dd2fbf1 100644 --- a/src/gui/ALMainWindow.py +++ b/src/gui/ALMainWindow.py @@ -59,6 +59,7 @@ class ALMainWindow(MsgBase, QMainWindow, Ui_ALMainWindow): self.connectSignals() self.startMsgPolling() self.startTimerTaskPolling() + self._showLog("主窗口初始化完成") def modifyUi( @@ -186,6 +187,7 @@ class ALMainWindow(MsgBase, QMainWindow, Ui_ALMainWindow): if self.__alConfigWidget: self.__alConfigWidget.close() # the config widget is already deleted in the 'self.onConfigWidgetClosed' + self._showLog("主窗口关闭") QMainWindow.closeEvent(self, event) @@ -300,6 +302,7 @@ class ALMainWindow(MsgBase, QMainWindow, Ui_ALMainWindow): self.__alConfigWidget = None self.__config_paths = ConfigManager.getValidateAutomationConfigPaths() self.setControlButtons(True, None, None) + self._showLog("配置窗口已关闭,配置文件路径已更新") @Slot(dict) def onTimerTaskIsReady( @@ -347,6 +350,7 @@ class ALMainWindow(MsgBase, QMainWindow, Ui_ALMainWindow): self.__alTimerTaskManageWidget.raise_() self.__alTimerTaskManageWidget.activateWindow() self.TimerTaskManageWidgetButton.setEnabled(False) + self._showLog("打开定时任务管理窗口") @Slot() def onConfigButtonClicked( @@ -360,6 +364,7 @@ class ALMainWindow(MsgBase, QMainWindow, Ui_ALMainWindow): self.__alConfigWidget.raise_() self.__alConfigWidget.activateWindow() self.ConfigButton.setEnabled(False) + self._showLog("打开配置窗口") @Slot() def onStartButtonClicked( @@ -376,6 +381,7 @@ class ALMainWindow(MsgBase, QMainWindow, Ui_ALMainWindow): self.__auto_lib_thread.autoLibWorkerIsFinished.connect(self.onStopButtonClicked) self.__auto_lib_thread.autoLibWorkerFinishedWithError.connect(self.onStopButtonClicked) self.__auto_lib_thread.start() + self._showLog("开始手动执行任务") @Slot() def onStopButtonClicked( @@ -383,14 +389,15 @@ class ALMainWindow(MsgBase, QMainWindow, Ui_ALMainWindow): ): if self.__auto_lib_thread: - self._showTrace("正在停止操作......") + self._showTrace("正在停止操作......", no_log=True) self.__auto_lib_thread.wait(2000) - self._showTrace("操作已停止") + self._showTrace("操作已停止", no_log=True) self.__auto_lib_thread.autoLibWorkerIsFinished.disconnect(self.onStopButtonClicked) self.__auto_lib_thread.autoLibWorkerFinishedWithError.disconnect(self.onStopButtonClicked) self.__auto_lib_thread.deleteLater() self.__auto_lib_thread = None self.setControlButtons(None, False, True) + self._showLog("任务已停止") @Slot() def onSendButtonClicked( diff --git a/src/gui/ALMainWorkers.py b/src/gui/ALMainWorkers.py index 4fee821..36e8950 100644 --- a/src/gui/ALMainWorkers.py +++ b/src/gui/ALMainWorkers.py @@ -44,9 +44,11 @@ class AutoLibWorker(MsgBase, QThread): current_time = time.strftime("%H:%M", time.localtime()) if current_time >= "23:30" or current_time <= "07:30": self._showTrace( - "当前时间不在图书馆开放时间内, 请在 07:30 - 23:30 之间尝试" + "当前时间不在图书馆开放时间内, 请在 07:30 - 23:30 之间尝试", + self.TraceLevel.WARNING ) return False + self._showLog(f"时间检查通过, 当前时间: {current_time}", self.TraceLevel.INFO) return True @@ -57,8 +59,12 @@ class AutoLibWorker(MsgBase, QThread): if not all( os.path.exists(path) for path in self.__config_paths.values() ): - self._showTrace("配置文件路径不存在, 请检查配置文件路径是否正确") + self._showTrace( + "配置文件路径不存在, 请检查配置文件路径是否正确", + self.TraceLevel.ERROR + ) return False + self._showLog(f"配置文件路径检查通过, 路径: {self.__config_paths}", self.TraceLevel.INFO) return True @@ -67,22 +73,28 @@ class AutoLibWorker(MsgBase, QThread): ) -> bool: self._showTrace( - f"正在加载配置文件, 运行配置文件路径: {self.__config_paths["run"]}" + f"正在加载配置文件, 运行配置文件路径: {self.__config_paths["run"]}", + no_log=True ) self.__run_config = JSONReader(self.__config_paths["run"]).data() self._showTrace( - f"正在加载配置文件, 用户配置文件路径: {self.__config_paths["user"]}" + f"正在加载配置文件, 用户配置文件路径: {self.__config_paths["user"]}", + no_log=True ) self.__user_config = JSONReader(self.__config_paths["user"]).data() if self.__run_config is None or self.__user_config is None: - self._showTrace("配置文件加载失败, 请检查配置文件是否正确") - self._showTrace("配置文件加载失败, 请检查配置文件是否正确") + self._showTrace( + "配置文件加载失败, 请检查配置文件是否正确", + self.TraceLevel.ERROR + ) return False if not self.__user_config.get("groups"): self._showTrace( - "用户配置文件中无有效任务组, 请检查用户配置文件是否正确" + "用户配置文件中无有效任务组, 请检查用户配置文件是否正确", + self.TraceLevel.WARNING ) return False + self._showLog(f"配置文件加载成功, 任务组数量: {len(self.__user_config.get('groups', []))}", self.TraceLevel.INFO) return True @@ -108,14 +120,17 @@ class AutoLibWorker(MsgBase, QThread): groups = self.__user_config.get("groups") for group in groups: if not group["enabled"]: - self._showTrace(f"任务组 {group["name"]} 已跳过") + self._showTrace(f"任务组 {group["name"]} 已跳过", no_log=True) continue - self._showTrace(f"正在运行任务组 {group["name"]}") + self._showTrace(f"正在运行任务组 {group["name"]}", no_log=True) auto_lib.run( { "users": group.get("users", []) } ) except Exception as e: - self._showTrace(f"AutoLibrary 运行时发生异常 : {e}") + self._showTrace( + f"AutoLibrary 运行时发生异常 : {e}", + self.TraceLevel.ERROR + ) self.autoLibWorkerFinishedWithError.emit() return if auto_lib: @@ -154,7 +169,10 @@ class TimerTaskWorker(AutoLibWorker): self ): - self._showTrace(f"定时任务 {self.__timer_task['name']} 运行时发生异常") + self._showTrace( + f"定时任务 {self.__timer_task['name']} 运行时发生异常", + self.TraceLevel.ERROR + ) self.timerTaskWorkerIsFinished.emit(True, self.__timer_task) @Slot() diff --git a/src/operators/AutoLib.py b/src/operators/AutoLib.py index a1b8ecf..154030d 100644 --- a/src/operators/AutoLib.py +++ b/src/operators/AutoLib.py @@ -54,7 +54,7 @@ class AutoLib(MsgBase): self ) -> bool: - self._showTrace("正在初始化浏览器驱动......") + self._showTrace("正在初始化浏览器驱动......", no_log=True) web_driver_config = self.__run_config.get("web_driver", None) self.__driver_type = web_driver_config.get("driver_type") @@ -126,7 +126,7 @@ class AutoLib(MsgBase): service = ChromeService(executable_path=self.__driver_path) self.__driver = webdriver.Chrome(service=service, options=driver_options) case "firefox": - self._showTrace(f"Firefox 浏览器驱动初始化略慢, 请耐心等待...") + self._showTrace(f"Firefox 浏览器驱动初始化略慢, 请耐心等待...", no_log=True) service = FirefoxService(executable_path=self.__driver_path) self.__driver = webdriver.Firefox(service=service, options=driver_options) case _: # actually will not happen, beacuse we have checked it at the initlization @@ -245,8 +245,10 @@ class AutoLib(MsgBase): else: self._showTrace(f"用户 {username} 无法预约,已跳过") result = 2 + # checkin - if run_mode["auto_checkin"] and result != 1: + last_result = result + if run_mode["auto_checkin"] and last_result != 1: if self.__lib_checker.canCheckin(): if self.__lib_checkin.checkin(username): result = 0 @@ -255,8 +257,12 @@ class AutoLib(MsgBase): else: self._showTrace(f"用户 {username} 无法签到,已跳过") result = 2 + if last_result == 0: # partly success + result = 0 + # renewal - if run_mode["auto_renewal"] and result != 1: + last_result = result + if run_mode["auto_renewal"] and last_result != 1: can_renew, record = self.__lib_checker.canRenew() if can_renew: if self.__lib_renew.renew(username, record, reserve_info): @@ -264,12 +270,18 @@ class AutoLib(MsgBase): self._showTrace(f"用户 {username} 续约成功 !") result = 0 else: - result = 1 + if result != 1: # partly success + result = 0 + else: + result = 1 else: result = 1 else: self._showTrace(f"用户 {username} 无法续约,已跳过") result = 2 + if last_result == 0: # partly success + result = 0 + # logout if not self.__lib_logout.logout( username @@ -294,7 +306,8 @@ class AutoLib(MsgBase): for user in users: user_counter["current"] += 1 self._showTrace( - f"正在处理第 {user_counter["current"]}/{len(users)} 个用户: {user["username"]}......" + f"正在处理第 {user_counter["current"]}/{len(users)} 个用户: {user["username"]}......", + no_log=True ) if not user["enabled"]: self._showTrace(f"用户 {user["username"]} 已跳过") @@ -333,11 +346,14 @@ class AutoLib(MsgBase): if self.__driver: if self.__driver_type.lower() == "firefox": - self._showTrace(f"Firefox 浏览器驱动关闭略慢, 请耐心等待...") + self._showTrace( + f"Firefox 浏览器驱动关闭略慢, 请耐心等待...", + no_log=True + ) self.__driver.quit() self.__driver = None self._showTrace(f"浏览器驱动已关闭") return True else: - self._showTrace(f"浏览器驱动未初始化, 无需关闭") + self._showTrace(f"浏览器驱动未初始化, 无需关闭", no_log=True) return False \ No newline at end of file diff --git a/src/operators/LibChecker.py b/src/operators/LibChecker.py index 0b72f0d..e87a2d9 100644 --- a/src/operators/LibChecker.py +++ b/src/operators/LibChecker.py @@ -213,7 +213,7 @@ class LibChecker(LibOperator): if wanted_date is None: self._showTrace("日期未指定, 无法检查当前预约状态", self.TraceLevel.WARNING) return None - self._showTrace(f"正在检查用户在 {wanted_date} 是否有预约状态为 {wanted_status} 的预约记录......") + self._showTrace(f"正在检查用户在 {wanted_date} 是否有预约状态为 {wanted_status} 的预约记录......", no_log=True) checked_count = 0 max_check_times = 6 # we only check (4*(6-1)=)20 reservations, the last time cant be checked @@ -245,7 +245,8 @@ class LibChecker(LibOperator): self._showTrace( f"寻找到用户第 {checked_count} 条状态为 {wanted_status} 的预约记录, " f"详细信息: {record["date"]} " - f"{record["time"]["begin"]} - {record["time"]["end"]} {record["info"]["location"]}" + f"{record["time"]["begin"]} - {record["time"]["end"]} {record["info"]["location"]}", + no_log=True ) return record if not self.__showMoreReserveRecords(): diff --git a/src/operators/LibCheckin.py b/src/operators/LibCheckin.py index 568c901..6705ef8 100644 --- a/src/operators/LibCheckin.py +++ b/src/operators/LibCheckin.py @@ -107,7 +107,7 @@ class LibCheckin(LibOperator): result = self.__driver.execute_script(script) time.sleep(0.1) if result: - self._showTrace("签到按钮已启用") + self._showTrace("签到按钮已启用", no_log=True) else: self._showTrace("签到按钮启用失败", self.TraceLevel.WARNING) return result @@ -129,13 +129,13 @@ class LibCheckin(LibOperator): self._showTrace(f"用户 {username} 签到界面加载失败 !", self.TraceLevel.ERROR) return False if "disabled" in checkin_btn.get_attribute("class"): - self._showTrace("签到按钮不可用, 可能不在场馆内, 正在尝试启用......") + self._showTrace("签到按钮不可用, 可能不在场馆内, 正在尝试启用......", no_log=True) if not self.__enableCheckinBtn(): self._showTrace(f"签到按钮启用失败 !", self.TraceLevel.ERROR) return False checkin_btn.click() if self._waitResponseLoad(): - self._showTrace(f"用户 {username} 签到成功 !") + self._showTrace(f"用户 {username} 签到成功 !", no_log=True) return True else: self._showTrace(f"用户 {username} 签到失败 !", self.TraceLevel.ERROR) diff --git a/src/operators/LibLogin.py b/src/operators/LibLogin.py index 3a186c4..28555d3 100644 --- a/src/operators/LibLogin.py +++ b/src/operators/LibLogin.py @@ -91,7 +91,7 @@ class LibLogin(LibOperator): captcha_img = base64.b64decode(base64_str) captcha_text = self.__ddddocr.classification(captcha_img) captcha_text = ''.join(filter(str.isalnum, captcha_text)).lower() - self._showTrace(f"识别到验证码为 : '{captcha_text}'") + self._showTrace(f"识别到验证码为 : '{captcha_text}'", no_log=True) if len(captcha_text) != 4: self._showLog("识别到的验证码长度不等于 4 个字符 !", self.TraceLevel.WARNING) raise Exception("识别到的验证码长度不等于 4 个字符 !") @@ -109,7 +109,7 @@ class LibLogin(LibOperator): try: self._showMsg("请输入验证码:") captcha_text = self._waitMsg(timeout=15) - self._showTrace(f"输入的验证码为 : '{captcha_text}'") + self._showTrace(f"输入的验证码为 : '{captcha_text}'", no_log=True) if len(captcha_text) != 4: self._showLog("输入的验证码长度不等于 4 个字符 !", self.TraceLevel.WARNING) raise Exception("输入的验证码长度不等于 4 个字符 !") @@ -125,7 +125,7 @@ class LibLogin(LibOperator): # refresh captcha try: - self._showTrace("刷新验证码......") + self._showTrace("刷新验证码......", no_log=True) self.__driver.find_element( By.ID, "loadImgId" ).click() @@ -145,7 +145,7 @@ class LibLogin(LibOperator): if auto_captcha: captcha_text = self.__autoRecognizeCaptcha() else: - self._showTrace(f"用户未配置自动识别验证码, 请手动输入验证码 !") + self._showTrace(f"用户未配置自动识别验证码, 请手动输入验证码 !", no_log=True) captcha_text = self.__manualRecognizeCaptcha() if captcha_text: return captcha_text @@ -187,7 +187,7 @@ class LibLogin(LibOperator): return False # begin login process for attempt in range(max_attempts): - self._showTrace(f"用户 {username} 第 {attempt + 1} 次尝试登录......") + self._showTrace(f"用户 {username} 第 {attempt + 1} 次尝试登录......", no_log=True) if not self.__fillLogInElements( username, password, @@ -198,7 +198,7 @@ class LibLogin(LibOperator): continue if not self.__fillCaptchaElement(captcha_text): continue - self._showTrace("尝试登录...") + self._showTrace("尝试登录...", no_log=True) try: self.__driver.find_element( By.XPATH, diff --git a/src/operators/LibRenew.py b/src/operators/LibRenew.py index 7c42370..e09838d 100644 --- a/src/operators/LibRenew.py +++ b/src/operators/LibRenew.py @@ -61,7 +61,7 @@ class LibRenew(LibTimeSelector): result_message = result_message.text.strip() self._showTrace(f"\n"\ f" 续约失败 !\n"\ - f" {result_message}") + f" {result_message}", no_log=True) return False try: WebDriverWait(self.__driver, 2).until( @@ -186,7 +186,8 @@ class LibRenew(LibTimeSelector): self._showTrace(f"用户 {username} 续约界面加载失败 !", self.TraceLevel.ERROR) return False if "disabled" in renew_btn.get_attribute("class"): - self._showTrace(f"用户 {username} 续约按钮不可用, 可能不在场馆内, 请连接图书馆网络后重试") + self._showLog(f"用户 {username} 续约按钮不可用, 可能不在场馆内") + self._showTrace(f"用户 {username} 续约按钮不可用, 可能不在场馆内, 请连接图书馆网络后重试", no_log=True) return False renew_btn.click() if not self.__waitRenewDialog(): diff --git a/src/operators/LibReserve.py b/src/operators/LibReserve.py index 91e23bc..55572bd 100644 --- a/src/operators/LibReserve.py +++ b/src/operators/LibReserve.py @@ -125,9 +125,14 @@ class LibReserve(LibTimeSelector): except ValueError as e: self._showTrace( f"预约信息错误 ! : {e}, "\ - f"由于缺少必要的预约信息, 无法开始预约流程, 请检查预约信息是否完整", + f"由于缺少必要的预约信息, 无法开始预约流程", self.TraceLevel.ERROR ) + self._showTrace( + f"预约信息错误 ! : {e}, "\ + f"由于缺少必要的预约信息, 无法开始预约流程, 请检查预约信息是否完整", + no_log=True + ) return False @@ -481,7 +486,8 @@ class LibReserve(LibTimeSelector): seat_status = seat_link.get_attribute("title") self._showTrace(f"座位 {seat_id} 选择成功 ! : 当前状态 - '{seat_status}'") return True - self._showTrace(f"座位 {seat_id} 在该楼层区域中不存在, 请检查座位号是否正确", self.TraceLevel.WARNING) + self._showLog(f"座位 {seat_id} 在该楼层区域中不存在", self.TraceLevel.WARNING) + self._showTrace(f"座位 {seat_id} 在该楼层区域中不存在, 请检查座位号是否正确", no_log=True) except: self._showTrace(f"座位选择失败 !", self.TraceLevel.ERROR) return False diff --git a/src/utils/AppInitializer.py b/src/utils/AppInitializer.py index ecc89b8..5dcc19d 100644 --- a/src/utils/AppInitializer.py +++ b/src/utils/AppInitializer.py @@ -18,15 +18,21 @@ from utils.LogManager import instance as logInstance def initializeConfigManager( ) -> bool: + logger = logInstance().getLogger("AppInitializer") + app_dir = QStandardPaths.writableLocation(QStandardPaths.StandardLocation.AppDataLocation) old_config_dir = os.path.join(app_dir, "config") new_config_dir = os.path.join(app_dir, "configs") if QDir(old_config_dir).exists(): # old config dir exists #we rename it to compatible with new version + logger.info("存在旧配置目录 %s,将其重命名为 %s", old_config_dir, new_config_dir) if not QDir().rename(old_config_dir, new_config_dir): + logger.error("重命名旧配置目录 %s 到 %s 失败", old_config_dir, new_config_dir) return False elif not QDir(new_config_dir).exists(): + logger.info("初始化配置目录 %s", new_config_dir) if not QDir().mkpath(new_config_dir): + logger.error("创建配置目录 %s 失败", new_config_dir) return False configInstance(new_config_dir) return True