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