Files
InboundVerify/main_router.py
Misaka 358b8327b9 Implement ZTO actual-arrival download and extract shared poll engine
- Implement zto_actual_download via the 到件扫描监控 menu with 子单
  number-type filter, reusing the common download flow
- Extract the export-task poll/verify/download/merge steps into
  _zto_poll_and_download_tasks so both ZTO flows share it
- Tighten task time-match window from 120s to 60s and add a
  refresh-and-recheck loop when expected tasks are missing or pending
- Drop unused _ensure_menu_expanded helper
- Update menu [6] label since actual-arrival is now implemented

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-20 13:48:28 +08:00

291 lines
13 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
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.
# main_router.py
import os
import yaml
import pandas as pd
from playwright.sync_api import sync_playwright
# 导入抽离出去的各个网点模块
import site_shunxin
import site_baishi
import site_zto
# 定义网点及对应的初始登录 URL
SITES_CONFIG = {
"顺心": "https://sxne.sxjdfreight.com",
"百世": "https://v5.800best.com",
"中通": "https://ws.zto56.com/",
}
def task_process_undelivered_data(site_name="顺心"):
"""全局模块:应到未到异常件比对引擎 (支持动态网点前缀)"""
print(f"\n▶ 开始执行【{site_name} - 应到未到数据处理】任务...")
download_dir = os.path.join(os.getcwd(), "downloads")
expected_path = os.path.join(download_dir, f"{site_name}-应到货物数据.xlsx")
actual_path = os.path.join(download_dir, f"{site_name}-实到货物数据.xlsx")
output_path = os.path.join(download_dir, f"{site_name}-应到未到货物数据.xlsx")
if not os.path.exists(expected_path):
print(f"❌ 错误:找不到【{site_name}-应到货物数据】主文档:{expected_path}")
return
if not os.path.exists(actual_path):
print(f"❌ 错误:找不到【{site_name}-实到货物数据】主文档:{actual_path}")
return
try:
print(">> 正在载入本地 Excel 文档...")
df_expected = pd.read_excel(expected_path)
df_actual = pd.read_excel(actual_path)
if "运单号" not in df_expected.columns or "运单号" not in df_actual.columns:
print("❌ 核心资产校验失败:数据源中缺失【运单号】字段,请检查导出配置。")
return
print(">> 正在启动多维数据集比对引擎...")
# Left Anti-Join在应到中找出不存在于实到里的运单号
df_undelivered = df_expected[~df_expected["运单号"].isin(df_actual["运单号"])]
target_columns = ["班次号", "交接单号", "运单号"]
available_columns = [
col for col in target_columns if col in df_undelivered.columns
]
df_output = df_undelivered[available_columns]
print(f">> 筛选完毕!共捕获到异常【应到未到】货物数据: {len(df_output)} 条。")
df_output.to_excel(output_path, index=False)
print(f"====================================================")
print(f" 🎉 异常比对流完成!独立数据已安全输出。")
print(f" 📁 成果归档路径: {output_path}")
print(f"====================================================")
except Exception as e:
print(f"❌ 数据处理引擎在执行连接和输出时发生致命异常: {e}")
def run_multi_site_daemon():
"""多网点自动化主控引擎"""
# 1. 读取配置文件
debug_mode = False
debug_target = ""
try:
if os.path.exists("config.yaml"):
with open("config.yaml", "r", encoding="utf-8") as f:
config = yaml.safe_load(f)
debug_mode = config.get("debug", {}).get("enabled", False)
debug_target = config.get("debug", {}).get("target_site", "")
except Exception as e:
print(f"⚠️ 读取 config.yaml 异常,将使用全量模式启动: {e}")
# 动态确定需要启动的站点
active_sites = {}
if debug_mode and debug_target in SITES_CONFIG:
print(f"\n🛠️ 【调试模式激活】当前仅挂载并启动目标站点: [{debug_target}]")
active_sites = {debug_target: SITES_CONFIG[debug_target]}
else:
active_sites = SITES_CONFIG
with sync_playwright() as p:
browser = p.chromium.launch(headless=False)
context = browser.new_context(viewport={"width": 1920, "height": 1080})
pages_map = {}
print("\n====================================================")
print("【初始化阶段】正在构建多网点运行环境...")
print("====================================================")
for site_name, url in active_sites.items():
print(f">> 正在打开【{site_name}】页面: {url}")
page = context.new_page()
page.goto(url)
pages_map[site_name] = page
page.wait_for_timeout(1000)
print("\n====================================================")
print("⚠️ 【等待人工介入】")
print("请在弹出的浏览器中,人工完成已加载网点的登录!")
print("====================================================")
input(">> 登录全部完成后,请在此处按下【回车键】正式接管中控台...")
print("\n====================================================")
print("【系统接管】正在执行各网点就绪前初始化动作...")
print("====================================================")
# 顺心环境净化
if "顺心" in pages_map:
try:
sx_page = pages_map["顺心"]
sx_page.bring_to_front()
print(">> 正在处理【顺心】网点初始状态...")
sx_page.wait_for_selector('h1:has-text("盟商门户网")', timeout=30000)
print(" 🎉 登录成功!系统已接管顺心浏览器。")
sx_page.wait_for_timeout(1000)
sx_page.locator("a").nth(4).click()
sx_page.wait_for_timeout(1000)
sx_page.get_by_role("button", name="Close").click()
sx_page.wait_for_timeout(1000)
sx_page.get_by_role("button", name="不再询问").click()
sx_page.wait_for_timeout(1000)
print(" ✅ 【顺心】弹窗清理完毕,状态就绪!")
except Exception as e:
print(f" ⚠️ 【顺心】初始化或弹窗清理异常 (如无弹窗可忽略): {e}")
# 百世环境检测与“打地鼠”清理引擎
if "百世" in pages_map:
try:
bs_page = pages_map["百世"]
bs_page.bring_to_front()
print("\n>> 正在处理【百世】网点初始状态...")
bs_page.wait_for_selector('h1[title="百世快运"]', timeout=30000)
print(" 🎉 登录成功!系统已接管百世浏览器。")
print(" >> 启动无序弹窗清理机制 (打地鼠模式)...")
bs_page.wait_for_timeout(2000)
for round_idx in range(5):
handled_any = False
try:
read_btns = bs_page.locator("button:has-text('阅读完毕')")
if read_btns.count() > 0:
for i in range(read_btns.count()):
if read_btns.nth(i).is_visible():
read_btns.nth(i).click()
bs_page.wait_for_timeout(500)
handled_any = True
except Exception:
pass
try:
close_config = bs_page.locator("button:has-text('关 闭')")
if close_config.is_visible():
close_config.click()
bs_page.wait_for_timeout(500)
handled_any = True
except Exception:
pass
try:
notice_close = bs_page.locator(
"a.ant-notification-notice-close"
)
if notice_close.is_visible():
notice_close.click()
bs_page.wait_for_timeout(500)
handled_any = True
except Exception:
pass
try:
ad_close = bs_page.locator("svg[data-icon='close-circle']")
if ad_close.is_visible():
ad_close.click()
bs_page.wait_for_timeout(500)
handled_any = True
except Exception:
pass
if not handled_any:
break
bs_page.wait_for_timeout(1000)
print(" ✅ 【百世】界面净化完毕,状态就绪!")
except Exception as e:
print(f" ⚠️ 【百世】初始化异常: {e}")
# 中通环境检测
if "中通" in pages_map:
try:
zto_page = pages_map["中通"]
zto_page.bring_to_front()
print("\n>> 正在处理【中通】网点初始状态...")
zto_page.wait_for_selector('.logo:has-text("网点版")', timeout=30000)
print(" 🎉 登录成功!系统已接管中通浏览器。")
print(" ✅ 【中通】状态就绪!")
except Exception as e:
print(f" ⚠️ 【中通】初始化异常: {e}")
# 路由拦截器
def is_site_ready(site_name):
if site_name not in pages_map:
print(
f"\n🚫 【拒绝执行】当前处于单站点调试模式,或【{site_name}】未加载,无法执行此操作!"
)
return False
return True
while True:
print("\n====================================================")
print(" 物流数据多端提取总枢纽 ")
if debug_mode:
print(f" [ 🛠️ 调试模式已激活 : {debug_target} ]")
print("====================================================")
print(" 模块一:【顺心】数据处理流")
print(" [1] 执行 - 应到货物数据下载")
print(" [2] 执行 - 实到货物数据下载")
print(" [3] 执行 - 应到未到数据清洗比对 (本地运算)")
print("-" * 52)
print(" 模块二:【百世】数据处理流")
print(" [4] 执行 - 一键提取应到未到异常数据")
print("-" * 52)
print(" 模块三:【中通】数据处理流")
print(" [5] 执行 - 应到货物数据下载")
print(" [6] 执行 - 实到货物数据下载")
print("-" * 52)
print(" 全局数据引擎")
print(" [9] 执行 - 离线异常数据清洗比对 (Left Anti-Join)")
print("-" * 52)
print(" [0] 退出系统")
print("====================================================")
choice = input("请输入任务编号并回车: ")
try:
if choice == "1" and is_site_ready("顺心"):
pages_map["顺心"].bring_to_front()
site_shunxin.shunxin_expected_download(pages_map["顺心"])
elif choice == "2" and is_site_ready("顺心"):
pages_map["顺心"].bring_to_front()
site_shunxin.shunxin_actual_download(pages_map["顺心"])
elif choice == "3" and is_site_ready("顺心"):
task_process_undelivered_data("顺心")
elif choice == "4" and is_site_ready("百世"):
pages_map["百世"].bring_to_front()
site_baishi.baishi_download_undelivered_data(pages_map["百世"])
elif choice == "5" and is_site_ready("中通"):
pages_map["中通"].bring_to_front()
site_zto.zto_expected_download(pages_map["中通"])
elif choice == "6" and is_site_ready("中通"):
pages_map["中通"].bring_to_front()
site_zto.zto_actual_download(pages_map["中通"])
elif choice == "9":
site_name = input("请输入要比对的网点名称 (如 顺心/中通): ").strip()
if not site_name:
site_name = "顺心"
task_process_undelivered_data(site_name)
elif choice == "0":
print("\n准备退出程序,释放浏览器资源...")
break
else:
if choice not in ["1", "2", "3", "4", "5", "6", "9", "0"]:
print("\n⚠️ 无效输入,请重新选一下。")
except Exception as e:
print(f"❌ 调度执行异常: {e}")
browser.close()
print("系统已安全关闭。")
if __name__ == "__main__":
run_multi_site_daemon()