""" 浏览器自动化填报 使用 Playwright 操作财务报销系统,自动完成登录、填单、上传附件等操作。 对外接口: run_bot(config, travel_info, normal_info) 启动浏览器并执行填报流程 run_bot_web(config, work_dir) Web 模式填报(从缓存加载信息) """ from pathlib import Path from typing import Any from .. import get_logger from .base import BaseBot log = get_logger("bot") def run_bot( config: dict[str, Any], headless: bool = False, work_dir: Path | None = None, travel_info: dict[str, Any] | None = None, normal_info: dict[str, Any] | None = None, ) -> None: """执行完整的浏览器填报流程,根据发票类型自动路由 Args: config: 配置字典。 headless: 是否无头模式。 work_dir: 工作目录。 travel_info: 差旅信息(由 pipeline 层提前提取并传入,非差旅时传 None)。 normal_info: 普通发票信息(由 pipeline 层提前提取并传入,非普通时传 None)。 """ if not config["username"] or not config["password"]: raise ValueError("缺少用户名或密码") if not work_dir: raise ValueError("缺少工作目录") bot = BaseBot(config, headless=headless) bot.work_dir = work_dir try: bot.launch() bot.login_portal() if travel_info is not None: log.info("处理差旅发票...") bot.navigate_to_reimburse(page_key="travel_page") bot.create_new_form() from . import travel travel.run(bot, travel_info) elif normal_info is not None: log.info("处理普通发票...") bot.navigate_to_reimburse(page_key="reimburse_page") bot.create_new_form() from . import normal normal.run(bot, normal_info) else: raise ValueError("缺少差旅信息(travel_info)和普通发票信息(normal_info),无法继续填报") except Exception as e: log.error(f"操作失败: {e}") try: bot._screenshot("error") except Exception: pass raise finally: bot.close() def run_bot_web(config: dict[str, Any], work_dir: Path) -> None: """Web 模式填报 — headless,附件从指定目录读取 Web 端的信息提取由 app.py 的管道负责,此处从缓存加载。 """ from ..doc.llm_extractor import load_cache cache_map = load_cache(work_dir) travel_info = cache_map.get("travel_info") normal_info = cache_map.get("normal_info") run_bot( config, headless=True, work_dir=work_dir, travel_info=travel_info, normal_info=normal_info, )