docs: update extractor-start-button-flow.md for database architecture changes

- Update version to 1.2
- Add database factory pattern documentation
- Add IDatabaseService interface documentation
- Update architecture diagram with Database Layer (MySQL/SQL Server)
- Update sequence diagram with DatabaseFactory and IDatabaseService
- Fix Mermaid syntax (replace <br/> with multiline strings)
- Update error handling flowchart for dual database support
- Add DB_TYPE environment variable documentation

Co-Authored-By: Claude (glm-5) <noreply@anthropic.com>
This commit is contained in:
Misaka
2026-03-03 20:55:56 +08:00
parent 0040ca7521
commit 00b120c762

View File

@@ -1,6 +1,6 @@
# 数据提取界面 - 开始按钮工作流程详解 # 数据提取界面 - 开始按钮工作流程详解
> **文档版本**: 1.1 > **文档版本**: 1.2
> **更新日期**: 2026-03-03 > **更新日期**: 2026-03-03
> **适用范围**: ERPAuto v1.0+ > **适用范围**: ERPAuto v1.0+
> **相关文件**: > **相关文件**:
@@ -10,6 +10,8 @@
> - `src/main/services/erp/extractor.ts` (业务逻辑层) > - `src/main/services/erp/extractor.ts` (业务逻辑层)
> - `src/main/services/erp/order-resolver.ts` (订单号解析服务) > - `src/main/services/erp/order-resolver.ts` (订单号解析服务)
> - `src/main/services/erp/erp-auth.ts` (ERP认证服务) > - `src/main/services/erp/erp-auth.ts` (ERP认证服务)
> - `src/main/services/database/index.ts` (数据库工厂)
> - `src/main/types/database.types.ts` (数据库类型定义)
> - `src/main/types/extractor.types.ts` (类型定义) > - `src/main/types/extractor.types.ts` (类型定义)
## 目录 ## 目录
@@ -47,8 +49,16 @@ graph TB
J[ExtractorService] J[ExtractorService]
end end
subgraph "Database Layer (数据库层)"
N["DatabaseFactory
create()"]
O["IDatabaseService
统一接口"]
P1[(MySQL)]
P2[(SQL Server)]
end
subgraph "External Services (外部服务)" subgraph "External Services (外部服务)"
K[(MySQL Database)]
L[ERP Web System] L[ERP Web System]
M[Playwright Browser] M[Playwright Browser]
end end
@@ -58,7 +68,10 @@ graph TB
E -->|IPC通信| F E -->|IPC通信| F
F -->|接收请求| G F -->|接收请求| G
G -->|解析订单号| H G -->|解析订单号| H
H -->|查询数据| K H -->|创建服务| N
N -->|返回实例| O
O -->|查询数据| P1
O -->|查询数据| P2
G -->|登录认证| I G -->|登录认证| I
I -->|自动化操作| M I -->|自动化操作| M
M -->|访问页面| L M -->|访问页面| L
@@ -71,7 +84,8 @@ graph TB
style A fill:#e1f5ff style A fill:#e1f5ff
style G fill:#fff4e1 style G fill:#fff4e1
style K fill:#e8f5e9 style N fill:#e8f5e9
style O fill:#e8f5e9
style L fill:#f3e5f5 style L fill:#f3e5f5
``` ```
@@ -80,7 +94,11 @@ graph TB
- **Renderer Process**: 负责UI展示和用户交互使用React管理状态 - **Renderer Process**: 负责UI展示和用户交互使用React管理状态
- **IPC Bridge**: 安全的进程间通信桥梁通过preload脚本暴露 - **IPC Bridge**: 安全的进程间通信桥梁通过preload脚本暴露
- **Main Process**: 处理业务逻辑、数据库操作、浏览器自动化 - **Main Process**: 处理业务逻辑、数据库操作、浏览器自动化
- **External Services**: MySQL数据库和ERP Web系统 - **Database Layer**: 数据库抽象层,通过工厂模式创建服务实例
- 支持 MySQL 和 SQL Server 双数据库
- 通过 `IDatabaseService` 统一接口操作
-`DB_TYPE` 环境变量决定使用哪种数据库
- **External Services**: ERP Web 系统
--- ---
@@ -94,7 +112,8 @@ sequenceDiagram
participant IPC as electron API participant IPC as electron API
participant Handler as extractor-handler.ts participant Handler as extractor-handler.ts
participant Resolver as OrderNumberResolver participant Resolver as OrderNumberResolver
participant MySQL as MySQL Database participant DBFactory as DatabaseFactory
participant Database as IDatabaseService
participant Auth as ErpAuthService participant Auth as ErpAuthService
participant Extractor as ExtractorService participant Extractor as ExtractorService
participant Browser as Playwright Browser participant Browser as Playwright Browser
@@ -122,17 +141,27 @@ sequenceDiagram
UI->>UI: 设置isRunning=false UI->>UI: 设置isRunning=false
end end
Note over Handler,MySQL: 订单号解析阶段 Note over Handler,Database: 数据库连接阶段
Handler->>MySQL: 11. 连接MySQL数据库 Handler->>DBFactory: 11. 调用 create()
alt MySQL连接失败 DBFactory->>DBFactory: 12. 读取 DB_TYPE 环境变量
alt DB_TYPE = mysql
DBFactory->>Database: 创建 MySqlService 实例
else DB_TYPE = sqlserver
DBFactory->>Database: 创建 SqlServerService 实例
end
Database->>Database: 13. connect()
alt 数据库连接失败
Database-->>Handler: 抛出异常
Handler-->>UI: 返回DatabaseQueryError Handler-->>UI: 返回DatabaseQueryError
end end
DBFactory-->>Handler: 14. 返回 IDatabaseService 实例
Handler->>Resolver: 12. 创建OrderNumberResolver Note over Handler,Database: 订单号解析阶段
Handler->>Resolver: 13. 调用resolve(orderNumbers) Handler->>Resolver: 15. 创建OrderNumberResolver(dbService)
Resolver->>MySQL: 14. 查询生产订单号映射 Handler->>Resolver: 16. 调用resolve(orderNumbers)
MySQL-->>Resolver: 15. 返回映射结果 Resolver->>Database: 17. 查询生产订单号映射
Resolver-->>Handler: 16. 返回映射结果<br/>(包含有效订单号和警告) Database-->>Resolver: 18. 返回映射结果
Resolver-->>Handler: 19. 返回映射结果<br/>(包含有效订单号和警告)
alt 没有有效订单号 alt 没有有效订单号
Handler-->>UI: 返回ValidationError Handler-->>UI: 返回ValidationError
@@ -140,62 +169,62 @@ sequenceDiagram
end end
Note over Handler,Browser: ERP认证阶段 Note over Handler,Browser: ERP认证阶段
Handler->>Auth: 17. 创建ErpAuthService Handler->>Auth: 20. 创建ErpAuthService
Handler->>Auth: 18. 调用login() Handler->>Auth: 21. 调用login()
Auth->>Browser: 19. 启动Playwright浏览器 Auth->>Browser: 22. 启动Playwright浏览器
Browser->>ERP: 20. 访问ERP登录页面 Browser->>ERP: 23. 访问ERP登录页面
Browser->>ERP: 21. 填写用户名密码 Browser->>ERP: 24. 填写用户名密码
Browser->>ERP: 22. 点击登录按钮 Browser->>ERP: 25. 点击登录按钮
ERP-->>Browser: 23. 登录成功 ERP-->>Browser: 26. 登录成功
Browser-->>Auth: 24. 返回session对象 Browser-->>Auth: 27. 返回session对象
Auth-->>Handler: 25. 登录成功 Auth-->>Handler: 28. 登录成功
alt 登录失败 alt 登录失败
Auth-->>Handler: 抛出异常 Auth-->>Handler: 抛出异常
Handler-->>UI: 返回ErpConnectionError Handler-->>UI: 返回ErpConnectionError
end end
Note over Extractor,ERP: 数据提取阶段 Note over Extractor,ERP: 数据提取阶段
Handler->>Extractor: 26. 创建ExtractorService Handler->>Extractor: 29. 创建ExtractorService
Handler->>Extractor: 27. 调用extract()<br/>传入有效订单号 Handler->>Extractor: 30. 调用extract()<br/>传入有效订单号
Extractor->>Browser: 28. 使用已有session Extractor->>Browser: 31. 使用已有session
Extractor->>ERP: 29. 导航到离散备料计划维护页面 Extractor->>ERP: 32. 导航到离散备料计划维护页面
Extractor->>ERP: 30. 设置查询界面<br/>(订单号查询, 全部标签, 限制5000) Extractor->>ERP: 33. 设置查询界面<br/>(订单号查询, 全部标签, 限制5000)
loop 批处理循环 (每批最多100个订单) loop 批处理循环 (每批最多100个订单)
Extractor->>Extractor: 31. 创建批次<br/>(按batchSize分组) Extractor->>Extractor: 34. 创建批次<br/>(按batchSize分组)
Note over Extractor: onProgress回调存在但<br/>无法通过IPC传递(函数不可序列化) Note over Extractor: onProgress回调存在但<br/>无法通过IPC传递(函数不可序列化)
Extractor->>ERP: 32. 填充订单号到搜索框 Extractor->>ERP: 35. 填充订单号到搜索框
Extractor->>ERP: 33. 点击搜索按钮 Extractor->>ERP: 36. 点击搜索按钮
Extractor->>ERP: 34. 等待加载完成 Extractor->>ERP: 37. 等待加载完成
Extractor->>ERP: 35. 点击第一行复选框 Extractor->>ERP: 38. 点击第一行复选框
Extractor->>ERP: 36. 悬停并点击"更多" Extractor->>ERP: 39. 悬停并点击"更多"
Extractor->>ERP: 37. 点击"输出" Extractor->>ERP: 40. 点击"输出"
Extractor->>ERP: 38. 设置行数阈值为300000 Extractor->>ERP: 41. 设置行数阈值为300000
Extractor->>ERP: 39. 点击"确定(Y)" Extractor->>ERP: 42. 点击"确定(Y)"
Browser->>Browser: 40. 监听下载事件 Browser->>Browser: 43. 监听下载事件
ERP->>Browser: 41. 触发文件下载 ERP->>Browser: 44. 触发文件下载
Browser->>Browser: 42. 保存文件到downloads目录 Browser->>Browser: 45. 保存文件到downloads目录
Browser-->>Extractor: 43. 返回文件路径 Browser-->>Extractor: 46. 返回文件路径
Extractor->>Extractor: 44. 记录下载文件路径 Extractor->>Extractor: 47. 记录下载文件路径
end end
Extractor->>Extractor: 45. 汇总结果<br/>(文件列表, 记录数, 错误) Extractor->>Extractor: 48. 汇总结果<br/>(文件列表, 记录数, 错误)
Extractor-->>Handler: 46. 返回ExtractorResult Extractor-->>Handler: 49. 返回ExtractorResult
Handler->>Handler: 47. 添加解析警告到错误列表 Handler->>Handler: 50. 添加解析警告到错误列表
Note over Handler,IPC: 清理阶段 Note over Handler,IPC: 清理阶段
Handler->>Browser: 48. 关闭浏览器 Handler->>Browser: 51. 关闭浏览器
Handler->>MySQL: 49. 断开数据库连接 Handler->>Database: 52. 断开数据库连接<br/>(dbService.disconnect())
Note over Handler,UI: 响应阶段 Note over Handler,UI: 响应阶段
Handler-->>IPC: 50. 返回IPC响应<br/>(success: true, data: result) Handler-->>IPC: 53. 返回IPC响应<br/>(success: true, data: result)
IPC-->>UI: 51. 返回response IPC-->>UI: 54. 返回response
UI->>UI: 52. 设置result状态 UI->>UI: 55. 设置result状态
UI->>UI: 53. 设置isRunning=false UI->>UI: 56. 设置isRunning=false
UI->>UI: 54. 清空进度状态 UI->>UI: 57. 清空进度状态
UI->>User: 55. 显示提取结果<br/>(文件数, 记录数, 错误数) UI->>User: 58. 显示提取结果<br/>(文件数, 记录数, 错误数)
alt 发生任何错误 alt 发生任何错误
Handler-->>UI: 返回error响应 Handler-->>UI: 返回error响应
@@ -288,23 +317,29 @@ stateDiagram-v2
```mermaid ```mermaid
flowchart TD flowchart TD
Start([用户点击开始]) --> Validate{前端验证} Start([用户点击开始]) --> Validate{前端验证}
Validate -->|订单号为空| ShowEmptyError[显示错误:<br/>请输入至少一个订单号] Validate -->|订单号为空| ShowEmptyError["显示错误:
请输入至少一个订单号"]
Validate -->|验证通过| CallIPC[调用IPC API] Validate -->|验证通过| CallIPC[调用IPC API]
CallIPC --> ConfigCheck{环境配置检查} CallIPC --> ConfigCheck{环境配置检查}
ConfigCheck -->|配置不完整| ConfigError[返回ValidationError:<br/>ERP配置不完整] ConfigCheck -->|配置不完整| ConfigError["返回ValidationError:
ConfigCheck -->|配置完整| ConnectMySQL[连接MySQL] ERP配置完整"]
ConfigCheck -->|配置完整| ConnectDB["连接数据库
(MySQL或SQL Server)"]
ConnectMySQL --> MySQLCheck{连接成功?} ConnectDB --> DBCheck{连接成功?}
MySQLCheck -->|失败| MySQLError[返回DatabaseQueryError:<br/>MySQL连接失败] DBCheck -->|失败| DBError["返回DatabaseQueryError:
MySQLCheck -->|成功| ResolveOrders[解析订单号] 数据库连接失败"]
DBCheck -->|成功| ResolveOrders[解析订单号]
ResolveOrders --> ValidOrders{有有效订单号?} ResolveOrders --> ValidOrders{有有效订单号?}
ValidOrders -->|无| NoOrdersError[返回ValidationError:<br/>没有有效的生产订单号] ValidOrders -->|无| NoOrdersError["返回ValidationError:
没有有效的生产订单号"]
ValidOrders -->|有| LoginERP[ERP登录] ValidOrders -->|有| LoginERP[ERP登录]
LoginERP --> LoginCheck{登录成功?} LoginERP --> LoginCheck{登录成功?}
LoginCheck -->|失败| LoginError[返回ErpConnectionError:<br/>ERP登录失败] LoginCheck -->|失败| LoginError["返回ErpConnectionError:
ERP登录失败"]
LoginCheck -->|成功| ExtractData[执行数据提取] LoginCheck -->|成功| ExtractData[执行数据提取]
ExtractData --> BatchLoop[批处理循环] ExtractData --> BatchLoop[批处理循环]
@@ -324,7 +359,7 @@ flowchart TD
ShowEmptyError --> ResetState1[设置isRunning=false] ShowEmptyError --> ResetState1[设置isRunning=false]
ConfigError --> ResetState2[设置isRunning=false] ConfigError --> ResetState2[设置isRunning=false]
MySQLError --> ResetState3[设置isRunning=false] DBError --> ResetState3[设置isRunning=false]
NoOrdersError --> ResetState4[设置isRunning=false] NoOrdersError --> ResetState4[设置isRunning=false]
LoginError --> ResetState5[设置isRunning=false] LoginError --> ResetState5[设置isRunning=false]
@@ -337,7 +372,7 @@ flowchart TD
style ShowEmptyError fill:#ffcccc style ShowEmptyError fill:#ffcccc
style ConfigError fill:#ffcccc style ConfigError fill:#ffcccc
style MySQLError fill:#ffcccc style DBError fill:#ffcccc
style NoOrdersError fill:#ffcccc style NoOrdersError fill:#ffcccc
style LoginError fill:#ffcccc style LoginError fill:#ffcccc
style RecordError fill:#fff4cc style RecordError fill:#fff4cc
@@ -349,7 +384,7 @@ flowchart TD
| 错误类型 | 触发条件 | 用户反馈 | 恢复策略 | | 错误类型 | 触发条件 | 用户反馈 | 恢复策略 |
|---------|---------|---------|---------| |---------|---------|---------|---------|
| `ValidationError` | 订单号为空、配置不完整、无有效订单号 | 显示红色错误消息 | 修正输入后重试 | | `ValidationError` | 订单号为空、配置不完整、无有效订单号 | 显示红色错误消息 | 修正输入后重试 |
| `DatabaseQueryError` | MySQL连接失败 | 显示数据库连接错误 | 检查数据库配置 | | `DatabaseQueryError` | 数据库连接失败 (MySQL/SQL Server) | 显示数据库连接错误 | 检查数据库配置 |
| `ErpConnectionError` | ERP登录失败 | 显示ERP登录错误 | 检查ERP凭据 | | `ErpConnectionError` | ERP登录失败 | 显示ERP登录错误 | 检查ERP凭据 |
| `BatchError` | 单个批次处理失败 | 记录到错误列表,继续处理 | 查看错误详情 | | `BatchError` | 单个批次处理失败 | 记录到错误列表,继续处理 | 查看错误详情 |
| `SystemError` | 未知系统错误 | 显示通用错误消息 | 查看日志 | | `SystemError` | 未知系统错误 | 显示通用错误消息 | 查看日志 |
@@ -361,42 +396,61 @@ flowchart TD
```mermaid ```mermaid
flowchart LR flowchart LR
subgraph "Input (用户输入)" subgraph "Input (用户输入)"
A1[原始输入<br/>订单号列表] A1["原始输入
A2[批次大小<br/>batchSize=100] 订单号列表"]
A2["批次大小
batchSize=100"]
end end
subgraph "Transformation (数据转换)" subgraph "Transformation (数据转换)"
B1[行解析<br/>按换行符分割] B1["行解析
B2[去空白<br/>trim每行] 按换行符分割"]
B3[过滤空行<br/>移除空字符串] B2["去空白
B4[存储共享状态<br/>Production IDs] trim每行"]
B3["过滤空行
移除空字符串"]
B4["存储共享状态
Production IDs"]
end end
subgraph "Resolution (订单号解析)" subgraph "Resolution (订单号解析)"
C1[查询MySQL<br/>查找映射关系] C0["DatabaseFactory
C2[提取生产订单号<br/>获取有效值] create()"]
C3[收集警告<br/>记录未映射项] C1["查询数据库
查找映射关系"]
C2["提取生产订单号
获取有效值"]
C3["收集警告
记录未映射项"]
end end
subgraph "Processing (批量处理)" subgraph "Processing (批量处理)"
D1[批次分组<br/>按batchSize切分] D1["批次分组
D2[批次迭代<br/>逐批处理] 按batchSize切分"]
D3[订单拼接<br/>逗号连接] D2["批次迭代
逐批处理"]
D3["订单拼接
逗号连接"]
end end
subgraph "Output (结果输出)" subgraph "Output (结果输出)"
E1[下载文件列表<br/>downloadedFiles数组] E1["下载文件列表
E2[合并文件<br/>mergedFile TODO] downloadedFiles数组"]
E3[记录总数<br/>recordCount] E2["合并文件
E4[错误列表<br/>errors数组] mergedFile TODO"]
E3["记录总数
recordCount"]
E4["错误列表
errors数组"]
end end
A1 --> B1 A1 --> B1
B1 --> B2 B1 --> B2
B2 --> B3 B2 --> B3
B3 --> B4 B3 --> B4
B4 --> C1 B4 --> C0
A2 --> D1 A2 --> D1
C0 --> C1
C1 --> C2 C1 --> C2
C2 --> D1 C2 --> D1
C3 --> E4 C3 --> E4
@@ -407,6 +461,7 @@ flowchart LR
style A1 fill:#e3f2fd style A1 fill:#e3f2fd
style A2 fill:#e3f2fd style A2 fill:#e3f2fd
style C0 fill:#e8f5e9
style E1 fill:#e8f5e9 style E1 fill:#e8f5e9
style E2 fill:#e8f5e9 style E2 fill:#e8f5e9
style E3 fill:#e8f5e9 style E3 fill:#e8f5e9
@@ -532,7 +587,7 @@ useEffect(() => {
> >
> 这确保了即使用户在Cleaner页面刷新数据也已同步。 > 这确保了即使用户在Cleaner页面刷新数据也已同步。
### 2. IPC处理器核心逻辑 (extractor-handler.ts:17-154) ### 2. IPC处理器核心逻辑 (extractor-handler.ts:17-145)
```typescript ```typescript
ipcMain.handle( ipcMain.handle(
@@ -540,7 +595,7 @@ ipcMain.handle(
async (_event, input: ExtractorInput): Promise<IpcResult<ExtractorResult>> => { async (_event, input: ExtractorInput): Promise<IpcResult<ExtractorResult>> => {
return withErrorHandling(async () => { return withErrorHandling(async () => {
let authService: ErpAuthService | null = null let authService: ErpAuthService | null = null
let mysqlService: MySqlService | null = null let dbService: IDatabaseService | null = null
try { try {
// 1. 环境配置检查 // 1. 环境配置检查
@@ -552,12 +607,15 @@ ipcMain.handle(
throw new ValidationError('ERP 配置不完整') throw new ValidationError('ERP 配置不完整')
} }
// 2. 连接MySQL并解析订单号 // 2. 使用数据库工厂创建服务实例 (支持 MySQL 和 SQL Server)
const mysqlConfig = { /* ... */ } try {
mysqlService = new MySqlService(mysqlConfig) dbService = await create() // 工厂方法,根据 DB_TYPE 自动选择数据库
await mysqlService.connect() } catch (error) {
throw new DatabaseQueryError('数据库连接失败', 'DB_CONNECTION_FAILED', error)
}
const resolver = new OrderNumberResolver(mysqlService) // 3. 解析订单号
const resolver = new OrderNumberResolver(dbService)
const mappings = await resolver.resolve(input.orderNumbers) const mappings = await resolver.resolve(input.orderNumbers)
const validOrderNumbers = resolver.getValidOrderNumbers(mappings) const validOrderNumbers = resolver.getValidOrderNumbers(mappings)
const warnings = resolver.getWarnings(mappings) const warnings = resolver.getWarnings(mappings)
@@ -566,33 +624,105 @@ ipcMain.handle(
throw new ValidationError('没有有效的生产订单号可处理') throw new ValidationError('没有有效的生产订单号可处理')
} }
// 3. ERP登录 // 4. ERP登录
authService = new ErpAuthService({ url, username, password, headless: true }) authService = new ErpAuthService({ url, username, password, headless: true })
await authService.login() await authService.login()
// 4. 执行提取 // 5. 执行提取
const extractor = new ExtractorService(authService) const extractor = new ExtractorService(authService)
const result = await extractor.extract({ const result = await extractor.extract({
...input, ...input,
orderNumbers: validOrderNumbers orderNumbers: validOrderNumbers
}) })
// 5. 添加警告到结果 // 6. 添加警告到结果
if (warnings.length > 0) { if (warnings.length > 0) {
result.errors = [...warnings, ...result.errors] result.errors = [...warnings, ...result.errors]
} }
return result return result
} finally { } finally {
// 6. 资源清理 // 7. 资源清理
if (authService) await authService.close() if (authService) await authService.close()
if (mysqlService) await mysqlService.disconnect() if (dbService) await dbService.disconnect()
} }
}, 'extractor:run') }, 'extractor:run')
} }
) )
``` ```
### 2.1 数据库工厂模式 (database/index.ts)
```typescript
/**
* 数据库工厂 - 创建数据库服务实例
* 支持 MySQL 和 SQL Server 双数据库
*/
export async function create(type?: DatabaseType): Promise<IDatabaseService> {
const dbType = type || getDatabaseType() // 从 DB_TYPE 环境变量读取
// 返回缓存的实例(单例模式)
const cached = instances.get(dbType)
if (cached && cached.isConnected()) {
return cached
}
// 创建新实例
let service: IDatabaseService
if (dbType === 'sqlserver') {
service = new SqlServerService(createSqlServerConfig())
} else {
service = new MySqlService(createMySqlConfig())
}
await service.connect()
instances.set(dbType, service) // 缓存实例
return service
}
/**
* 数据库类型判断
*/
export function getDatabaseType(): DatabaseType {
const dbType = process.env.DB_TYPE?.toLowerCase()
if (dbType === 'sqlserver' || dbType === 'mssql') {
return 'sqlserver'
}
return 'mysql'
}
```
### 2.2 数据库服务接口 (types/database.types.ts)
```typescript
/**
* 数据库服务统一接口
*/
export interface IDatabaseService {
/** 数据库类型标识 */
readonly type: DatabaseType
/** 连接数据库 */
connect(): Promise<void>
/** 断开连接 */
disconnect(): Promise<void>
/** 检查连接状态 */
isConnected(): boolean
/** 执行查询 */
query(sql: string, params?: any[]): Promise<QueryResult>
/** 事务执行 */
transaction(queries: { sql: string; params?: any[] }[]): Promise<void>
}
export type DatabaseType = 'mysql' | 'sqlserver'
```
### 3. 提取服务批处理逻辑 (extractor.ts:29-77) ### 3. 提取服务批处理逻辑 (extractor.ts:29-77)
```typescript ```typescript
@@ -725,21 +855,27 @@ export interface ExtractorResult {
1. **三层验证机制**: 1. **三层验证机制**:
- 前端验证: 非空检查 - 前端验证: 非空检查
- 配置验证: 环境变量完整性 - 配置验证: 环境变量完整性
- 数据验证: 订单号有效性(通过MySQL查询) - 数据验证: 订单号有效性(通过数据库查询)
2. **资源管理策略**: 2. **数据库架构 (v1.2 更新)**:
- 使用工厂模式 (`create()`) 创建数据库服务实例
- 支持 MySQL 和 SQL Server 双数据库,通过 `DB_TYPE` 环境变量切换
- 通过 `IDatabaseService` 统一接口实现数据库无关操作
- 单例缓存机制,避免重复创建连接
3. **资源管理策略**:
- 使用 try-finally 确保资源清理 - 使用 try-finally 确保资源清理
- 浏览器在使用后立即关闭 - 浏览器在使用后立即关闭
- 数据库连接在使用后断开 - 数据库连接在使用后断开
- 清理操作在 finally 块中独立 try-catch避免清理失败影响结果返回 - 清理操作在 finally 块中独立 try-catch避免清理失败影响结果返回
3. **错误容错设计**: 4. **错误容错设计**:
- 单个批次失败不影响其他批次 - 单个批次失败不影响其他批次
- 警告信息独立收集,不影响主流程 - 警告信息独立收集,不影响主流程
- 详细错误信息返回给前端展示 - 详细错误信息返回给前端展示
- 使用自定义错误类型 (`ValidationError`, `DatabaseQueryError`, `ErpConnectionError`) - 使用自定义错误类型 (`ValidationError`, `DatabaseQueryError`, `ErpConnectionError`)
4. **用户体验优化**: 5. **用户体验优化**:
- sessionStorage 持久化用户输入(`orderNumbers`, `batchSize` - sessionStorage 持久化用户输入(`orderNumbers`, `batchSize`
- 订单号实时同步到共享状态(供 Cleaner 模块使用) - 订单号实时同步到共享状态(供 Cleaner 模块使用)
- 详细的日志记录 - 详细的日志记录
@@ -761,9 +897,11 @@ export interface ExtractorResult {
- **批处理**: 默认每批100个订单平衡性能与稳定性 - **批处理**: 默认每批100个订单平衡性能与稳定性
- **异步并发**: 使用 async/await 处理异步操作 - **异步并发**: 使用 async/await 处理异步操作
- **下载监听**: 使用 Playwright 事件监听处理文件下载 - **下载监听**: 使用 Playwright 事件监听处理文件下载
- **数据库连接池**: 工厂模式缓存实例,复用连接
### 扩展性 ### 扩展性
- **数据库可切换**: 通过 `DB_TYPE` 环境变量切换 MySQL/SQL Server
- **配置化**: batchSize 可配置 - **配置化**: batchSize 可配置
- **模块化**: 服务独立,易于测试和维护 - **模块化**: 服务独立,易于测试和维护
- **错误类型化**: 使用自定义错误类型便于精确处理 - **错误类型化**: 使用自定义错误类型便于精确处理