diff --git a/mobile/damai_app/orchestrator.py b/mobile/damai_app/orchestrator.py index 50be82b..95657f4 100755 --- a/mobile/damai_app/orchestrator.py +++ b/mobile/damai_app/orchestrator.py @@ -192,6 +192,29 @@ def _set_run_outcome(self, outcome): """Record the terminal outcome for the latest run attempt.""" self._last_run_outcome = outcome + def _report_pending_order_dialog(self): + """检测到「未支付订单弹窗」时的统一上报(真话优先,U-10 同源)。 + + probe_only 从不提交任何订单,此弹窗必然是账号里预先存在的占单(非本次 + 安全探测产生)。旧实现无论何种模式都记 ``order_pending_payment`` → + 「抢票成功:检测到未支付订单」,会误导安全探测用户以为 probe 下了单 + (2026-07-11 真机实测)。据实分流,返回 True 语义不变: + - probe_only:``preexisting_pending_order`` —— 只做探测提醒,不称成功; + - 正式/开发模式:``order_pending_payment`` —— 本轮流程占单,沿用原语义。 + """ + if self.config.probe_only: + self._set_run_outcome("preexisting_pending_order") + logger.info( + "探测提醒:账号存在未支付订单(非本次安全探测产生)," + "如需可自行前往订单页处理;本次探测未做任何提交" + ) + else: + self._set_run_outcome("order_pending_payment") + logger.info( + "检测到未支付订单弹窗(已占单待支付),请立即前往订单页完成支付" + ) + return True + def _execution_mode_key(self): """Return the current execution mode key.""" if self.config.probe_only: @@ -232,6 +255,7 @@ def _log_success_outcome(self, retry_prefix=""): "validation_ready": "开发验证成功:已到订单确认页,未提交订单", "order_submitted": "抢票成功:已提交订单", "order_pending_payment": "抢票成功:检测到未支付订单,请立即前往支付完成下单", + "preexisting_pending_order": "探测提醒:账号存在未支付订单(非本次探测产生),本次未做任何提交", "order_flow_completed": "抢票流程完成:已执行提交,等待后续结果确认", } logger.info( @@ -760,11 +784,7 @@ def run_ticket_grabbing(self, initial_page_probe=None): return False if page_probe["state"] == "pending_order_dialog": - self._set_run_outcome("order_pending_payment") - logger.info( - "检测到未支付订单弹窗(已占单待支付),请立即前往订单页完成支付" - ) - return True + return self._report_pending_order_dialog() if page_probe["state"] not in { "detail_page", @@ -779,11 +799,7 @@ def run_ticket_grabbing(self, initial_page_probe=None): return False page_probe = self.probe_current_page() if page_probe["state"] == "pending_order_dialog": - self._set_run_outcome("order_pending_payment") - logger.info( - "检测到未支付订单弹窗(已占单待支付),请立即前往订单页完成支付" - ) - return True + return self._report_pending_order_dialog() else: logger.warning("当前不在演出详情页,请先手动打开目标演出详情页") return False diff --git a/mobile/damai_app/recovery_strategies.py b/mobile/damai_app/recovery_strategies.py index 1b1372c..5bb918d 100644 --- a/mobile/damai_app/recovery_strategies.py +++ b/mobile/damai_app/recovery_strategies.py @@ -351,11 +351,7 @@ def _fast_retry_from_current_state(self): result = self._submit_order_fast(submit_selectors) return self._finalize_submit_result(result) elif state == "pending_order_dialog": - self._set_run_outcome("order_pending_payment") - logger.info( - "检测到未支付订单弹窗(已占单待支付),请立即前往订单页完成支付" - ) - return True + return self._report_pending_order_dialog() else: if self.config.auto_navigate: return ( diff --git a/tests/unit/test_mobile_damai_app.py b/tests/unit/test_mobile_damai_app.py index 85a1f7c..f7e5771 100644 --- a/tests/unit/test_mobile_damai_app.py +++ b/tests/unit/test_mobile_damai_app.py @@ -1588,6 +1588,45 @@ def test_run_ticket_grabbing_returns_success_when_pending_order_dialog_detected_ assert result is True assert bot._last_run_outcome == "order_pending_payment" + def test_run_ticket_grabbing_probe_mode_pending_order_is_not_grab_success(self, bot): + # 2026-07-11 真机回归:probe_only 从不提交,遇到预先存在的未支付订单弹窗 + # 必须据实上报(preexisting_pending_order),绝不能记「抢票成功」——否则 + # 安全探测用户会误以为 probe 下了单(信任问题,U-10 同源)。 + bot.config.probe_only = True + bot.config.if_commit_order = False + with patch.object(bot, "dismiss_startup_popups"): + with patch.object(bot, "check_session_valid", return_value=True): + with patch.object( + bot, + "probe_current_page", + return_value={ + "state": "pending_order_dialog", + "purchase_button": False, + "price_container": False, + "quantity_picker": False, + "submit_button": False, + "reservation_mode": False, + "pending_order_dialog": True, + }, + ): + result = bot.run_ticket_grabbing() + + assert result is True + assert bot._last_run_outcome == "preexisting_pending_order" + + def test_log_success_outcome_preexisting_pending_order_not_grab_success( + self, bot, caplog + ): + # 成功日志映射也不得对该 outcome 说「抢票成功」 + import logging as _logging + + bot._set_run_outcome("preexisting_pending_order") + with caplog.at_level(_logging.INFO): + bot._log_success_outcome("快速重试成功:") + joined = " ".join(r.message for r in caplog.records) + assert "抢票成功" not in joined + assert "未支付订单" in joined + def test_run_ticket_grabbing_rush_mode_skips_detail_prepare_and_reprobe_when_no_sell_time( self, bot ):