Files
InboundVerify/site_shunxin.py
Misaka 5d13c13f40 Close Shunxin tabs after each stage via shared _close_tab helper
- Add _close_tab(page, tab_name) for closing Ant Design Tabs by label
- Close 运单列表 / 车辆点到 in the expected-arrival flow after export
  submission, and 数据导出 after download/merge
- Close 卸车扫描记录 and 数据导出 likewise in the actual-arrival flow
- Renumber step comments

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-20 21:47:05 +08:00

399 lines
17 KiB
Python
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# site_shunxin.py
import os
import re
from datetime import datetime
import pandas as pd
def _close_tab(page, tab_name):
"""关闭指定名称的标签页Ant Design Tabs
通过标签文字定位标签容器,点击其右侧的关闭(×)按钮。
- tab_name 采用包含匹配,对“ 运单列表”这类带空格的标签同样有效。
- 关闭失败不会影响主流程,仅打印提示信息。
"""
try:
tab = page.locator(".ant-tabs-tab").filter(
has=page.get_by_role("tab", name=tab_name)
)
if tab.count() == 0:
print(
f" 未找到标签页【{tab_name.strip()}】(可能尚未打开或已关闭),跳过。"
)
return
tab.first.locator(".ant-tabs-tab-remove").click()
print(f" 🗙 已关闭标签页【{tab_name.strip()}")
page.wait_for_timeout(300)
except Exception as e:
print(f" ⚠️ 关闭标签页【{tab_name.strip()}】时出错: {e}")
def shunxin_expected_download(page):
"""顺心:应到货物数据下载"""
print("\n▶ 开始执行【顺心 - 应到货物数据下载】任务...")
# 初始化并创建下载目录
download_dir = os.path.join(os.getcwd(), "downloads")
if not os.path.exists(download_dir):
os.makedirs(download_dir)
print(f">> 已创建专属下载文件夹: {download_dir}")
export_times = []
target_task_timestamps = []
try:
# 1. 导航与页面加载判断
print(">> 正在进入【车辆点到】界面...")
page.locator("span.ant-pro-menu-item-title:has-text('派件管理')").click()
page.locator("div.ant-pro-menu-item:has-text('车辆点到')").click()
page.get_by_role("button", name="点到").wait_for(state="visible")
page.get_by_role("button", name="打印交接单").wait_for(state="visible")
page.get_by_role("button", name="强卸").wait_for(state="visible")
print("✅ 车辆点到界面加载完毕")
# 2. 筛选条件1天、状态=已发 -> 已到、查询
print(">> 正在设置筛选条件: 选择【1天】...")
page.get_by_role("radio", name="1天").check()
print(">> 正在展开【状态】下拉菜单...")
page.locator(
".ant-select-selection-item", has_text=re.compile(r"已发|已到")
).click()
print(">> 正在选择状态为【已到】...")
page.locator(".ant-select-item-option", has_text="已到").click()
print(">> 正在点击【查询】按钮...")
page.get_by_role("button", name="search 查询").click()
page.wait_for_timeout(2000)
# 3. 获取所有【运单列表】按钮并循环处理
waybill_btns = page.get_by_role("button", name="运单列表")
count = waybill_btns.count()
print(f">> 共发现 {count} 个班次需要导出。")
for i in range(count):
print(f" ⏳ 正在处理第 {i+1}/{count} 个班次...")
waybill_btns.nth(i).click()
page.locator("label[title='运单查询']").wait_for(state="visible")
# 4. 执行导出流程
page.get_by_role("button", name="export 导出").click()
page.locator(
"span.ant-transfer-list-header-title:has-text('待选导出列')"
).wait_for(state="visible")
page.locator(".ant-transfer-list").first.locator(
".ant-transfer-list-header label"
).click()
page.get_by_label("导出").get_by_role("button", name="right").click()
page.get_by_role("button", name="export 导出数据").click()
export_times.append(datetime.now())
page.locator("text=任务添加成功!").wait_for(state="visible")
page.get_by_role("button", name="知道了").click()
page.get_by_role("tab", name="车辆点到").click()
page.wait_for_timeout(500)
if count > 0:
print("✅ 所有班次的导出任务已成功提交!")
else:
print("⚠️ 未发现任何运单列表,直接跳转至下载环节。")
# 5. 关闭【运单列表】与【车辆点到】标签页(这两个页面的工作均已完成)
# 运单列表为单一复用标签,所有班次导出完成后统一关闭即可。
_close_tab(page, "运单列表")
_close_tab(page, "车辆点到")
# 6. 前往数据导出页面去下载
print(">> 正在前往【数据导出】界面...")
page.locator("a[href='/dataExport']").click()
page.get_by_role("columnheader", name="任务标题").wait_for(state="visible")
page.wait_for_timeout(2000)
page.get_by_role("button", name="search 查询").click()
page.wait_for_timeout(2000)
# 7. 轮询任务状态
print(">> 列表中已渲染,开始匹配并检查后端处理状态...")
while True:
rows = page.locator(".ant-table-tbody > tr.ant-table-row")
row_count = rows.count()
pending_tasks = 0
current_ready_timestamps = []
for i in range(row_count):
tds = rows.nth(i).locator("td")
if tds.count() < 9:
continue
submit_time_str = tds.nth(2).inner_text().strip()
title_str = tds.nth(5).inner_text().strip()
status_str = tds.nth(6).inner_text().strip()
if title_str == "发车管理运单列表":
try:
row_time = datetime.strptime(
submit_time_str, "%Y-%m-%d %H:%M:%S"
)
matched = any(
abs((row_time - et).total_seconds()) <= 60
for et in export_times
)
if matched:
if status_str != "执行完成":
pending_tasks += 1
if submit_time_str not in current_ready_timestamps:
print(
f" ⏳ 任务 [{submit_time_str}] 状态为【{status_str}】,数据生成中..."
)
else:
if submit_time_str not in current_ready_timestamps:
current_ready_timestamps.append(submit_time_str)
except Exception as e:
print(f" ⚠️ 解析时间时出错: {e}")
if pending_tasks > 0:
print(
f">> 共有 {pending_tasks} 个匹配任务还在处理中,等待 5 秒后刷新..."
)
page.wait_for_timeout(5000)
page.get_by_role("button", name="search 查询").click()
page.wait_for_timeout(2000)
else:
target_task_timestamps = current_ready_timestamps
if len(target_task_timestamps) > 0:
print(">> ✅ 所有目标任务已就绪!开始并行下载...")
break
# 8. 下载逻辑
downloaded_files = []
for time_str in target_task_timestamps:
try:
target_row = page.locator(".ant-table-tbody > tr.ant-table-row").filter(
has=page.locator(f"td:nth-child(3):has-text('{time_str}')")
)
print(f" 🎯 触发下载 -> 任务 [{time_str}] ...")
with page.expect_download() as download_info:
target_row.locator("td").nth(8).locator(
"button", has_text=re.compile(r"\s*载")
).click()
download = download_info.value
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}")
except Exception as e:
print(f" ❌ 下载任务 [{time_str}] 失败: {e}")
# 9. 合并数据 (修改了文件名前缀)
if downloaded_files:
print("\n>> 🧪 正在开始执行扁平数据高能合并流程...")
all_data_frames = []
for file_path in downloaded_files:
try:
df = pd.read_excel(file_path)
if not df.empty:
all_data_frames.append(df)
except Exception as e:
pass
if all_data_frames:
combined_df = pd.concat(all_data_frames, ignore_index=True)
final_output_path = os.path.join(download_dir, "顺心-应到货物数据.xlsx")
combined_df.to_excel(final_output_path, index=False)
print(f"====================================================")
print(f" 🎉 恭喜!合并成功!最终输出路径: {final_output_path}")
print(f"====================================================")
for file_path in downloaded_files:
os.remove(file_path)
print("✅ 临时数据清理完毕。")
# 10. 关闭【数据导出】标签页(该页面工作已完成)
_close_tab(page, "数据导出")
print("\n🎉 【顺心 - 应到货物数据下载】全流程测试完毕!")
except Exception as e:
print(f"\n❌ 任务执行过程中发生异常: {e}")
def shunxin_actual_download(page):
"""顺心:实到货物数据下载"""
print("\n▶ 开始执行【顺心 - 实到货物数据下载】任务...")
download_dir = os.path.join(os.getcwd(), "downloads")
if not os.path.exists(download_dir):
os.makedirs(download_dir)
export_times = []
target_task_timestamps = []
try:
# 1. 导航与页面加载
print(">> 正在进入【卸车扫描记录】界面...")
page.locator("span.ant-pro-menu-item-title:has-text('派件管理')").click()
page.locator("div.ant-pro-menu-item:has-text('卸车扫描记录')").click()
page.get_by_role("radio", name="1天").wait_for(state="visible")
print("✅ 卸车扫描记录界面加载完毕")
# 2. 筛选条件1天、查询
print(">> 正在设置筛选条件: 选择【1天】...")
page.get_by_role("radio", name="1天").check()
print(">> 正在点击【查询】按钮...")
page.get_by_role("button", name="search 查询").click()
page.wait_for_timeout(2000)
# 3. 直接发起全局导出
print(">> 正在发起导出请求...")
page.get_by_role("button", name="export 导出").click()
page.locator(
"span.ant-transfer-list-header-title:has-text('待选导出列')"
).wait_for(state="visible")
page.locator(".ant-transfer-list").first.locator(
".ant-transfer-list-header label"
).click()
page.get_by_label("导出").get_by_role("button", name="right").click()
page.get_by_role("button", name="export 导出数据").click()
export_times.append(datetime.now())
page.locator("text=任务添加成功!").wait_for(state="visible")
page.get_by_role("button", name="知道了").click()
print("✅ 卸车扫描记录导出任务已成功提交!")
# 4. 关闭【卸车扫描记录】标签页(该页面工作已完成)
_close_tab(page, "卸车扫描记录")
# 5. 前往数据导出页面去下载
print(">> 正在前往【数据导出】界面...")
page.locator("a[href='/dataExport']").click()
page.get_by_role("columnheader", name="任务标题").wait_for(state="visible")
page.wait_for_timeout(2000)
page.get_by_role("button", name="search 查询").click()
page.wait_for_timeout(2000)
# 6. 轮询任务状态
print(">> 列表中已渲染,开始匹配并检查后端处理状态...")
while True:
rows = page.locator(".ant-table-tbody > tr.ant-table-row")
row_count = rows.count()
pending_tasks = 0
current_ready_timestamps = []
for i in range(row_count):
tds = rows.nth(i).locator("td")
if tds.count() < 9:
continue
submit_time_str = tds.nth(2).inner_text().strip()
title_str = tds.nth(5).inner_text().strip()
status_str = tds.nth(6).inner_text().strip()
if title_str.startswith("卸车扫描记录"):
try:
row_time = datetime.strptime(
submit_time_str, "%Y-%m-%d %H:%M:%S"
)
matched = any(
abs((row_time - et).total_seconds()) <= 60
for et in export_times
)
if matched:
if status_str != "执行完成":
pending_tasks += 1
if submit_time_str not in current_ready_timestamps:
print(
f" ⏳ 任务 [{submit_time_str}] 状态为【{status_str}】,数据生成中..."
)
else:
if submit_time_str not in current_ready_timestamps:
current_ready_timestamps.append(submit_time_str)
except Exception as e:
print(f" ⚠️ 解析时间时出错: {e}")
if pending_tasks > 0:
print(
f">> 共有 {pending_tasks} 个匹配任务还在处理中,等待 5 秒后刷新..."
)
page.wait_for_timeout(5000)
page.get_by_role("button", name="search 查询").click()
page.wait_for_timeout(2000)
else:
target_task_timestamps = current_ready_timestamps
if len(target_task_timestamps) > 0:
print(">> ✅ 目标任务已就绪!开始下载...")
break
# 7. 下载逻辑
downloaded_files = []
for time_str in target_task_timestamps:
try:
target_row = page.locator(".ant-table-tbody > tr.ant-table-row").filter(
has=page.locator(f"td:nth-child(3):has-text('{time_str}')")
)
print(f" 🎯 触发下载 -> 任务 [{time_str}] ...")
with page.expect_download() as download_info:
target_row.locator("td").nth(8).locator(
"button", has_text=re.compile(r"\s*载")
).click()
download = download_info.value
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}")
except Exception as e:
print(f" ❌ 下载任务 [{time_str}] 失败: {e}")
# 8. 合并数据 (修改了文件名前缀)
if downloaded_files:
print("\n>> 🧪 正在开始执行数据归档整理...")
all_data_frames = []
for file_path in downloaded_files:
try:
df = pd.read_excel(file_path)
if not df.empty:
all_data_frames.append(df)
except Exception as e:
pass
if all_data_frames:
combined_df = pd.concat(all_data_frames, ignore_index=True)
final_output_path = os.path.join(download_dir, "顺心-实到货物数据.xlsx")
combined_df.to_excel(final_output_path, index=False)
print(f"====================================================")
print(f" 🎉 恭喜!处理成功!最终输出路径: {final_output_path}")
print(f"====================================================")
for file_path in downloaded_files:
os.remove(file_path)
print("✅ 临时数据清理完毕。")
# 9. 关闭【数据导出】标签页(该页面工作已完成)
_close_tab(page, "数据导出")
print("\n🎉 【顺心 - 实到货物数据下载】全流程测试完毕!")
except Exception as e:
print(f"\n❌ 任务执行过程中发生异常: {e}")