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