Files
playwrite/docs/MAIN_CLEAN_EXECUTION.md
Misaka_Company 5024384c73 refactor: restructure main_clean.py with dependency injection and interfaces
Refactor discrete material cleaner to improve maintainability and testability:

- Introduce dependency injection pattern for service components
- Add interfaces (IPageNavigator, IMaterialExtractor, IDeletionChecker)
- Extract PageNavigator service for page navigation logic
- Extract MaterialExtractor service for data extraction
- Extract DatabaseDeletionChecker service for deletion logic
- Add MaterialInfo data model for structured data
- Centralize configuration (CleanerConfig, UIConstants)
- Implement separation of concerns across services layer

Also includes comprehensive execution mechanism documentation.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-10 14:17:37 +08:00

802 lines
22 KiB
Markdown
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_clean.py 运行机制文档
## 1. 概述
### 1.1 系统简介
**离散备料计划维护数据清理工具** 是一个基于 Python + Playwright 开发的自动化工具,用于清理 YonBIP用友ERP 系统中的备料数据。该工具通过浏览器自动化技术访问 ERP 系统,提取生产订单的备料信息,并根据数据库查询结果判断哪些物料需要清理。
### 1.2 技术栈
- **Python 3.x**: 主要开发语言
- **Playwright**: 浏览器自动化框架Chromium
- **SQL Server**: 数据库查询pyodbc
- **数据类dataclass**: 配置和数据模型
### 1.3 主要功能
1. 自动登录 YonBIP ERP 系统
2. 读取待处理的生产订单列表
3. 遍历每个订单的备料计划
4. 提取物料详细信息(编码、名称、数量等)
5. 查询数据库判断物料是否需要删除
6. 标记需要清理的物料(预留删除接口)
---
## 2. 系统架构图
```mermaid
graph TB
subgraph "外部系统"
ERP[YonBIP ERP 系统]
DB[(SQL Server 数据库)]
FILE[ProductionID.txt]
end
subgraph "配置层 (config/)"
CONFIG[CleanerConfig<br/>清理器配置]
UICONST[UIConstants<br/>UI 常量配置]
UICONST -.-> SELECTORS[Selectors<br/>选择器]
UICONST -.-> TIMEOUTS[Timeouts<br/>超时配置]
UICONST -.-> RETRY[RetryConfig<br/>重试配置]
end
subgraph "服务层 (services/)"
NAVIGATOR[PageNavigator<br/>页面导航服务]
EXTRACTOR[MaterialExtractor<br/>物料提取服务]
CHECKER[DatabaseDeletionChecker<br/>删除判断服务]
end
subgraph "业务逻辑层 (utils/)"
CLEANER[DiscreteMaterialCleaner<br/>离散备料清理器]
end
subgraph "数据层 (models/)"
MATERIAL[MaterialInfo<br/>物料信息模型]
end
subgraph "接口层 (interfaces/)"
I1[IPageNavigator]
I2[IMaterialExtractor]
I3[IDeletionChecker]
end
FILE --> CLEANER
CLEANER --> NAVIGATOR
CLEANER --> EXTRACTOR
CLEANER --> CHECKER
CLEANER --> CONFIG
CLEANER --> UICONST
NAVIGATOR -.实现.-> I1
EXTRACTOR -.实现.-> I2
CHECKER -.实现.-> I3
NAVIGATOR --> ERP
EXTRACTOR --> ERP
EXTRACTOR --> MATERIAL
CHECKER --> DB
CHECKER --> MATERIAL
style ERP fill:#ff9999
style DB fill:#99ccff
style FILE fill:#99ff99
style CLEANER fill:#ffcc99
```
---
## 3. 主执行流程图
```mermaid
flowchart TD
START([开始: main函数]) --> INIT_CONFIG[创建 CleanerConfig<br/>配置用户名、密码、负责人等]
INIT_CONFIG --> INIT_UI[创建 UIConstants<br/>选择器、超时、重试配置]
INIT_UI --> DEP_INJ[依赖注入<br/>创建服务实例]
DEP_INJ --> CREATE_NAV[创建 PageNavigator<br/>页面导航服务]
DEP_INJ --> CREATE_EXT[创建 MaterialExtractor<br/>物料提取服务]
DEP_INJ --> CREATE_CHK[创建 DatabaseDeletionChecker<br/>删除判断服务]
CREATE_NAV --> CREATE_CLEANER[创建 DiscreteMaterialCleaner<br/>注入所有服务]
CREATE_CHK --> CREATE_CLEANER
CREATE_EXT --> CREATE_CLEANER
CREATE_CLEANER --> READ_FILE[读取 ProductionID.txt<br/>获取生产总排号列表]
READ_FILE --> CALL_CLEAN[调用 cleaner.clean方法]
CALL_CLEAN --> PW_START[启动 Playwright]
PW_START --> LOGIN[登录 ERP 系统<br/>获取 browser, context, page]
LOGIN --> NAV_MAIN[导航到主页面<br/>获取 nested iframe]
NAV_MAIN --> SETUP_QUERY[设置查询界面<br/>打开订单号查询]
SETUP_QUERY --> READ_IDS[读取总排号]
READ_IDS --> QUERY_DB[查询数据库<br/>获取生产订单号列表]
QUERY_DB --> ORDER_LOOP{遍历订单列表}
ORDER_LOOP --> QUERY_ORDER[查询订单<br/>填充订单号并点击查询]
QUERY_ORDER --> WAIT_LOAD[等待页面加载完成]
WAIT_LOAD --> DEBUG_CHECK{调试模式?}
DEBUG_CHECK -->|是| PAUSE[暂停执行<br/>等待调试]
DEBUG_CHECK -->|否| NAV_PLAN
PAUSE --> NAV_PLAN[导航到备料计划页面]
NAV_PLAN --> EXTRACT_COUNT[提取详细信息数量]
EXTRACT_COUNT --> EXTRACT_STATUS[提取备料状态]
EXTRACT_STATUS --> SHOULD_PROC{需要处理?}
SHOULD_PROC -->|数量=0| SKIP[跳过订单]
SHOULD_PROC -->|状态≠审批通过| SKIP
SHOULD_PROC -->|是| ENTER_EDIT[进入编辑模式<br/>点击修改按钮]
ENTER_EDIT --> EXPAND[展开物料列表<br/>点击展开按钮]
EXPAND --> EXT_MATERIALS[提取所有物料信息<br/>遍历物料列表]
EXT_MATERIALS --> MAT_LOOP{遍历物料}
MAT_LOOP --> CHECK_DELETE[查询数据库<br/>判断是否需要删除]
CHECK_DELETE --> MARK[标记物料<br/>设置 should_delete]
MARK --> MAT_LOOP
MAT_LOOP -->|完成| CLOSE_PAGE[关闭当前页面]
CLOSE_PAGE --> SLEEP[等待 1 秒]
SKIP --> ORDER_LOOP
SLEEP --> ORDER_LOOP
ORDER_LOOP -->|完成| LOGOUT[注销登录]
LOGOUT --> CLOSE_ALL[关闭浏览器上下文]
CLOSE_ALL --> END([结束])
style START fill:#90EE90
style END fill:#FFB6C1
style ORDER_LOOP fill:#FFE4B5
style MAT_LOOP fill:#FFE4B5
style SHOULD_PROC fill:#FFD700
```
---
## 4. 组件交互时序图
```mermaid
sequenceDiagram
participant Main as main()
participant Cleaner as DiscreteMaterialCleaner
participant Nav as PageNavigator
participant Ext as MaterialExtractor
participant Checker as DatabaseDeletionChecker
participant ERP as YonBIP ERP
participant DB as SQL Server
Main->>Cleaner: clean(production_id_file)
activate Cleaner
Cleaner->>Cleaner: _initialize_browser()
Note over Cleaner: 启动 Playwright 并登录
Cleaner->>Nav: navigate_to_main_page(page)
activate Nav
Nav->>ERP: 点击功能菜单
Nav->>ERP: 打开"离散生产订单维护"
Nav-->>Cleaner: (page1, main_frame, inner_frame)
deactivate Nav
Cleaner->>Nav: setup_query_interface(inner_frame)
activate Nav
Nav->>ERP: 打开订单号查询界面
Nav->>ERP: 设置显示数量为 5000
deactivate Nav
Cleaner->>Cleaner: _get_order_ids(production_id_file)
Note over Cleaner: 读取文件并查询数据库
loop 遍历每个订单
Cleaner->>Cleaner: _query_order(inner_frame, order_id)
Note over Cleaner: 填充订单号并点击查询
Cleaner->>Nav: wait_for_page_loaded(inner_frame)
activate Nav
Nav->>ERP: 等待"加载中"消失
deactivate Nav
alt 调试模式
Cleaner->>ERP: pause() 调试暂停
end
Cleaner->>Nav: navigate_to_material_plan_page(page1)
activate Nav
Nav->>ERP: 点击"更多" → "备料计划"
Nav->>ERP: 等待页面加载完成
Nav-->>Cleaner: (page2, plan_frame)
deactivate Nav
Cleaner->>Ext: extract_detail_count(plan_frame)
activate Ext
Ext->>ERP: 提取详细信息数量
Ext-->>Cleaner: detail_count
deactivate Ext
Cleaner->>Ext: extract_detail_status(plan_frame)
activate Ext
Ext->>ERP: 提取备料状态
Ext-->>Cleaner: detail_status
deactivate Ext
alt 需要处理订单
Cleaner->>Cleaner: _enter_edit_mode(plan_frame)
Note over Cleaner: 点击修改、展开按钮
Cleaner->>Ext: extract_materials(plan_frame, count)
activate Ext
loop 遍历物料
Ext->>ERP: 提取物料编码
Ext->>ERP: 提取物料名称
Ext->>ERP: 提取待发数量
Ext->>ERP: 提取出库数量
end
Ext-->>Cleaner: materials (List[MaterialInfo])
deactivate Ext
loop 遍历每个物料
Cleaner->>Checker: should_delete(material)
activate Checker
Checker->>DB: 查询物料负责人
DB-->>Checker: 查询结果
Checker-->>Cleaner: True/False
deactivate Checker
Cleaner->>Cleaner: material.should_delete = result
end
Cleaner->>ERP: 关闭页面
else 不需要处理
Cleaner->>ERP: 关闭页面
end
end
Cleaner->>Cleaner: _cleanup()
Cleaner->>ERP: 注销登录
Cleaner->>Cleaner: 关闭浏览器
Cleaner-->>Main: 完成
deactivate Cleaner
```
---
## 5. 组件详细说明
### 5.1 DiscreteMaterialCleaner (utils/discrete_material_cleaner.py)
**职责**: 核心业务逻辑编排器,协调整个清理流程
**主要方法**:
- `clean(production_id_file)` - 执行完整清理流程
- `_initialize_browser(playwright)` - 初始化浏览器并登录
- `_get_order_ids(production_id_file)` - 获取待处理订单列表
- `_process_order(inner_frame, order_id, order_index, page1)` - 处理单个订单
- `_query_order(inner_frame, order_id, order_index)` - 查询订单
- `_should_process_order(detail_count, detail_status, order_index)` - 判断是否需要处理
- `_enter_edit_mode(frame)` - 进入编辑模式
- `_cleanup(main_frame, context, browser)` - 清理资源
**依赖注入**:
- `navigator: IPageNavigator` - 页面导航
- `extractor: IMaterialExtractor` - 物料提取
- `deletion_checker: IDeletionChecker` - 删除判断
---
### 5.2 PageNavigator (services/page_navigator.py)
**职责**: 处理所有页面导航和 iframe 操作
**主要方法**:
- `navigate_to_main_page(page)` - 导航到主页面
- `navigate_to_order_page(main_frame, page)` - 导航到订单页面
- `navigate_to_material_plan_page(page)` - 导航到备料计划页面
- `setup_query_interface(frame)` - 设置查询界面
- `wait_for_page_loaded(frame)` - 等待页面加载完成
**关键特性**:
- 处理嵌套 iframe 结构 (#forwardFrame#mainiframe)
- 等待加载状态("加载中"提示)
- 使用 expect_popup() 处理新窗口
---
### 5.3 MaterialExtractor (services/material_extractor.py)
**职责**: 从页面提取物料信息
**主要方法**:
- `extract_detail_count(frame)` - 提取详细信息数量
- `extract_detail_status(frame)` - 提取备料状态
- `extract_materials(frame, count)` - 提取所有物料信息
- `_extract_material_code(child_form)` - 提取物料编码
- `_extract_material_name(child_form)` - 提取物料名称
- `_extract_pending_quantity(child_form)` - 提取待发数量
- `_extract_shipped_quantity(child_form)` - 提取出库数量
- `_navigate_to_next_material(child_form, current_index, total_count)` - 导航到下一个物料
**数据返回**: `List[MaterialInfo]`
---
### 5.4 DatabaseDeletionChecker (services/deletion_checker.py)
**职责**: 基于数据库查询判断物料是否需要删除
**主要方法**:
- `should_delete(material: MaterialInfo) -> bool` - 判断是否需要删除
**逻辑**:
- 查询数据库中物料对应的负责人
- 与配置的 `manager_name` 进行比对
- 返回是否需要删除的布尔值
---
### 5.5 CleanerConfig (config/cleaner_config.py)
**职责**: 管理清理器运行配置
**属性**:
- `username: str` - ERP 登录用户名
- `password: str` - ERP 登录密码
- `manager_name: str` - 负责人姓名(用于删除判断)
- `headless: bool` - 是否无头模式运行浏览器
- `verbose: bool` - 是否打印详细日志
- `debug_mode: bool` - 是否启用调试模式
- `debug_order: Optional[int]` - 调试的订单索引
---
### 5.6 UIConstants (config/ui_constants.py)
**职责**: 集中管理所有 UI 相关常量
**包含三个嵌套数据类**:
1. **Selectors** - CSS 选择器
- 搜索相关: `SEARCH_BTN`, `SEARCH_ICON`, `QUERY_BY_ORDER`
- iframe: `FORWARD_FRAME`, `MAIN_IFRAME`
- 按钮: `MODIFY_BUTTON`, `SAVE_BUTTON`, `EXPAND_BUTTON`
2. **Timeouts** - 超时配置(毫秒)
- 页面加载: `IFRAME_VISIBLE`, `PLAN_CODE_VISIBLE`
- 加载等待: `LOADING_VISIBLE_WAIT`, `LOADING_HIDDEN_WAIT`
- 轮询: `PLAN_CODE_MAX_WAIT`, `PLAN_CHECK_INTERVAL`
3. **RetryConfig** - 重试配置
- `DISPLAY_COUNT_MAX_RETRIES` - 最大重试次数
- `EXPECTED_DISPLAY_COUNT` - 期望显示数量
---
### 5.7 MaterialInfo (models/material_info.py)
**职责**: 物料信息数据模型
**属性**:
- `serial_number: int` - 序号
- `code: str` - 物料编码
- `name: str` - 物料名称
- `pending_quantity: str` - 累计待发数量
- `shipped_quantity: str` - 累计出库数量
- `should_delete: bool` - 是否需要删除(默认 False
---
## 6. 配置类结构图
```mermaid
classDiagram
class CleanerConfig {
+str username
+str password
+str manager_name
+bool headless
+bool verbose
+bool debug_mode
+Optional~int~ debug_order
}
class UIConstants {
+Selectors selectors
+Timeouts timeouts
+RetryConfig retry
}
class Selectors {
+str SEARCH_BTN
+str SEARCH_ICON
+str QUERY_BY_ORDER
+str TAB_ALL
+str HOT_KEY_HEAD
+str MORE_BUTTON
+str MATERIAL_PLAN
+str FORWARD_FRAME
+str MAIN_IFRAME
+str MODIFY_BUTTON
+str SAVE_BUTTON
+str EXPAND_BUTTON
+str CARD_TABLE_SIDE_BOX
}
class Timeouts {
+int IFRAME_VISIBLE
+int PLAN_CODE_VISIBLE
+int BUTTON_VISIBLE
+int SERIAL_VISIBLE
+int FORM_VISIBLE
+int LOADING_VISIBLE_WAIT
+int LOADING_HIDDEN_WAIT
+int PLAN_CODE_MAX_WAIT
+float PLAN_CHECK_INTERVAL
}
class RetryConfig {
+int DISPLAY_COUNT_MAX_RETRIES
+str EXPECTED_DISPLAY_COUNT
}
class MaterialInfo {
+int serial_number
+str code
+str name
+str pending_quantity
+str shipped_quantity
+bool should_delete
}
UIConstants *-- Selectors
UIConstants *-- Timeouts
UIConstants *-- RetryConfig
CleanerConfig ..> UIConstants : 使用
DiscreteMaterialCleaner ..> CleanerConfig : 依赖
MaterialExtractor ..> MaterialInfo : 返回
DatabaseDeletionChecker ..> MaterialInfo : 处理
```
---
## 7. 数据流图
```mermaid
flowchart LR
subgraph "数据输入"
FILE[ProductionID.txt<br/>生产总排号列表]
CONFIG[CleanerConfig<br/>配置参数]
end
subgraph "数据处理"
READ[read_production_ids<br/>读取总排号]
QUERY[query_production_order_numbers<br/>查询数据库]
EXTRACT[extract_materials<br/>提取物料信息]
CHECK[should_delete<br/>删除判断]
end
subgraph "数据模型"
IDs[总排号列表]
OrderIDs[生产订单号列表]
Materials[物料信息列表]
Material[单个物料信息]
end
subgraph "外部系统"
DB[(SQL Server)]
ERP[YonBIP ERP]
end
FILE --> READ
READ --> IDs
IDs --> QUERY
QUERY --> DB
DB --> OrderIDs
OrderIDs --> ERP
ERP --> EXTRACT
EXTRACT --> Materials
Materials --> Material
Material --> CHECK
CHECK --> DB
style FILE fill:#99ff99
style DB fill:#99ccff
style ERP fill:#ff9999
style Materials fill:#ffcc99
```
---
## 8. 单个订单处理流程
```mermaid
flowchart TD
START([开始处理订单]) --> QUERY[填充订单号]
QUERY --> CLICK_SEARCH[点击查询按钮]
CLICK_SEARCH --> WAIT_LOADING[等待加载完成]
WAIT_LOADING --> NAV_PLAN[导航到备料计划页面]
NAV_PLAN --> WAIT_PLAN[等待备料计划页面加载]
WAIT_PLAN --> EXT_COUNT[提取详细信息数量]
EXT_COUNT --> EXT_STATUS[提取备料状态]
EXT_STATUS --> CHECK_COUNT{数量 > 0?}
CHECK_COUNT -->|否| SKIP_CLOSE[关闭页面]
CHECK_COUNT -->|是| CHECK_STATUS{状态 = 审批通过?}
CHECK_STATUS -->|否| SKIP_CLOSE
CHECK_STATUS -->|是| CLICK_MODIFY[点击修改按钮]
CLICK_MODIFY --> WAIT_SAVE[等待保存按钮可见]
WAIT_SAVE --> CLICK_EXPAND[点击展开按钮]
CLICK_EXPAND --> EXT_LOOP{遍历物料}
EXT_LOOP --> FIND_SERIAL[查找序号元素]
FIND_SERIAL --> EXT_CODE[提取物料编码]
EXT_CODE --> EXT_NAME[提取物料名称]
EXT_NAME --> EXT_PENDING[提取待发数量]
EXT_PENDING --> EXT_SHIPPED[提取出库数量]
EXT_SHIPPED --> CREATE_OBJ[创建 MaterialInfo 对象]
CREATE_OBJ --> CHECK_DELETE[查询数据库判断删除]
CHECK_DELETE --> MARK_DELETE[标记 should_delete]
MARK_DELETE --> NEXT_MAT{还有下一个?}
NEXT_MAT -->|是| CLICK_NEXT[点击下一个按钮]
CLICK_NEXT --> EXT_LOOP
NEXT_MAT -->|否| CLICK_LAST[点击最后一个按钮]
CLICK_LAST --> ADD_LIST[添加到物料列表]
ADD_LIST --> CLOSE_PAGE[关闭页面]
SKIP_CLOSE --> END([结束])
CLOSE_PAGE --> END
style START fill:#90EE90
style END fill:#FFB6C1
style EXT_LOOP fill:#FFE4B5
style CHECK_COUNT fill:#FFD700
style CHECK_STATUS fill:#FFD700
```
---
## 9. 关键设计模式
### 9.1 依赖注入模式
`DiscreteMaterialCleaner` 通过构造函数注入所有依赖服务,而不是直接创建实例:
```python
cleaner = DiscreteMaterialCleaner(
config=config,
navigator=navigator,
extractor=extractor,
deletion_checker=deletion_checker,
)
```
**优点**:
- 降低耦合度
- 方便单元测试(可注入 Mock 对象)
- 灵活替换实现
---
### 9.2 接口隔离原则
通过抽象接口定义服务契约:
- `IPageNavigator` - 页面导航接口
- `IMaterialExtractor` - 物料提取接口
- `IDeletionChecker` - 删除判断接口
**优点**:
- 职责分离清晰
- 实现可替换
- 便于扩展新功能
---
### 9.3 策略模式
`IDeletionChecker` 接口允许多种删除判断策略:
- `DatabaseDeletionChecker` - 基于数据库查询
- 可扩展其他策略(如基于规则、基于 API 等)
---
## 10. 嵌套 iframe 处理机制
YonBIP ERP 系统使用嵌套 iframe 结构,需要特殊处理:
```
page (浏览器上下文)
└── #forwardFrame (外层 iframe)
└── #mainiframe (内层 iframe包含实际应用 UI)
```
**处理模式**:
```python
outer_frame = page.locator("#forwardFrame").content_frame
inner_frame_locator = outer_frame.locator("#mainiframe")
inner_frame_locator.wait_for(state="visible", timeout=15000)
inner_frame = inner_frame_locator.content_frame
```
**关键点**:
- 必须等待内层 iframe 可见后再获取其 content_frame
- 操作前确保元素已加载完成
- 注意不同页面的 iframe 可能需要重新获取
---
## 11. 加载状态处理
ERP 系统使用"加载中"提示表示页面正在加载数据:
```python
loading_locator = frame.locator("div").filter(has_text="加载中").nth(1)
try:
loading_locator.wait_for(state="visible", timeout=3000)
loading_locator.wait_for(state="hidden", timeout=0) # 无限等待
except PlaywrightTimeoutError:
# 加载很快完成或未出现
pass
```
**处理逻辑**:
1. 先尝试等待"加载中"出现
2. 然后无限等待直到消失
3. 如果超时则认为加载已完成
---
## 12. 运行示例
```bash
# 激活虚拟环境
.venv\Scripts\activate
# 运行清理脚本
python main_clean.py
```
**输出示例**:
```
================================================================================
开始执行离散备料计划维护数据清理
================================================================================
使用负责人 [彭羽] 进行数据清理
从文件读取到 10 个总排号
查询到 10 个生产订单号
=== 开始处理第 1 个订单,订单号: PO20250101001 ===
第 1 个订单查询完成,等待加载结果...
第 1 个订单加载完成,开始清理数据...
等待备料计划页面加载完成...
备料计划页面加载完成,编码: BL20250101001
详细信息数量: 5
备料状态: 审批通过
处理 序号 1
材料编码: 00000001001
材料名称: 原材料A
累计待发数量: 100
累计出库数量: 0
>>> 需要清理原材料A【00000001001】
...
开始执行账号注销...
=== 全部完成 ===
```
---
## 13. 扩展与维护
### 13.1 添加新的删除判断策略
实现 `IDeletionChecker` 接口:
```python
class RuleBasedDeletionChecker(IDeletionChecker):
def should_delete(self, material: MaterialInfo) -> bool:
# 基于规则的判断逻辑
return material.name.startswith("测试")
```
### 13.2 修改配置参数
编辑 `main_clean.py` 中的 `CleanerConfig` 初始化:
```python
config = CleanerConfig(
username="your_username",
password="your_password",
manager_name="新的负责人",
headless=True, # 无头模式
verbose=False, # 关闭详细日志
)
```
### 13.3 调试特定订单
启用调试模式:
```python
config = CleanerConfig(
debug_mode=True,
debug_order=0, # 调试第一个订单
)
```
---
## 14. 常见问题
### Q1: 页面加载超时
**A**: 检查网络连接,调整 `UIConstants.Timeouts` 中的超时配置
### Q2: iframe 未找到
**A**: 确认 ERP 系统版本未变化,检查 `Selectors.FORWARD_FRAME``Selectors.MAIN_IFRAME`
### Q3: 数据库查询失败
**A**: 检查 `db/materials_to_delete.py` 中的 SQL 连接配置
### Q4: 物料信息提取错误
**A**: 使用 `debug_mode=True` 暂停执行,检查页面元素选择器
---
## 附录 A: 文件路径速查
| 文件 | 路径 |
|------|------|
| 主入口 | `main_clean.py` |
| 核心业务逻辑 | `utils/discrete_material_cleaner.py` |
| 页面导航服务 | `services/page_navigator.py` |
| 物料提取服务 | `services/material_extractor.py` |
| 删除判断服务 | `services/deletion_checker.py` |
| 清理器配置 | `config/cleaner_config.py` |
| UI 常量配置 | `config/ui_constants.py` |
| 物料信息模型 | `models/material_info.py` |
| 页面导航接口 | `interfaces/i_page_navigator.py` |
| 物料提取接口 | `interfaces/i_material_extractor.py` |
| 删除判断接口 | `interfaces/i_deletion_checker.py` |
---
## 附录 B: 接口定义摘要
### IPageNavigator
```python
def navigate_to_main_page(page: Page) -> Frame
def navigate_to_order_page(main_frame: Frame, page: Page) -> Tuple[Page, Frame]
def navigate_to_material_plan_page(page: Page) -> Tuple[Page, Frame]
def setup_query_interface(frame: Frame) -> None
def wait_for_page_loaded(frame: Frame) -> bool
```
### IMaterialExtractor
```python
def extract_detail_count(frame: Frame) -> int
def extract_detail_status(frame: Frame) -> str
def extract_materials(frame: Frame, count: int) -> List[MaterialInfo]
```
### IDeletionChecker
```python
def should_delete(material: MaterialInfo) -> bool
```
---
**文档版本**: 1.0
**最后更新**: 2026-02-10
**维护者**: Claude Code