""" 离散备料计划清理器 - 重构版 使用依赖注入、接口抽象、职责分离设计 """ import time from playwright.sync_api import sync_playwright, TimeoutError as PlaywrightTimeoutError from interfaces.i_page_navigator import IPageNavigator from interfaces.i_material_extractor import IMaterialExtractor from interfaces.i_deletion_checker import IDeletionChecker from config.cleaner_config import CleanerConfig from config.ui_constants import UIConstants from utils.auth import login, logout from db.production_order_query import ( read_production_ids, query_production_order_numbers, ) class DiscreteMaterialCleaner: """离散备料计划清理器 - 重构版""" def __init__( self, config: CleanerConfig, navigator: IPageNavigator, extractor: IMaterialExtractor, deletion_checker: IDeletionChecker, ): """ 初始化清理器 Args: config: 清理器配置 navigator: 页面导航器 extractor: 物料信息提取器 deletion_checker: 删除判断器 """ self.config = config self.navigator = navigator self.extractor = extractor self.checker = deletion_checker def _print(self, *args, **kwargs): """打印日志(如果 verbose=True)""" if self.config.verbose: print(*args, **kwargs) def clean(self, production_id_file: str) -> None: """ 执行清理流程 Args: production_id_file: ProductionID.txt 文件路径 """ self._print(f"使用负责人 [{self.config.manager_name}] 进行数据清理") with sync_playwright() as playwright: # 1. 初始化浏览器和页面 browser, context, page, main_frame = self._initialize_browser(playwright) # 2. 导航到目标页面 page1, main_frame, inner_frame = self.navigator.navigate_to_main_page(page) self.navigator.setup_query_interface(inner_frame) # 3. 获取订单列表 order_ids = self._get_order_ids(production_id_file) # 4. 处理每个订单 for order_index, order_id in enumerate(order_ids): self._process_order(inner_frame, order_id, order_index, page1) # 5. 清理 self._cleanup(main_frame, context, browser) def _initialize_browser(self, playwright): """初始化浏览器和登录""" self._print("=" * 80) self._print("开始执行离散备料计划维护数据清理") self._print("=" * 80) return login( playwright=playwright, username=self.config.username, password=self.config.password, headless=self.config.headless, ignore_https_errors=True, ) def _get_order_ids(self, production_id_file: str): """获取订单 ID 列表""" # 读取总排号 production_ids = read_production_ids(production_id_file) self._print(f"从文件读取到 {len(production_ids)} 个总排号") # 查询数据库获取生产订单号 order_ids = query_production_order_numbers(production_ids) self._print(f"查询到 {len(order_ids)} 个生产订单号") return order_ids def _process_order(self, inner_frame, order_id: str, order_index: int, page1): """处理单个订单""" self._print( f"\n=== 开始处理第 {order_index + 1} 个订单,订单号: {order_id} ===" ) # 1. 查询订单 self._query_order(inner_frame, order_id, order_index) # 2. 调试模式暂停 if ( self.config.debug_mode and (self.config.debug_order is None or order_index == self.config.debug_order) ): self._print(f"=== 调试暂停:第 {order_index + 1} 个订单 ===") page1.pause() # 3. 导航到备料计划页面 page2, plan_frame = self.navigator.navigate_to_material_plan_page(page1) # 4. 提取订单信息 detail_count = self.extractor.extract_detail_count(plan_frame) detail_status = self.extractor.extract_detail_status(plan_frame) # 5. 检查是否需要处理 if not self._should_process_order(detail_count, detail_status, order_index): page2.close() return # 6. 进入编辑模式并展开 self._enter_edit_mode(plan_frame) # 7. 提取并处理物料 materials = self.extractor.extract_materials(plan_frame, detail_count) for material in materials: material.should_delete = self.checker.should_delete(material) # TODO: 执行实际的删除操作 page2.close() time.sleep(1) def _query_order(self, inner_frame, order_id: str, order_index: int): """查询单个订单""" # 清空文本框 textbox = inner_frame.get_by_role("textbox", name="生产订单号") textbox.fill("") # 填充订单号 textbox.fill(order_id) # 点击查询 from config.ui_constants import Selectors inner_frame.locator(Selectors.SEARCH_BTN).click() self._print(f"第 {order_index + 1} 个订单查询完成,等待加载结果...") # 等待加载完成 self.navigator.wait_for_page_loaded(inner_frame) self._print(f"第 {order_index + 1} 个订单加载完成,开始清理数据...") def _should_process_order( self, detail_count: int, detail_status: str, order_index: int ) -> bool: """判断订单是否需要处理""" if detail_count == 0: self._print(f"第 {order_index + 1} 个订单无数据需要清理,跳过...") return False elif detail_status != "审批通过": self._print(f"第 {order_index + 1} 个订单备料状态: {detail_status}") return False return True def _enter_edit_mode(self, frame): """进入编辑模式并展开物料列表""" from config.ui_constants import Selectors frame.get_by_role("button", name=Selectors.MODIFY_BUTTON).click() save_button_locator = frame.get_by_role("button", name=Selectors.SAVE_BUTTON) save_button_locator.wait_for(state="visible", timeout=10000) frame.get_by_text(Selectors.EXPAND_BUTTON).first.click() def _cleanup(self, main_frame, context, browser): """清理资源""" self._print("\n开始执行账号注销...") logout(main_frame, verbose=self.config.verbose) self._print(f"\n=== 全部完成 ===") context.close() browser.close()