Add cross-flow automation test and tone down log/comment wording

- main_router: add run_automation_test that runs each site's download
  flows in a cross sequence (expected/actual) plus a pass/fail report
  with timings; expose it as menu [8]
- Site download functions now return False on their exception paths so
  the test harness can record failures (baishi also flags config-read
  failure)
- Replace overblown log/comment phrasing across all modules with plain
  statements; trim docstrings

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Misaka
2026-06-20 23:33:22 +08:00
parent ab3f25a10e
commit 229e474b58
5 changed files with 298 additions and 191 deletions

View File

@@ -10,7 +10,7 @@ from paths import DOWNLOAD_DIR, CONFIG_PATH
def _wait_and_get_frame(page, text_indicator, timeout_ms=20000):
"""动态雷达探测器:全域扫描所有视窗"""
"""在主页面与所有 iframe 中查找包含指定文本的窗口"""
start_time = datetime.now()
while (datetime.now() - start_time).total_seconds() * 1000 < timeout_ms:
try:
@@ -27,11 +27,11 @@ def _wait_and_get_frame(page, text_indicator, timeout_ms=20000):
pass
page.wait_for_timeout(300)
raise TimeoutError(f"爆栈超时:全域未死守到包含 [{text_indicator}] 的弹窗视窗。")
raise TimeoutError(f"超时:未找到包含 [{text_indicator}] 的窗")
def zto_smart_menu_click(page, menu_path):
"""中通智能菜单导航"""
"""中通菜单导航"""
print(f">> 正在导航: {' -> '.join(menu_path)}")
for i in range(len(menu_path)):
current_menu = menu_path[i]
@@ -58,12 +58,12 @@ def zto_expected_download(page):
export_times = []
try:
# 1. 智能导航
# 1. 菜单导航
zto_smart_menu_click(page, ["运营管理", "进站管理", "进站交接单查询"])
ewb_frame = page.frame_locator('iframe[src*="inEwbsListNoSearch"]')
print(">> 正在探针检测表格统计面板 (#inEwbCount)...")
print(">> 正在检测表格统计面板 (#inEwbCount)...")
ewb_frame.locator("#inEwbCount").wait_for(state="attached", timeout=15000)
# 读取 YAML 配置设定天数
@@ -105,7 +105,7 @@ def zto_expected_download(page):
page.wait_for_timeout(500)
# 3. 触发查询与深度状态机判定
# 3. 触发查询并判断数据状态
print(">> 正在点击【查询】按钮并等待数据响应...")
old_count = ewb_frame.locator("#inEwbCount").inner_text().strip()
ewb_frame.locator("#searchbtn").click()
@@ -129,7 +129,7 @@ def zto_expected_download(page):
wait_cycles += 1
ticket_count_str = ewb_frame.locator("#inEwbCount").inner_text().strip()
print(f" ✅ 数据面板就绪!当前进站实际票数: [{ticket_count_str}]")
print(f" ✅ 数据已加载,进站实际票数: [{ticket_count_str}]")
# 数据分流
if (
@@ -137,13 +137,13 @@ def zto_expected_download(page):
or not ticket_count_str.isdigit()
or int(ticket_count_str) == 0
):
print(" >> 票数为 0正在验证是否为确切的空数据...")
print(" >> 票数为 0正在确认是否为空数据...")
empty_flag = ewb_frame.locator("#datagrid1").get_by_text(
"没有搜索到符合条件的数据记录"
)
if empty_flag.is_visible():
print(" ⚠️ 确认为空数据,正在关闭当前标签页清理环境...")
# 即使为空数据,我们也清理关闭当前已经打开的 Tab
print(" ⚠️ 确认为空数据,正在关闭当前标签页...")
# 即使无数据也关闭已打开的标签页
try:
page.locator(".mini-tab", has_text="进站交接单查询").locator(
".mini-tab-close"
@@ -161,7 +161,7 @@ def zto_expected_download(page):
pass
return
else:
print(" >> 票数验通过,等待主数据表格渲染实体行...")
print(" >> 票数验通过,等待主表格渲染数据行...")
ewb_frame.locator(
"#datagrid1 .mini-grid-rows-view .mini-grid-row"
).first.wait_for(state="visible", timeout=15000)
@@ -180,7 +180,7 @@ def zto_expected_download(page):
raw_text = row.locator("td").nth(3).inner_text()
match = re.search(r"\d{18}", raw_text)
handover_no = match.group(0) if match else raw_text.strip()
print(f" -> 锁定提取单号:{handover_no}")
print(f" -> 当前交接单号:{handover_no}")
row.dblclick()
@@ -203,12 +203,12 @@ def zto_expected_download(page):
page.wait_for_timeout(300)
export_frame.locator(".mini-button-text", has_text="确定").click()
print(" >> [雷达扫描] 正在跨域动态追踪【温馨提示】所在的隐身视窗...")
print(" >> 正在查找【温馨提示】弹窗...")
ctx_alert = _wait_and_get_frame(page, "温馨提示")
ctx_alert.locator(
".mini-messagebox-buttons .mini-button-text", has_text="确定"
).click()
print(" ✅ 【温馨提示】已成功通过父级过滤并确认!")
print("已确认【温馨提示】弹窗。")
print(" >> 正在等待服务器建立后台离线任务...")
try:
@@ -218,9 +218,9 @@ def zto_expected_download(page):
ctx_tips.locator(".mini-tips-success").wait_for(
state="hidden", timeout=15000
)
print(" ✅ 成功提示框已平滑隐藏")
print(" ✅ 成功提示框已消失")
except Exception:
print(" ✅ 成功提示框及其容器已自动销毁")
print(" ✅ 成功提示框已关闭")
export_times.append(datetime.now())
@@ -229,19 +229,19 @@ def zto_expected_download(page):
page.wait_for_timeout(1000)
# ====================================================================
# 🛡️ 优化优化:完成交接单主表的所有穿透提交后,立刻销毁本 Tab 净化内存
# 完成所有交接单导出提交后,关闭当前标签页
# ====================================================================
print(">> 📥 【进站交接单查询】任务已就绪,正在精准执行 Tab 窗口销毁复位...")
print(">> 【进站交接单查询】已完成,正在关闭标签页...")
try:
page.locator(".mini-tab", has_text="进站交接单查询").locator(
".mini-tab-close"
).click()
page.wait_for_timeout(500)
print(" ✅ 【进站交接单查询】Tab 已成功关闭。")
print(" ✅ 【进站交接单查询】标签页已关闭。")
except Exception as e:
print(f" ⚠️ 尝试关闭【进站交接单查询】Tab 时遇到轻微阻碍: {e}")
print(f" ⚠️ 关闭【进站交接单查询】标签页时出错: {e}")
# 核心逻辑交由统一的轮询下载引擎处理
# 交由统一的轮询下载流程处理
_zto_poll_and_download_tasks(
page,
export_times,
@@ -252,6 +252,7 @@ def zto_expected_download(page):
except Exception as e:
print(f"\n❌ 任务执行过程中发生异常: {e}")
return False
def zto_actual_download(page):
@@ -266,12 +267,12 @@ def zto_actual_download(page):
export_times = []
try:
# 1. 智能导航
# 1. 菜单导航
zto_smart_menu_click(page, ["运营管理", "扫描操作与监控", "到件扫描监控"])
arr_frame = page.frame_locator('iframe[src*="ArriveScan"]')
print(">> 正在探针检测主页面 (#daterange)...")
print(">> 正在检测主页面 (#daterange)...")
arr_frame.locator("#daterange").wait_for(state="attached", timeout=15000)
# 2. 读取 YAML 并设定时间范围
@@ -322,7 +323,7 @@ def zto_actual_download(page):
).locator(".mini-tree-checkbox").click()
page.wait_for_timeout(300)
# 4. 触发查询与深度状态机判定
# 4. 触发查询并判断数据状态
print(">> 正在点击【查询】按钮并等待数据响应...")
arr_frame.locator("#searchbtn").click()
@@ -340,7 +341,7 @@ def zto_actual_download(page):
)
if empty_flag.is_visible():
print(
" ⚠️ 当前查询范围内没有搜索到符合条件的数据记录】,终止并关闭环境"
" ⚠️ 当前查询范围内没有数据,终止并关闭标签页"
)
try:
page.locator(".mini-tab", has_text="到件扫描监控").locator(
@@ -351,7 +352,7 @@ def zto_actual_download(page):
return
arr_frame.locator("#page1").wait_for(state="visible", timeout=15000)
print(" ✅ 数据渲染完毕!(底部分页统计控件已就绪)")
print(" ✅ 数据已加载。(底部分页控件已就绪)")
# 5. 执行导出流程
arr_frame.locator("#exportExcel").click()
@@ -365,12 +366,12 @@ def zto_actual_download(page):
page.wait_for_timeout(300)
export_frame.locator(".mini-button-text", has_text="确定").click()
print(" >> [雷达扫描] 正在跨域动态追踪【温馨提示】所在的隐身视窗...")
print(" >> 正在查找【温馨提示】弹窗...")
ctx_alert = _wait_and_get_frame(page, "温馨提示")
ctx_alert.locator(
".mini-messagebox-buttons .mini-button-text", has_text="确定"
).click()
print(" ✅ 【温馨提示】已成功通过父级过滤并确认!")
print("已确认【温馨提示】弹窗。")
print(" >> 正在等待服务器建立后台离线任务...")
try:
@@ -380,26 +381,26 @@ def zto_actual_download(page):
ctx_tips.locator(".mini-tips-success").wait_for(
state="hidden", timeout=15000
)
print(" ✅ 成功提示框已平滑隐藏")
print(" ✅ 成功提示框已消失")
except Exception:
print(" ✅ 成功提示框及其容器已自动销毁")
print(" ✅ 成功提示框已关闭")
export_times.append(datetime.now())
# ====================================================================
# 🛡️ 优化优化:完成实到主表的数据导出提交后,立刻销毁本 Tab 恢复空态
# 完成实到数据导出提交后,关闭当前标签页
# ====================================================================
print(">> 📥 【到件扫描监控】任务已就绪,正在精准执行 Tab 窗口销毁复位...")
print(">> 【到件扫描监控】已完成,正在关闭标签页...")
try:
page.locator(".mini-tab", has_text="到件扫描监控").locator(
".mini-tab-close"
).click()
page.wait_for_timeout(500)
print(" ✅ 【到件扫描监控】Tab 已成功关闭。")
print(" ✅ 【到件扫描监控】标签页已关闭。")
except Exception as e:
print(f" ⚠️ 尝试关闭【到件扫描监控】Tab 时遇到轻微阻碍: {e}")
print(f" ⚠️ 关闭【到件扫描监控】标签页时出错: {e}")
# 核心逻辑交由统一的轮询下载引擎处理
# 交由统一的轮询下载流程处理
_zto_poll_and_download_tasks(
page,
export_times,
@@ -410,12 +411,13 @@ def zto_actual_download(page):
except Exception as e:
print(f"\n❌ 任务执行过程中发生异常: {e}")
return False
def _zto_poll_and_download_tasks(
page, export_times, target_task_title, download_dir, final_filename
):
"""进站中通任务专属轮询、验证及即选即隐下载引擎"""
"""中通离线任务的轮询与下载"""
print("\n>> 正在前往【导出任务管理】界面...")
zto_smart_menu_click(page, ["系统配置", "导出任务管理"])
@@ -425,7 +427,7 @@ def _zto_poll_and_download_tasks(
)
page.wait_for_timeout(1000)
print(">> 列表中已渲染,开始匹配并检查后端处理状态...")
print(">> 列表已加载,开始匹配并检查任务状态...")
total_expected = len(export_times)
while True:
@@ -464,23 +466,23 @@ def _zto_poll_and_download_tasks(
total_found = len(ready_timestamps) + len(processing_timestamps)
print(
f" 📊 状态盘点:期望任务数 [{total_expected}]实际入表 [{total_found}] (就绪 [{len(ready_timestamps)}],处理中 [{len(processing_timestamps)}])"
f" 📊 状态统计:期望 [{total_expected}]入表 [{total_found}] (就绪 [{len(ready_timestamps)}],处理中 [{len(processing_timestamps)}])"
)
if total_found < total_expected or len(processing_timestamps) > 0:
print("发现任务缺失或正在生成,执行【点击查询按钮】触发局部重载...")
print("任务尚未齐全或仍在生成,点击查询刷新...")
try:
taskdone_frame.locator(
".mini-button-text", has_text="查询"
).first.click()
except Exception as e:
print(f" ⚠️ 局部刷新按钮失效,使用菜单后备刷新: {e}")
print(f" ⚠️ 查询按钮不可用,改用菜单刷新: {e}")
page.locator("li.leaf span.menu-name", has_text="导出任务管理").click()
page.wait_for_timeout(3000)
else:
target_task_timestamps = list(ready_timestamps)
print(">> 目标任务已全部生成,解除锁定,开始接收文件流...")
print(">> 所有目标任务已生成,开始下载...")
break
# 8. 下载逻辑
@@ -498,7 +500,7 @@ def _zto_poll_and_download_tasks(
)
.first
)
print(f" 🎯 触发下载 -> 任务 [{time_str}] ...")
print(f" 开始下载任务 [{time_str}] ...")
checkbox = target_row.locator(".mini-grid-checkbox")
if checkbox.is_visible():
@@ -513,32 +515,32 @@ def _zto_poll_and_download_tasks(
save_path = os.path.join(download_dir, download.suggested_filename)
download.save_as(save_path)
downloaded_files.append(save_path)
print(f" ⬇️ 文件已落盘: downloads/{download.suggested_filename}")
print(f" 已下载: downloads/{download.suggested_filename}")
if checkbox.is_visible():
checkbox.click()
print(" -> 已取消勾选,清理现场进入下一条")
print(" -> 已取消勾选,继续下一条")
page.wait_for_timeout(500)
except Exception as e:
print(f" ❌ 下载任务 [{time_str}] 失败: {e}")
# ====================================================================
# 🛡️ 优化优化:所有目标任务文件全部下载完后,立刻销毁“导出任务管理” Tab
# 所有目标文件下载完后,关闭“导出任务管理”标签页
# ====================================================================
print(">> 📥 【导出任务管理】所有数据拉取流闭环,正在执行 Tab 窗口销毁复位...")
print(">> 【导出任务管理】下载完成,正在关闭标签页...")
try:
page.locator(".mini-tab", has_text="导出任务管理").locator(
".mini-tab-close"
).click()
page.wait_for_timeout(500)
print(" ✅ 【导出任务管理】Tab 已安全关闭。页面归档空态")
print(" ✅ 【导出任务管理】标签页已关闭")
except Exception as e:
print(f" ⚠️ 尝试收尾关闭【导出任务管理】Tab 时有波动: {e}")
print(f" ⚠️ 关闭【导出任务管理】标签页时出错: {e}")
# 9. 合并数据
if downloaded_files:
print("\n>> 🧪 正在开始执行扁平数据高能合并与清洗流程...")
print("\n>> 正在合并下载的数据...")
all_data_frames = []
for file_path in downloaded_files:
try:
@@ -553,10 +555,10 @@ def _zto_poll_and_download_tasks(
final_output_path = os.path.join(download_dir, final_filename)
combined_df.to_excel(final_output_path, index=False)
print(f"====================================================")
print(f" 🎉 恭喜!数据流闭环完成")
print(f" 📁 最终输出成果归档至: {final_output_path}")
print(f" 合并完成")
print(f" 📁 输出路径: {final_output_path}")
print(f"====================================================")
for file_path in downloaded_files:
os.remove(file_path)
print(" ✅ 临时缓存数据清理完毕")
print(" 临时文件已清理")