From 6ec422f357cbd200e819879506e71a14648986a9 Mon Sep 17 00:00:00 2001 From: Misaka_Company Date: Tue, 3 Mar 2026 09:06:33 +0800 Subject: [PATCH] docs: add UI flow analysis documentation and update gitignore Add documentation for extractor start button and settings save button flows with detailed Mermaid diagrams. Also add logs directory to gitignore. Co-Authored-By: Claude Sonnet 4.5 --- .gitignore | 5 +- docs/extractor-start-button-flow.md | 657 ++++++++++++++++++++ docs/settings-save-button-flow.md | 913 ++++++++++++++++++++++++++++ 3 files changed, 1574 insertions(+), 1 deletion(-) create mode 100644 docs/extractor-start-button-flow.md create mode 100644 docs/settings-save-button-flow.md diff --git a/.gitignore b/.gitignore index 2cab9ab..a7bd664 100644 --- a/.gitignore +++ b/.gitignore @@ -22,4 +22,7 @@ tests/debug/ tests/manual/ test-*.mjs test-*.js -compare_*.js \ No newline at end of file +compare_*.js + +# logs +logs \ No newline at end of file diff --git a/docs/extractor-start-button-flow.md b/docs/extractor-start-button-flow.md new file mode 100644 index 0000000..e889969 --- /dev/null +++ b/docs/extractor-start-button-flow.md @@ -0,0 +1,657 @@ +# 数据提取界面 - 开始按钮工作流程详解 + +> **文档版本**: 1.0 +> **创建日期**: 2026-03-03 +> **适用范围**: ERPAuto v1.0+ +> **相关文件**: +> - `src/renderer/src/pages/ExtractorPage.tsx` (UI层) +> - `src/main/ipc/extractor-handler.ts` (IPC处理层) +> - `src/main/services/erp/extractor.ts` (业务逻辑层) +> - `src/main/services/erp/order-resolver.ts` (订单号解析服务) +> - `src/main/services/erp/erp-auth.ts` (ERP认证服务) + +## 目录 + +1. [系统架构概览](#系统架构概览) +2. [完整执行流程](#完整执行流程) +3. [状态管理流程](#状态管理流程) +4. [错误处理机制](#错误处理机制) +5. [数据流转过程](#数据流转过程) +6. [关键代码引用](#关键代码引用) + +--- + +## 系统架构概览 + +```mermaid +graph TB + subgraph "Renderer Process (UI层)" + A[ExtractorPage.tsx] + B[React State Management] + C[用户输入: 订单号列表] + D[开始按钮] + end + + subgraph "IPC Bridge (通信桥梁)" + E[electron.extractor.runExtractor] + F[extractor:run Channel] + end + + subgraph "Main Process (主进程)" + G[extractor-handler.ts] + H[OrderNumberResolver] + I[ErpAuthService] + J[ExtractorService] + end + + subgraph "External Services (外部服务)" + K[(MySQL Database)] + L[ERP Web System] + M[Playwright Browser] + end + + A -->|用户点击| D + D -->|调用API| E + E -->|IPC通信| F + F -->|接收请求| G + G -->|解析订单号| H + H -->|查询数据| K + G -->|登录认证| I + I -->|自动化操作| M + M -->|访问页面| L + G -->|执行提取| J + J -->|使用| I + J -->|返回结果| G + G -->|IPC响应| F + F -->|更新UI| A + B -->|管理状态| A + + style A fill:#e1f5ff + style G fill:#fff4e1 + style K fill:#e8f5e9 + style L fill:#f3e5f5 +``` + +### 架构说明 + +- **Renderer Process**: 负责UI展示和用户交互,使用React管理状态 +- **IPC Bridge**: 安全的进程间通信桥梁,通过preload脚本暴露 +- **Main Process**: 处理业务逻辑、数据库操作、浏览器自动化 +- **External Services**: MySQL数据库和ERP Web系统 + +--- + +## 完整执行流程 + +```mermaid +sequenceDiagram + autonumber + participant User as 👤 用户 + participant UI as ExtractorPage.tsx + participant IPC as electron API + participant Handler as extractor-handler.ts + participant Resolver as OrderNumberResolver + participant MySQL as MySQL Database + participant Auth as ErpAuthService + participant Extractor as ExtractorService + participant Browser as Playwright Browser + participant ERP as ERP Web System + + User->>UI: 1. 输入订单号列表
(每行一个) + User->>UI: 2. 点击"开始提取"按钮 + + Note over UI: 前端验证与准备 + UI->>UI: 3. 验证订单号非空 + UI->>UI: 4. 设置isRunning=true + UI->>UI: 5. 清空之前的结果和错误 + UI->>UI: 6. 存储Production IDs到共享状态 + UI->>IPC: 7. 调用electron.extractor.runExtractor() + + Note over IPC,Handler: IPC通信 + IPC->>Handler: 8. 发送IPC消息 'extractor:run' + + Note over Handler: 环境配置检查 + Handler->>Handler: 9. 读取.env配置
(ERP_URL, USERNAME, PASSWORD) + Handler->>Handler: 10. 验证配置完整性 + alt 配置不完整 + Handler-->>UI: 返回ValidationError + UI->>UI: 显示错误提示 + UI->>UI: 设置isRunning=false + end + + Note over Handler,MySQL: 订单号解析阶段 + Handler->>MySQL: 11. 连接MySQL数据库 + alt MySQL连接失败 + Handler-->>UI: 返回DatabaseQueryError + end + + Handler->>Resolver: 12. 创建OrderNumberResolver + Handler->>Resolver: 13. 调用resolve(orderNumbers) + Resolver->>MySQL: 14. 查询生产订单号映射 + MySQL-->>Resolver: 15. 返回映射结果 + Resolver-->>Handler: 16. 返回映射结果
(包含有效订单号和警告) + + alt 没有有效订单号 + Handler-->>UI: 返回ValidationError + UI->>UI: 显示错误: "没有有效的生产订单号" + 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. 登录成功 + alt 登录失败 + Auth-->>Handler: 抛出异常 + Handler-->>UI: 返回ErpConnectionError + end + + Note over Extractor,ERP: 数据提取阶段 + Handler->>Extractor: 26. 创建ExtractorService + Handler->>Extractor: 27. 调用extract()
传入有效订单号 + Extractor->>Browser: 28. 使用已有session + Extractor->>ERP: 29. 导航到离散备料计划维护页面 + Extractor->>ERP: 30. 设置查询界面
(订单号查询, 全部标签, 限制5000) + + loop 批处理循环 (每批最多100个订单) + Extractor->>Extractor: 31. 创建批次
(按batchSize分组) + Extractor->>UI: 32. 发送进度更新
onProgress(message, progress%) + UI->>UI: 33. 更新进度条和日志 + + Extractor->>ERP: 34. 填充订单号到搜索框 + Extractor->>ERP: 35. 点击搜索按钮 + Extractor->>ERP: 36. 等待加载完成 + Extractor->>ERP: 37. 点击第一行复选框 + Extractor->>ERP: 38. 悬停并点击"更多" + Extractor->>ERP: 39. 点击"输出" + Extractor->>ERP: 40. 设置行数阈值为300000 + Extractor->>ERP: 41. 点击"确定(Y)" + + Browser->>Browser: 42. 监听下载事件 + ERP->>Browser: 43. 触发文件下载 + Browser->>Browser: 44. 保存文件到downloads目录 + Browser-->>Extractor: 45. 返回文件路径 + Extractor->>Extractor: 46. 记录下载文件路径 + end + + Extractor->>Extractor: 47. 汇总结果
(文件列表, 记录数, 错误) + Extractor-->>Handler: 48. 返回ExtractorResult + Handler->>Handler: 49. 添加解析警告到错误列表 + + Note over Handler,IPC: 清理阶段 + Handler->>Browser: 50. 关闭浏览器 + Handler->>MySQL: 51. 断开数据库连接 + + Note over Handler,UI: 响应阶段 + Handler-->>IPC: 52. 返回IPC响应
(success: true, data: result) + IPC-->>UI: 53. 返回response + UI->>UI: 54. 设置result状态 + UI->>UI: 55. 设置isRunning=false + UI->>UI: 56. 清空进度状态 + UI->>User: 57. 显示提取结果
(文件数, 记录数, 错误数) + + alt 发生任何错误 + Handler-->>UI: 返回error响应 + UI->>UI: 设置error状态 + UI->>UI: 设置isRunning=false + UI->>User: 显示错误信息 + end +``` + +--- + +## 状态管理流程 + +```mermaid +stateDiagram-v2 + [*] --> Idle: 初始状态 + + Idle --> Validating: 用户点击开始按钮 + Validating --> Idle: 验证失败
(订单号为空) + Validating --> Running: 验证通过 + + Running --> Processing: 调用IPC API + Processing --> Progress: 收到进度更新 + Progress --> Processing: 继续处理 + + Processing --> Success: 提取成功 + Processing --> Error: 提取失败 + + Success --> Idle: 用户清空或重新输入 + Error --> Idle: 用户修正后重试 + + note right of Validating + 前端验证阶段: + - 检查orderNumbers非空 + - 解析订单号列表 + - 存储到sessionStorage + - 存储到共享状态 + end note + + note right of Processing + 后端处理阶段: + - 环境配置检查 + - 订单号解析 + - ERP登录 + - 批量数据提取 + - 资源清理 + end note + + note right of Progress + 进度更新: + - 更新进度条百分比 + - 添加日志到控制台 + - 保持isRunning=true + end note + + note right of Success + 成功状态: + - 显示下载文件数 + - 显示记录总数 + - 显示错误数 + - isRunning=false + end note + + note right of Error + 错误状态: + - 显示错误信息 + - isRunning=false + - 保留用户输入 + end note +``` + +### 状态变量说明 + +| 状态变量 | 类型 | 说明 | 持久化 | +|---------|------|------|--------| +| `orderNumbers` | string | 用户输入的订单号列表 | ✅ sessionStorage | +| `batchSize` | number | 每批处理的订单数量 (默认100) | ✅ sessionStorage | +| `isRunning` | boolean | 是否正在执行提取 | ❌ 内存状态 | +| `progress` | ExtractorProgress \| null | 当前进度信息 | ❌ 内存状态 | +| `result` | ExtractorResult \| null | 提取结果 | ❌ 内存状态 | +| `error` | string \| null | 错误信息 | ❌ 内存状态 | +| `logs` | string[] | 执行日志列表 | ❌ 内存状态 | + +--- + +## 错误处理机制 + +```mermaid +flowchart TD + Start([用户点击开始]) --> Validate{前端验证} + Validate -->|订单号为空| ShowEmptyError[显示错误:
请输入至少一个订单号] + Validate -->|验证通过| CallIPC[调用IPC API] + + CallIPC --> ConfigCheck{环境配置检查} + ConfigCheck -->|配置不完整| ConfigError[返回ValidationError:
ERP配置不完整] + ConfigCheck -->|配置完整| ConnectMySQL[连接MySQL] + + ConnectMySQL --> MySQLCheck{连接成功?} + MySQLCheck -->|失败| MySQLError[返回DatabaseQueryError:
MySQL连接失败] + MySQLCheck -->|成功| ResolveOrders[解析订单号] + + ResolveOrders --> ValidOrders{有有效订单号?} + ValidOrders -->|无| NoOrdersError[返回ValidationError:
没有有效的生产订单号] + ValidOrders -->|有| LoginERP[ERP登录] + + LoginERP --> LoginCheck{登录成功?} + LoginCheck -->|失败| LoginError[返回ErpConnectionError:
ERP登录失败] + LoginCheck -->|成功| ExtractData[执行数据提取] + + ExtractData --> BatchLoop[批处理循环] + BatchLoop --> BatchError{批次成功?} + BatchError -->|失败| RecordError[记录错误到result.errors] + BatchError -->|成功| SaveFile[保存文件] + RecordError --> NextBatch{还有批次?} + SaveFile --> NextBatch + + NextBatch -->|是| BatchLoop + NextBatch -->|否| Cleanup[清理资源] + + Cleanup --> CheckWarnings{有警告?} + CheckWarnings -->|是| AddWarnings[添加警告到errors] + CheckWarnings -->|否| ReturnSuccess[返回成功结果] + AddWarnings --> ReturnSuccess + + ShowEmptyError --> ResetState1[设置isRunning=false] + ConfigError --> ResetState2[设置isRunning=false] + MySQLError --> ResetState3[设置isRunning=false] + NoOrdersError --> ResetState4[设置isRunning=false] + LoginError --> ResetState5[设置isRunning=false] + + ResetState1 --> End1([结束]) + ResetState2 --> End2([结束]) + ResetState3 --> End3([结束]) + ResetState4 --> End4([结束]) + ResetState5 --> End5([结束]) + ReturnSuccess --> End6([显示结果]) + + style ShowEmptyError fill:#ffcccc + style ConfigError fill:#ffcccc + style MySQLError fill:#ffcccc + style NoOrdersError fill:#ffcccc + style LoginError fill:#ffcccc + style RecordError fill:#fff4cc + style ReturnSuccess fill:#ccffcc +``` + +### 错误类型与处理策略 + +| 错误类型 | 触发条件 | 用户反馈 | 恢复策略 | +|---------|---------|---------|---------| +| `ValidationError` | 订单号为空、配置不完整、无有效订单号 | 显示红色错误消息 | 修正输入后重试 | +| `DatabaseQueryError` | MySQL连接失败 | 显示数据库连接错误 | 检查数据库配置 | +| `ErpConnectionError` | ERP登录失败 | 显示ERP登录错误 | 检查ERP凭据 | +| `BatchError` | 单个批次处理失败 | 记录到错误列表,继续处理 | 查看错误详情 | +| `SystemError` | 未知系统错误 | 显示通用错误消息 | 查看日志 | + +--- + +## 数据流转过程 + +```mermaid +flowchart LR + subgraph "Input (用户输入)" + A1[原始输入
订单号列表] + A2[批次大小
batchSize=100] + end + + subgraph "Transformation (数据转换)" + B1[行解析
按换行符分割] + B2[去空白
trim每行] + B3[过滤空行
移除空字符串] + B4[存储共享状态
Production IDs] + end + + subgraph "Resolution (订单号解析)" + C1[查询MySQL
查找映射关系] + C2[提取生产订单号
获取有效值] + C3[收集警告
记录未映射项] + end + + subgraph "Processing (批量处理)" + D1[批次分组
按batchSize切分] + D2[批次迭代
逐批处理] + D3[订单拼接
逗号连接] + end + + subgraph "Output (结果输出)" + E1[下载文件列表
downloadedFiles数组] + E2[合并文件
mergedFile TODO] + E3[记录总数
recordCount] + E4[错误列表
errors数组] + end + + A1 --> B1 + B1 --> B2 + B2 --> B3 + B3 --> B4 + B4 --> C1 + A2 --> D1 + C1 --> C2 + C2 --> D1 + C3 --> E4 + D1 --> D2 + D2 --> D3 + D3 --> E1 + E1 --> E3 + + style A1 fill:#e3f2fd + style A2 fill:#e3f2fd + style E1 fill:#e8f5e9 + style E2 fill:#e8f5e9 + style E3 fill:#e8f5e9 + style E4 fill:#fff3e0 +``` + +### 数据转换详情 + +**阶段1: 用户输入 → Production IDs** +``` +输入: "PO-20231024-001\nPO-20231024-002\nPO-20231024-003" + ↓ 分割 + trim + 过滤 +结果: ["PO-20231024-001", "PO-20231024-002", "PO-20231024-003"] + ↓ 存储到共享状态 +共享状态: Production IDs (供清理模块使用) +``` + +**阶段2: Production IDs → 生产订单号** +``` +输入: ["PO-20231024-001", "PO-20231024-002", "INVALID"] + ↓ MySQL查询 (production_order表) +映射结果: { + "PO-20231024-001": "MO-20231024-001", + "PO-20231024-002": "MO-20231024-002", + "INVALID": null +} + ↓ 提取有效值 +有效订单号: ["MO-20231024-001", "MO-20231024-002"] +警告: ["INVALID: 未找到对应的生产订单号"] +``` + +**阶段3: 生产订单号 → 批次** +``` +输入: ["MO-001", "MO-002", ..., "MO-250"] (250个) +批次大小: 100 + ↓ 分组 +批次1: ["MO-001", ..., "MO-100"] +批次2: ["MO-101", ..., "MO-200"] +批次3: ["MO-201", ..., "MO-250"] +``` + +**阶段4: 批次 → ERP查询字符串** +``` +批次: ["MO-001", "MO-002", "MO-003"] + ↓ 逗号连接 +查询字符串: "MO-001,MO-002,MO-003" + ↓ 填充到ERP搜索框 +ERP操作: 填入搜索框并点击搜索 +``` + +--- + +## 关键代码引用 + +### 1. 前端开始按钮处理 (ExtractorPage.tsx:52-90) + +```typescript +const handleExtract = async () => { + // 1. 前端验证 + if (!orderNumbers.trim()) { + setError('请输入至少一个订单号') + return + } + + // 2. 设置运行状态 + setIsRunning(true) + setProgress(null) + setResult(null) + setError(null) + + try { + // 3. 解析订单号列表 + const orderNumberList = orderNumbers + .split('\n') + .map((line) => line.trim()) + .filter((line) => line.length > 0) + + // 4. 存储到共享状态 + await window.electron.validation.setSharedProductionIds(orderNumberList) + + // 5. 调用后端API + const response = await window.electron.extractor.runExtractor({ + orderNumbers: orderNumberList, + batchSize + }) + + // 6. 处理响应 + if (response.success && response.data) { + setResult(response.data) + } else { + setError(response.error || '提取失败') + } + } catch (err) { + setError(err instanceof Error ? err.message : '发生未知错误') + } finally { + // 7. 重置状态 + setIsRunning(false) + setProgress(null) + } +} +``` + +### 2. IPC处理器核心逻辑 (extractor-handler.ts:17-154) + +```typescript +ipcMain.handle('extractor:run', async (_event, input: ExtractorInput) => { + return withErrorHandling(async () => { + // 1. 环境配置检查 + const erpUrl = process.env.ERP_URL || '' + const erpUsername = process.env.ERP_USERNAME || '' + const erpPassword = process.env.ERP_PASSWORD || '' + + if (!erpUrl || !erpUsername || !erpPassword) { + throw new ValidationError('ERP 配置不完整') + } + + // 2. 订单号解析 + const mysqlService = new MySqlService(mysqlConfig) + await mysqlService.connect() + + const resolver = new OrderNumberResolver(mysqlService) + const mappings = await resolver.resolve(input.orderNumbers) + const validOrderNumbers = resolver.getValidOrderNumbers(mappings) + + if (validOrderNumbers.length === 0) { + throw new ValidationError('没有有效的生产订单号可处理') + } + + // 3. ERP登录 + const authService = new ErpAuthService({...}) + await authService.login() + + // 4. 执行提取 + const extractor = new ExtractorService(authService) + const result = await extractor.extract({ + ...input, + orderNumbers: validOrderNumbers + }) + + // 5. 资源清理 + await authService.close() + await mysqlService.disconnect() + + return result + }, 'extractor:run') +}) +``` + +### 3. 提取服务批处理逻辑 (extractor.ts:43-67) + +```typescript +// 批处理循环 +const batches = this.createBatches(input.orderNumbers, batchSize) + +for (let i = 0; i < batches.length; i++) { + const batch = batches[i] + const progress = ((i + 1) / batches.length) * 100 + + // 发送进度更新 + input.onProgress?.(`Processing batch ${i + 1}/${batches.length}`, progress) + + try { + const filePath = await this.downloadBatch( + session, popupPage, workFrame, batch, i, batches.length + ) + result.downloadedFiles.push(filePath) + } catch (error) { + // 记录错误但继续处理 + result.errors.push(`Batch ${i + 1}: ${error.message}`) + } +} +``` + +### 4. 浏览器自动化单批次处理 (extractor.ts:151-194) + +```typescript +private async downloadBatch(...): Promise { + // 1. 填充订单号 + const textbox = workFrame.getByRole('textbox', { name: '来源生产订单号' }) + await textbox.fill(orderNumbers.join(',')) + + // 2. 点击搜索 + await workFrame.locator('.search-component-searchBtn').click() + + // 3. 等待加载 + await this.waitForLoading(workFrame) + + // 4. 选择第一行 + await workFrame.getByRole('row', { name: '序号' }).getByLabel('').click() + + // 5. 点击更多 -> 输出 + await workFrame.getByRole('button', { name: '更多' }).hover() + await workFrame.getByText('输出', { exact: true }).click() + + // 6. 设置阈值 + await thresholdBox.fill('300000') + + // 7. 等待下载 + const downloadPromise = popupPage.waitForEvent('download') + await workFrame.getByRole('button', { name: '确定(Y)' }).click() + const download = await downloadPromise + + // 8. 保存文件 + const downloadPath = path.join(this.downloadDir, `temp_batch_${batchIndex + 1}.xlsx`) + await download.saveAs(downloadPath) + + return downloadPath +} +``` + +--- + +## 总结 + +### 流程关键点 + +1. **三层验证机制**: + - 前端验证: 非空检查 + - 配置验证: 环境变量完整性 + - 数据验证: 订单号有效性 + +2. **资源管理策略**: + - 使用try-finally确保资源清理 + - 浏览器在使用后立即关闭 + - 数据库连接在使用后断开 + +3. **错误容错设计**: + - 单个批次失败不影响其他批次 + - 警告信息独立收集,不影响主流程 + - 详细错误信息返回给前端展示 + +4. **用户体验优化**: + - sessionStorage持久化用户输入 + - 实时进度反馈 + - 共享状态支持跨页面数据传递 + - 详细的日志记录 + +### 性能考虑 + +- **批处理**: 默认每批100个订单,平衡性能与稳定性 +- **异步并发**: 使用async/await处理异步操作 +- **进度反馈**: 避免长时间无响应的用户体验 + +### 扩展性 + +- **配置化**: batchSize可配置 +- **模块化**: 服务独立,易于测试和维护 +- **错误类型化**: 使用自定义错误类型便于精确处理 + +--- + +**文档维护**: 如代码逻辑变更,请及时更新本文档和相关流程图。 diff --git a/docs/settings-save-button-flow.md b/docs/settings-save-button-flow.md new file mode 100644 index 0000000..66235df --- /dev/null +++ b/docs/settings-save-button-flow.md @@ -0,0 +1,913 @@ +# 系统设置保存按钮工作流程分析 +# System Settings Save Button Workflow Analysis + +## 文档概述 / Document Overview + +本文档详细分析了 ERPAuto 系统设置界面中保存按钮的完整工作流程,包括架构设计、数据流转、技术实现细节以及错误处理机制。 + +This document provides a comprehensive analysis of the save button workflow in the ERPAuto system settings interface, including architecture design, data flow, technical implementation details, and error handling mechanisms. + +--- + +## 目录 / Table of Contents + +1. [架构概览](#架构概览) +2. [数据流程图](#数据流程图) +3. [组件详解](#组件详解) +4. [数据结构](#数据结构) +5. [错误处理机制](#错误处理机制) +6. [安全考虑](#安全考虑) +7. [技术实现细节](#技术实现细节) + +--- + +## 架构概览 / Architecture Overview + +### 系统架构 / System Architecture + +系统设置保存功能采用典型的 Electron 三层架构模式: + +The system settings save functionality follows the classic Electron three-tier architecture pattern: + +```mermaid +graph TB + subgraph "Renderer Process 渲染进程" + UI[SettingsPage.tsx
UI Component] + end + + subgraph "Preload Script 预加载脚本" + BRIDGE[contextBridge API
Security Boundary] + end + + subgraph "Main Process 主进程" + IPC[settings-handler.ts
IPC Handler] + SERVICE[ConfigManager.ts
Configuration Service] + FILE[.env File
Persistent Storage] + end + + UI -->|IPC Invoke| BRIDGE + BRIDGE -->|Secure Channel| IPC + IPC -->|Business Logic| SERVICE + SERVICE -->|Write| FILE + FILE -->|Confirm| SERVICE + SERVICE -->|Result| IPC + IPC -->|Response| BRIDGE + BRIDGE -->|Promise Resolve| UI + + style UI fill:#e1f5ff + style BRIDGE fill:#fff4e1 + style IPC fill:#ffe1f5 + style SERVICE fill:#e1ffe1 + style FILE fill:#f5f5f5 +``` + +### 核心设计模式 / Core Design Patterns + +1. **单向数据流**:数据从 UI → Main Process → File,响应沿相反路径返回 +2. **安全隔离**:Preload 脚本作为安全桥梁,通过 `contextBridge` 暴露受限 API +3. **单例模式**:ConfigManager 使用单例确保配置一致性 +4. **缓存优先**:配置读取优先从内存缓存获取,写入时同步到磁盘 + +--- + +## 数据流程图 / Data Flow Diagrams + +### 完整保存流程 / Complete Save Flow + +```mermaid +sequenceDiagram + actor User as 用户 User + participant UI as SettingsPage.tsx + participant Preload as preload/index.ts + participant IPC as settings-handler.ts + participant Config as ConfigManager.ts + participant File as .env File + + User->>UI: 点击保存按钮
Click Save Button + activate UI + + UI->>UI: handleSaveSettings() + Note over UI: 检查是否修改
Check isModified + + UI->>Preload: window.electron.settings
.saveSettings(settings) + activate Preload + + Preload->>IPC: ipcRenderer.invoke
('settings:saveSettings', settings) + activate IPC + + IPC->>IPC: 验证用户类型
Validate User Type + IPC->>Config: configManager
.saveAllSettings(settings) + activate Config + + Config->>Config: 更新内存缓存
Update Cache + Note over Config: set('erp.url', value)
set('erp.username', value)
... (40+ fields) + + Config->>File: fs.writeFileSync
(.env, content) + activate File + File-->>Config: true/false + deactivate File + + Config-->>IPC: Promise + deactivate Config + + IPC-->>Preload: {success, error?} + deactivate IPC + + Preload-->>UI: Promise resolve + deactivate Preload + + alt 保存成功 / Save Success + UI->>UI: setIsModified(false) + UI->>User: 显示成功消息
Show Success Message + else 保存失败 / Save Failed + UI->>User: 显示错误消息
Show Error Message + end + + deactivate UI +``` + +### 数据转换流程 / Data Transformation Flow + +```mermaid +graph LR + subgraph "UI State" + STATE[Settings Interface
settings.erp.url = 'https://...'] + end + + subgraph "Type Conversion" + T1[SettingsData Object
TypeScript Interface] + end + + subgraph "IPC Transport" + JSON[JSON Serialization
String Transfer] + end + + subgraph "Service Layer" + CACHE[Config Cache
Map] + end + + subgraph "File System" + ENV[.env File Format
KEY=VALUE] + end + + STATE -->|Object| T1 + T1 -->|JSON.stringify| JSON + JSON -->|Deserialize| T1 + T1 -->|set key-value| CACHE + CACHE -->|Format| ENV + + style STATE fill:#e1f5ff + style JSON fill:#fff4e1 + style CACHE fill:#e1ffe1 + style ENV fill:#f5f5f5 +``` + +--- + +## 组件详解 / Component Details + +### 1. 渲染进程 / Renderer Process + +#### SettingsPage.tsx (`src/renderer/src/pages/SettingsPage.tsx`) + +**主要职责 / Main Responsibilities:** +- 用户界面渲染和交互 +- 本地状态管理(settings, isModified, message) +- 调用 IPC 通信 + +**关键函数 / Key Functions:** + +```typescript +// 第 61-73 行 / Lines 61-73 +const handleSaveSettings = async () => { + try { + const result = await window.electron.settings.saveSettings(settings as any) + if (result.success) { + setIsModified(false) // 清除修改标记 + showMessage('success', '设置保存成功') + } else { + showMessage('error', result.error || '保存失败') + } + } catch (error) { + showMessage('error', '保存设置时发生错误') + } +} +``` + +**状态管理 / State Management:** + +| 状态变量 | 类型 | 用途 | +|---------|------|------| +| `settings` | `Settings` | 当前配置数据,结构为 `{ erp: { url, username, password } }` | +| `isModified` | `boolean` | 标记配置是否已修改,控制保存按钮启用状态 | +| `isLoading` | `boolean` | 加载状态,显示加载动画 | +| `message` | `object \| null` | 临时消息,3秒后自动消失 | + +**UI 交互逻辑 / UI Interaction Logic:** + +```mermaid +stateDiagram-v2 + [*] --> Loading: 组件挂载 + Loading --> Ready: loadSettings() + Ready --> Modified: updateSettings() + Modified --> Modified: 继续修改 + Modified --> Ready: 保存成功 + Modified --> Error: 保存失败 + Error --> Modified: 用户继续操作 + Ready --> [*]: 组件卸载 + + note right of Modified + 保存按钮启用 + Save Button Enabled + end note + + note right of Ready + 保存按钮禁用 + Save Button Disabled + end note +``` + +### 2. 预加载脚本 / Preload Script + +#### preload/index.ts (`src/preload/index.ts`) + +**主要职责 / Main Responsibilities:** +- 安全桥梁,暴露受限 API 到渲染进程 +- 类型安全的 IPC 通道定义 + +**关键代码 / Key Code:** + +```typescript +// 第 89-97 行 / Lines 89-97 +settings: { + getUserType: () => ipcRenderer.invoke('settings:getUserType'), + getSettings: () => ipcRenderer.invoke('settings:getSettings'), + saveSettings: (settings: SettingsData) => + ipcRenderer.invoke('settings:saveSettings', settings), + resetDefaults: () => ipcRenderer.invoke('settings:resetDefaults'), + testErpConnection: () => ipcRenderer.invoke('settings:testErpConnection'), + testDbConnection: () => ipcRenderer.invoke('settings:testDbConnection') +} +``` + +**安全隔离机制 / Security Isolation:** + +```mermaid +graph TB + Renderer[Renderer Process
Untrusted Context] + Preload[Preload Script
Trusted Context] + Main[Main Process
Trusted Context] + + Renderer -->|window.electron| Preload + Preload -->|ipcRenderer.invoke| Main + Main -->|Validation| Preload + Preload -->|Return Promise| Renderer + + style Renderer fill:#ffe1e1 + style Preload fill:#e1ffe1 + style Main fill:#e1e1ff +``` + +### 3. 主进程 / Main Process + +#### settings-handler.ts (`src/main/ipc/settings-handler.ts`) + +**主要职责 / Main Responsibilities:** +- IPC 通道注册和处理 +- 权限验证(基于用户类型) +- 业务逻辑协调 + +**保存设置处理函数 / Save Settings Handler:** + +```typescript +// 第 83-102 行 / Lines 83-102 +ipcMain.handle( + 'settings:saveSettings', + async (_event, settings: SettingsData): Promise => { + try { + log.info('Saving settings') + const success = await configManager.saveAllSettings(settings) + if (success) { + log.info('Settings saved successfully') + return { success: true } + } else { + log.warn('Failed to save settings') + return { success: false, error: '保存设置失败' } + } + } catch (error) { + const message = error instanceof Error ? error.message : 'Unknown error' + log.error('Error saving settings', { error: message }) + return { success: false, error: `保存设置失败:${message}` } + } + } +) +``` + +**用户类型过滤 / User Type Filtering:** + +```typescript +// 第 31-54 行 / Lines 31-54 +function filterSettingsByUserType(settings: SettingsData, userType: UserType): SettingsData { + if (userType === 'Admin') { + return settings // Admin 获取完整配置 + } + + // User 用户获取受限配置 + return { + erp: { + username: settings.erp.username, + password: settings.erp.password, + headless: settings.erp.headless, + url: settings.erp.url, + ignoreHttpsErrors: settings.erp.ignoreHttpsErrors, + autoCloseBrowser: settings.erp.autoCloseBrowser + }, + paths: settings.paths, + execution: settings.execution, + database: settings.database, + extraction: settings.extraction, + validation: settings.validation, + ui: settings.ui + } +} +``` + +**权限控制矩阵 / Permission Control Matrix:** + +| 功能 / Feature | Admin | User | Guest | +|---------------|-------|------|-------| +| 查看所有设置 | ✅ | ⚠️ 部分 | ❌ | +| 保存设置 | ✅ | ✅ | ❌ | +| 恢复默认值 | ✅ | ❌ | ❌ | +| 测试 ERP 连接 | ✅ | ✅ | ❌ | +| 测试数据库连接 | ✅ | ✅ | ❌ | + +### 4. 配置管理服务 / Configuration Manager Service + +#### config-manager.ts (`src/main/services/config/config-manager.ts`) + +**主要职责 / Main Responsibilities:** +- .env 文件读写 +- 配置缓存管理 +- 默认值管理 +- 类型转换和验证 + +**类结构 / Class Structure:** + +```typescript +export class ConfigManager { + private static instance: ConfigManager | null = null // 单例模式 + private envPath: string // .env 文件路径 + private configCache: Map // 内存缓存 + private initialized: boolean = false // 初始化标记 + + // 单例获取方法 + public static getInstance(): ConfigManager + + // 配置读取 + public get(key: string, defaultValue?: string): string | undefined + public getBoolean(key: string, defaultValue?: boolean): boolean + public getNumber(key: string, defaultValue?: number): number + + // 配置写入 + public set(key: string, value: string | number | boolean): void + + // 持久化 + public async save(): Promise + + // 高级操作 + public getAllSettings(): SettingsData + public async saveAllSettings(settings: SettingsData): Promise + public resetToDefaults(): SettingsData +} +``` + +**保存详细流程 / Save Detailed Flow:** + +```mermaid +graph TD + START[saveAllSettings] --> STEP1[更新 ERP 配置 6 字段] + STEP1 --> STEP2[更新数据库配置 7 字段] + STEP2 --> STEP3[更新路径配置 3 字段] + STEP3 --> STEP4[更新提取配置 5 字段] + STEP4 --> STEP5[更新校验配置 5 字段] + STEP5 --> STEP6[更新 UI 配置 3 字段] + STEP6 --> STEP7[更新执行配置 1 字段] + STEP7 --> SAVE[调用 save 方法] + SAVE --> BUILD[构建 .env 内容] + BUILD --> WRITE[写入文件系统] + WRITE --> CHECK{检查结果} + CHECK -->|成功| SUCCESS[返回 true] + CHECK -->|失败| FAILURE[返回 false] +``` + +**.env 文件格式 / .env File Format:** + +```bash +# =========================== +# ERP 系统配置 +# =========================== +ERP_URL=https://68.11.34.30:8082/ +ERP_USERNAME= +ERP_PASSWORD= +ERP_HEADLESS=true +ERP_IGNORE_HTTPS_ERRORS=true +ERP_AUTO_CLOSE_BROWSER=true + +# =========================== +# 数据库配置 - MySQL +# =========================== +DB_TYPE=mysql +DB_NAME=BLD_DB +DB_USERNAME=remote_user +DB_PASSWORD= +DB_MYSQL_HOST=192.168.31.83 +DB_MYSQL_PORT=3306 +DB_MYSQL_CHARSET=utf8mb4 + +# =========================== +# 路径配置 +# =========================== +PATH_DATA_DIR=D:/python/playwrite/data/ +PATH_DEFAULT_OUTPUT=离散备料计划维护_合并.xlsx +PATH_VALIDATION_OUTPUT=物料状态校验结果.xlsx + +# ... 更多配置节 +``` + +--- + +## 数据结构 / Data Structures + +### SettingsData 接口 / Interface Definition + +**类型定义位置 / Type Definition Location:** +`src/main/types/settings.types.ts` (第 136-151 行) + +```typescript +export interface SettingsData { + erp: ErpConfig + database: DatabaseConfig + paths: PathsConfig + extraction: ExtractionConfig + validation: ValidationConfig + ui: UiConfig + execution: ExecutionConfig +} +``` + +### 完整数据结构树 / Complete Data Structure Tree + +```mermaid +graph TB + Settings[SettingsData] + + Settings --> Erp[ErpConfig] + Erp --> Erp1[url: string] + Erp --> Erp2[username: string] + Erp --> Erp3[password: string] + Erp --> Erp4[headless: boolean] + Erp --> Erp5[ignoreHttpsErrors: boolean] + Erp --> Erp6[autoCloseBrowser: boolean] + + Settings --> DB[DatabaseConfig] + DB --> DB1[dbType: 'mysql'|'sqlserver'] + DB --> DB2[server: string] + DB --> DB3[mysqlHost: string] + DB --> DB4[mysqlPort: number] + DB --> DB5[database: string] + DB --> DB6[username: string] + DB --> DB7[password: string] + + Settings --> Paths[PathsConfig] + Paths --> Paths1[dataDir: string] + Paths --> Paths2[defaultOutput: string] + Paths --> Paths3[validationOutput: string] + + Settings --> Extract[ExtractionConfig] + Extract --> Extract1[batchSize: number] + Extract --> Extract2[verbose: boolean] + Extract --> Extract3[autoConvert: boolean] + Extract --> Extract4[mergeBatches: boolean] + Extract --> Extract5[enableDbPersistence: boolean] + + Settings --> Valid[ValidationConfig] + Valid --> Valid1[dataSource: ValidationDataSource] + Valid --> Valid2[batchSize: number] + Valid --> Valid3[matchMode: MatchMode] + Valid --> Valid4[enableCrud: boolean] + Valid --> Valid5[defaultManager: string] + + Settings --> UI[UiConfig] + UI --> UI1[fontFamily: string] + UI --> UI2[fontSize: number] + UI --> UI3[productionIdInputWidth: number] + + Settings --> Exec[ExecutionConfig] + Exec --> Exec1[dryRun: boolean] + + style Settings fill:#e1f5ff + style Erp fill:#ffe1f5 + style DB fill:#e1ffe1 + style Paths fill:#fff4e1 + style Extract fill:#f5e1ff + style Valid fill:#ffe1e1 + style UI fill:#e1f5ff + style Exec fill:#f5f5f5 +``` + +### IPC 通信数据格式 / IPC Communication Data Format + +**请求格式 / Request Format:** +```json +{ + "erp": { + "url": "https://68.11.34.30:8082/", + "username": "admin", + "password": "password123", + "headless": true, + "ignoreHttpsErrors": true, + "autoCloseBrowser": true + }, + "database": { ... }, + "paths": { ... }, + "extraction": { ... }, + "validation": { ... }, + "ui": { ... }, + "execution": { ... } +} +``` + +**响应格式 / Response Format:** +```json +// 成功 / Success +{ + "success": true +} + +// 失败 / Failure +{ + "success": false, + "error": "保存设置失败:Access denied" +} +``` + +--- + +## 错误处理机制 / Error Handling Mechanism + +### 错误处理层次 / Error Handling Layers + +```mermaid +graph TB + subgraph "UI Layer" + UI_TRY[try-catch in handleSaveSettings] + UI_MSG[showMessage display] + end + + subgraph "IPC Layer" + IPC_TRY[try-catch in handler] + IPC_LOG[Structured logging] + IPC_RETURN[Return error object] + end + + subgraph "Service Layer" + SVC_TRY[try-catch in save] + SVC_LOG[Console error log] + SVC_RETURN[Return false] + end + + subgraph "File System" + FS_CHECK[File exists check] + FS_WRITE[Write with error handling] + end + + UI_TRY -->|Catch| UI_MSG + IPC_TRY -->|Catch| IPC_LOG --> IPC_RETURN + SVC_TRY -->|Catch| SVC_LOG --> SVC_RETURN + FS_WRITE -->|Error| SVC_TRY + + style UI_TRY fill:#ffe1e1 + style IPC_TRY fill:#ffe1e1 + style SVC_TRY fill:#ffe1e1 +``` + +### 错误场景分析 / Error Scenario Analysis + +| 错误场景 / Error Scenario | 触发位置 / Location | 处理方式 / Handling | 用户反馈 / User Feedback | +|--------------------------|-------------------|-------------------|----------------------| +| IPC 通信失败 | Renderer | try-catch | 显示"保存设置时发生错误" | +| 权限不足 | Main Process | 检查 UserType | 返回权限错误信息 | +| 文件写入失败 | ConfigManager | fs.writeFileSync 捕获 | 返回"保存设置失败" | +| 无效数据类型 | IPC Handler | TypeScript 类型检查 | 返回验证错误 | +| 磁盘空间不足 | File System | OS 异常捕获 | 返回系统错误信息 | + +### 日志记录策略 / Logging Strategy + +```typescript +// Main Process 结构化日志 / Structured Logging +log.info('Saving settings') +log.info('Settings saved successfully') +log.warn('Failed to save settings') +log.error('Error saving settings', { error: message }) +``` + +**日志级别使用 / Log Level Usage:** +- `info`: 正常操作流程 +- `warn`: 潜在问题(如保存失败但未崩溃) +- `error`: 严重错误(如异常抛出) + +--- + +## 安全考虑 / Security Considerations + +### 安全机制层级 / Security Layers + +```mermaid +graph TB + L1[Layer 1: Context Isolation
渲染进程隔离] + L2[Layer 2: contextBridge
受限 API 暴露] + L3[Layer 3: User Type Filtering
基于角色的访问控制] + L4[Layer 4: File System Permissions
.env 文件保护] + + L1 --> L2 --> L3 --> L4 + + style L1 fill:#e1f5ff + style L2 fill:#fff4e1 + style L3 fill:#e1ffe1 + style L4 fill:#ffe1f5 +``` + +### 关键安全措施 / Key Security Measures + +1. **密码明文存储风险 / Password Storage Risk** + - ⚠️ 当前:密码以明文形式存储在 .env 文件中 + - 🔒 建议:实现加密存储机制 + +2. **用户权限隔离 / User Permission Isolation** + - ✅ 实现:基于用户类型过滤可见配置 + - ✅ 实现:Guest 用户无法访问设置页面 + +3. **IPC 通信安全 / IPC Communication Security** + - ✅ 实现:使用 `contextBridge` 而非直接暴露 + - ✅ 实现:类型安全的 TypeScript 接口 + +4. **文件系统访问 / File System Access** + - ✅ 实现:.env 文件仅主进程可访问 + - ⚠️ 风险:文件权限取决于操作系统 + +### 敏感数据流向 / Sensitive Data Flow + +```mermaid +sequenceDiagram + participant User as 用户输入 + participant UI as UI State (内存) + participant IPC as IPC Channel + participant Cache as Config Cache + participant File as .env File + + User->>UI: password = "secret123" + UI->>IPC: JSON 传输 (未加密) + IPC->>Cache: Map.set('erp.password', 'secret123') + Cache->>File: 写入明文到磁盘 + + Note over File: ⚠️ 安全风险:
密码以明文形式持久化 +``` + +--- + +## 技术实现细节 / Technical Implementation Details + +### 文件位置索引 / File Location Index + +| 组件 / Component | 文件路径 / File Path | 关键行数 / Key Lines | +|-----------------|---------------------|-------------------| +| UI 组件 | `src/renderer/src/pages/SettingsPage.tsx` | 61-73 (保存处理) | +| 预加载脚本 | `src/preload/index.ts` | 89-97 (API 定义) | +| IPC 处理器 | `src/main/ipc/settings-handler.ts` | 83-102 (保存处理) | +| 配置管理器 | `src/main/services/config/config-manager.ts` | 437-483 (保存方法) | +| 类型定义 | `src/main/types/settings.types.ts` | 136-171 (接口定义) | +| IPC 注册 | `src/main/ipc/index.ts` | 导入 settings-handler | + +### 性能特性 / Performance Characteristics + +1. **异步操作 / Async Operations** + - 所有 IPC 调用使用 `async/await` 模式 + - 避免阻塞主进程事件循环 + +2. **内存优化 / Memory Optimization** + - 使用 Map 缓存配置,减少文件读取 + - 按需加载配置项 + +3. **写入策略 / Write Strategy** + - 每次保存完整重写 .env 文件 + - 原子写入(writeFileSync) + +### 依赖关系图 / Dependency Graph + +```mermaid +graph TD + A[SettingsPage.tsx] -->|imports| B[lucide-react] + A -->|uses| C[window.electron.settings] + + C -->|exposed by| D[preload/index.ts] + D -->|imports| E[electron API] + D -->|imports| F[SettingsData Type] + + G[settings-handler.ts] -->|imports| H[ipcMain] + G -->|imports| I[ConfigManager] + G -->|imports| J[SessionManager] + G -->|imports| K[Logger] + + I -->|imports| L[fs/path] + I -->|imports| M[SettingsData Type] + I -->|imports| N[DEFAULT_SETTINGS] + + style A fill:#e1f5ff + style D fill:#fff4e1 + style G fill:#ffe1f5 + style I fill:#e1ffe1 +``` + +### 关键代码片段分析 / Key Code Snippet Analysis + +**1. 状态更新逻辑 / State Update Logic** + +```typescript +// SettingsPage.tsx 第 50-59 行 +const updateSettings = (category: string, key: string, value: any) => { + setSettings((prev) => ({ + ...prev, + [category]: { + ...(prev as any)[category], + [key]: value + } + })) + setIsModified(true) // 标记为已修改 +} +``` + +**设计要点 / Design Points:** +- 不可变更新模式(Immutable Update Pattern) +- 使用展开运算符保持对象引用 +- 自动启用保存按钮 + +**2. 配置保存逻辑 / Configuration Save Logic** + +```typescript +// config-manager.ts 第 437-483 行 +public async saveAllSettings(settings: SettingsData): Promise { + // 批量更新缓存 (40+ 字段) + this.set('erp.url', settings.erp.url) + this.set('erp.username', settings.erp.username) + // ... 更多字段 + + // 同步写入文件 + return this.save() +} +``` + +**设计要点 / Design Points:** +- 先更新内存,后写入磁盘 +- 失败时缓存保持不变 +- 返回布尔值表示成功/失败 + +**3. .env 文件生成逻辑 / .env File Generation** + +```typescript +// config-manager.ts 第 179-345 行 +public async save(): Promise { + const lines: string[] = [] + + // 构建格式化的 .env 内容 + lines.push('# ===========================') + lines.push('# ERP 系统配置') + lines.push('# ===========================') + lines.push(`ERP_URL=${this.configCache.get('erp.url') || DEFAULT_SETTINGS.erp.url}`) + + const content = lines.join('\n') + fs.writeFileSync(this.envPath, content, 'utf-8') + return true +} +``` + +**设计要点 / Design Points:** +- 添加注释分隔符提高可读性 +- 使用默认值作为后备 +- 同步写入确保一致性 + +--- + +## 扩展与改进建议 / Extension and Improvement Suggestions + +### 短期改进 / Short-term Improvements + +1. **输入验证 / Input Validation** + - 添加 URL 格式验证 + - 密码强度检查 + - 端口号范围验证 + +2. **用户体验 / User Experience** + - 添加保存进度指示器 + - 实现自动保存功能 + - 添加配置导入/导出 + +3. **错误处理 / Error Handling** + - 更详细的错误消息 + - 错误恢复建议 + - 错误日志导出 + +### 长期改进 / Long-term Improvements + +1. **安全性增强 / Security Enhancement** + ```typescript + // 建议实现密码加密 + interface SecureSettingsData extends SettingsData { + erp: { + ...ErpConfig + encryptedPassword: string // 替代明文密码 + } + } + ``` + +2. **配置版本控制 / Configuration Versioning** + - 实现配置历史记录 + - 支持回滚到之前版本 + - 配置变更审计日志 + +3. **实时配置重载 / Live Config Reload** + - 监听 .env 文件变化 + - 自动重载配置 + - 通知相关服务更新 + +--- + +## 测试建议 / Testing Recommendations + +### 单元测试 / Unit Tests + +```typescript +// 测试用例示例 +describe('ConfigManager', () => { + it('should save settings successfully', async () => { + const manager = ConfigManager.getInstance() + const settings: SettingsData = { /* mock data */ } + const result = await manager.saveAllSettings(settings) + expect(result).toBe(true) + }) + + it('should handle file write errors', async () => { + // Mock fs.writeFileSync to throw error + const result = await manager.saveAllSettings(settings) + expect(result).toBe(false) + }) +}) +``` + +### 集成测试 / Integration Tests + +```typescript +describe('Settings Save Flow', () => { + it('should complete full save cycle', async () => { + // 1. User modifies settings + // 2. Clicks save button + // 3. Verifies .env file updated + // 4. Confirms UI feedback + }) +}) +``` + +--- + +## 附录 / Appendix + +### 完整配置字段列表 / Complete Configuration Field List + +| 类别 / Category | 字段数 / Field Count | 字段列表 / Field List | +|---------------|---------------------|-------------------| +| ERP | 6 | url, username, password, headless, ignoreHttpsErrors, autoCloseBrowser | +| Database | 7 | dbType, server, mysqlHost, mysqlPort, database, username, password | +| Paths | 3 | dataDir, defaultOutput, validationOutput | +| Extraction | 5 | batchSize, verbose, autoConvert, mergeBatches, enableDbPersistence | +| Validation | 5 | dataSource, batchSize, matchMode, enableCrud, defaultManager | +| UI | 3 | fontFamily, fontSize, productionIdInputWidth | +| Execution | 1 | dryRun | +| **总计 / Total** | **30** | | + +### 相关文档 / Related Documentation + +- [Electron Security Guidelines](https://www.electronjs.org/docs/latest/tutorial/security) +- [IPC 通信最佳实践](https://www.electronjs.org/docs/latest/tutorial/ipc) +- [环境变量管理规范](.env.example) + +### 版本历史 / Version History + +| 版本 / Version | 日期 / Date | 变更 / Changes | +|---------------|------------|--------------| +| 1.0 | 2025-03-03 | 初始版本 / Initial version | + +--- + +**文档生成时间 / Document Generated:** 2025-03-03 +**最后更新 / Last Updated:** 2025-03-03 +**维护者 / Maintainer:** ERPAuto Development Team