diff --git a/inbound_verify/cli/server.py b/inbound_verify/cli/server.py index c928c28..0779828 100644 --- a/inbound_verify/cli/server.py +++ b/inbound_verify/cli/server.py @@ -58,7 +58,7 @@ scheduler = BackgroundScheduler(daemon=True) def _worker_loop(): """worker 线程:启动 Playwright + 等就绪 + 任务循环(执行任务 + 心跳)。""" try: - ctx = launch_and_prepare() + ctx = launch_and_prepare(foreground=False) worker_state["ctx"] = ctx worker_state["ready"] = True # 【P1-2 重启自愈】worker 就绪后清理上轮遗留的 pending/running 僵尸任务 diff --git a/inbound_verify/runtime.py b/inbound_verify/runtime.py index 264b147..19ffe81 100644 --- a/inbound_verify/runtime.py +++ b/inbound_verify/runtime.py @@ -179,6 +179,7 @@ class RuntimeContext: sites_to_watch, debug_mode, debug_target, + foreground=True, ): self.pw = pw self.browser = browser @@ -188,6 +189,8 @@ class RuntimeContext: self.sites_to_watch = sites_to_watch self.debug_mode = debug_mode self.debug_target = debug_target + # True=任务执行时把 page 置顶(交互调试);False=后台静默不置顶(服务模式,避免抢焦点) + self.foreground = foreground def stop(self): """关闭 browser + 安能 + Playwright。退出时调用。""" @@ -236,11 +239,15 @@ def seed_legacy_config(): print(f">> [seed] 从 config.yaml 灌入站点配置: {', '.join(seeded)}") -def launch_and_prepare(debug_mode=False, debug_target=""): +def launch_and_prepare(debug_mode=False, debug_target="", foreground=True): """启动 Playwright + 各站 page + 就绪轮询 + 弹窗清理 + 心跳初值,返回 RuntimeContext。 必须在"持有 Playwright 的线程"调用(交互模式主线程 / 服务模式 worker 线程)。 阻塞至所有站点登录就绪才返回。 + + foreground:True=任务执行时把 page 置顶(交互调试);False=后台静默不置顶(服务模式, + 避免抢用户焦点)。仅控制任务执行阶段的 bring_to_front;启动登录/初始弹窗清理的置顶 + 不受影响(启动时窗口需对用户可见以便登录)。 """ # 0. 状态库建表/迁移 + 从 config.yaml 灌入站点配置(须在 reset_login_states 等之前) state_store.init_db() @@ -397,6 +404,7 @@ def launch_and_prepare(debug_mode=False, debug_target=""): sites_to_watch, debug_mode, debug_target, + foreground, ) @@ -440,11 +448,18 @@ def _dismiss_initial_popups(pages_map): def _web_handler(site, download_func): - """构造网页站任务 handler:单 page 先 bring_to_front 再 download;顺心(list) 直接传。""" + """构造网页站任务 handler。 + + foreground(ctx)控制任务执行时是否把 page 置顶:服务模式后台跑不置顶,避免抢用户 + 焦点;交互模式置顶便于调试。顺心是 page 列表,置顶标志透传给 shunxin_download。 + """ def handler(ctx): pg = ctx.pages_map[site] - if not isinstance(pg, list): + if isinstance(pg, list): + # 顺心双账号:置顶与否交给 shunxin_download 在逐账号循环里按 foreground 决定 + return download_func(pg, foreground=ctx.foreground) + if ctx.foreground: pg.bring_to_front() return download_func(pg) diff --git a/inbound_verify/sites/shunxin.py b/inbound_verify/sites/shunxin.py index 2bcc1e2..d78511c 100644 --- a/inbound_verify/sites/shunxin.py +++ b/inbound_verify/sites/shunxin.py @@ -149,7 +149,7 @@ def shunxin_merge_final(kind, tags): pass -def shunxin_expected_download(pages): +def shunxin_expected_download(pages, foreground=True): """顺心:应到货物数据下载(双账号/双归属地,内部含异常兜底重试与数据融合)。 pages 为该站点的 page 列表(双账号在同一窗口的各一个标签页)。 @@ -169,7 +169,8 @@ def shunxin_expected_download(pages): ) for idx, (pg, tag) in enumerate(zip(pages, tags), start=1): - pg.bring_to_front() + if foreground: + pg.bring_to_front() print(f"\n========== 顺心 · 账号{idx}({tag})应到数据下载 ==========") ok = with_retry( f"顺心-{tag}", @@ -476,7 +477,7 @@ def shunxin_expected_download_impl(page, out_tag=""): return False -def shunxin_actual_download(pages): +def shunxin_actual_download(pages, foreground=True): """顺心:实到货物数据下载(双账号/双归属地,内部含异常兜底重试与数据融合)。 与 shunxin_expected_download 同构:读归属地 → 去重校验 → 顺序各账号下载 → @@ -494,7 +495,8 @@ def shunxin_actual_download(pages): ) for idx, (pg, tag) in enumerate(zip(pages, tags), start=1): - pg.bring_to_front() + if foreground: + pg.bring_to_front() print(f"\n========== 顺心 · 账号{idx}({tag})实到数据下载 ==========") ok = with_retry( f"顺心-{tag}",