diff --git a/CLAUDE.md b/CLAUDE.md index 1239be7..bd042bf 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -27,8 +27,9 @@ The BOM Configuration system follows a modular architecture with clear separatio The BOM Extraction system follows a modular architecture with clear separation of concerns: - **M09_BOMExtractor.bas** - Main orchestration and entry point. Run `RunBOMExtraction()` to execute the full workflow. Implements two-phase validation (collect → validate) and generates output worksheets. -- **M06_ModelParser.bas** - Product model string parsing. Extracts parameters from full model strings (e.g., `YTHN-100.A0.531.G123.M04.Y3|BP-088.2312.B09.0A3`). -- **M07_BOMMatcher.bas** - BOM library matching. Matches extracted parameters against BOM库.xlsx worksheets using parameter-based rules (empty cells = wildcard, "!=" prefix = negative match, fjgn field = substring matching). +- **M06_ModelParser.bas** - Product model string parsing. Extracts parameters from full model strings (e.g., `YTHN-100.A0.531.G123.M04.Y3|BP-088.2312.B09.0A3`). Applies value mapping via M06A_Mapper. +- **M06A_Mapper.bas** - Value mapping module. Maps azxs and lcfw parameters to their descriptive values from "对照表" worksheet. Returns dual-value format (raw,mapped) for flexible matching. +- **M07_BOMMatcher.bas** - BOM library matching. Matches extracted parameters against BOM库.xlsx worksheets using parameter-based rules (empty cells = wildcard, "!=" prefix = negative match, fjgn field = substring matching, dual-value matching for mapped parameters). - **M08_ComponentProcessor.bas** - Special handling for "部件" (component) materials. Handles component inventory logic (component vs sub-components selection). - **M06B_TestRunner.bas** - Unit testing framework for BOM Extraction system. Run tests in VBA Immediate window. @@ -49,8 +50,14 @@ New Excel Workbook + Error Report **BOM Extraction System Flow**: ``` "产品型号" worksheet → M09_BOMExtractor.RunBOMExtraction() - → M06_ModelParser.ParseProductModel() (extract parameters: azxs, bkxs, gclj, jycz, lcfw, fjgn) - → M07_BOMMatcher.MatchBOMRecord() (match in BOM库.xlsx worksheets) + → M06A_Mapper.InitMapper() (load mapping table from "对照表" worksheet) + → M06_ModelParser.ParseProductModel() + → Extract raw parameters: azxs="A0", lcfw="M01" + → M06A_Mapper.MapAzxs() / MapLcfw() + → Return dual values: azxs="A0,径向", lcfw="M01,低压" + → M07_BOMMatcher.MatchBOMRecord() + → EvaluateCellCondition() supports dual-value matching + → Match if BOM库 contains "A0" OR "径向" → M08_ComponentProcessor.ProcessComponentRecord() (special component handling if applicable) → Two-phase validation (collect all matches → validate with cross-worksheet rules) → "BOM提取结果" worksheet + "错误报告" worksheet @@ -149,6 +156,34 @@ Product models follow a structured format that the parser can extract parameters - `gclj` and `jycz` are extracted from single segment (last char is `jycz`) - `fjgn` combines all segments from position 5 onwards with comma separator +### Value Mapping (M06A_Mapper) + +After parsing raw parameter values, the system applies value mapping from "对照表" worksheet: + +**Dual-Value Format**: +- Parameters are stored as "raw,mapped" to support flexible matching +- Example: `azxs="A0,径向"` means the parameter can match either "A0" or "径向" +- Example: `lcfw="M01,低压"` means the parameter can match either "M01" or "低压" + +**Mapping Table Configuration** (M04_Config): +- `MAPPING_SHEET_NAME` = "对照表" +- `MAPPING_COL_LCFW_KEY` = 1 (A列) - lcfw raw values +- `MAPPING_COL_LCFW_VAL` = 2 (B列) - lcfw mapped values +- `MAPPING_COL_AZXS_KEY` = 4 (D列) - azxs raw values +- `MAPPING_COL_AZXS_VAL` = 5 (E列) - azxs mapped values +- `MAPPING_START_ROW` = 3 + +**Graceful Degradation**: +- If "对照表" worksheet is not found, system logs a warning and uses raw values only +- If a value is not found in mapping table, only the raw value is used (no error) + +**Example Mapping Data**: +| Row | A列 (lcfw key) | B列 (lcfw val) | D列 (azxs key) | E列 (azxs val) | +|-----|----------------|----------------|----------------|----------------| +| 3 | M01 | 低压 | A0 | 径向 | +| 4 | M12 | 高压 | AT | 径向 | +| 5 | - | - | B0 | 下轴向 | + ### BOM Matching Rules (M07_BOMMatcher) The system matches extracted parameters against BOM库.xlsx worksheets using these rules: @@ -157,13 +192,23 @@ The system matches extracted parameters against BOM库.xlsx worksheets using the - **Empty cell**: Wildcard - matches all values - **"!=" prefix**: Negative match - matches when parameter ≠ value (e.g., `!=A0`) - **fjgn field**: Substring matching - matches when fjgn list contains cell value (InStr check) +- **Dual-value parameters**: Matches if ANY value matches (e.g., `azxs="A0,径向"` matches both `azxs=A0` and `azxs=径向`) - **Normal values**: Exact match - parameter must equal cell value **Logic**: AND across all parameters - all conditions must be satisfied -**Example**: BOM库 row has `azxs=A0`, `bkxs=`, `gclj=G12`, `fjgn=N1` -- Matches: `azxs=A0`, `bkxs=531`, `gclj=G12`, `fjgn=N1,N2` -- Reason: `azxs` matches exactly, `bkxs` is empty (wildcard), `gclj` matches exactly, `fjgn` contains "N1" +**Examples**: + +1. **Dual-value matching**: + - Parameter: `azxs="A0,径向"` + - BOM库 has `azxs=径向` → MATCH (because "径向" is in the parameter) + - BOM库 has `azxs=A0` → MATCH (because "A0" is in the parameter) + - BOM库 has `azxs=AT` → NO MATCH + +2. **Standard matching**: + - BOM库 row has `azxs=A0`, `bkxs=`, `gclj=G12`, `fjgn=N1` + - Matches: `azxs=A0,径向`, `bkxs=531`, `gclj=G12`, `fjgn=N1,N2` + - Reason: `azxs` contains "A0", `bkxs` is empty (wildcard), `gclj` matches exactly, `fjgn` contains "N1" ### Component Special Handling (M08_ComponentProcessor) @@ -223,7 +268,8 @@ The system uses two-phase validation to enable cross-worksheet validation: 1. Open the main Excel workbook (e.g., `YTHN-100.xlsm`) 2. Ensure "产品型号" worksheet exists with product model data 3. Ensure `BOM库.xlsx` is in the same directory as the main workbook -4. Run `M09_BOMExtractor.RunBOMExtraction()` or execute from the Excel interface +4. (Optional) Ensure "对照表" worksheet exists for azxs/lcfw value mapping +5. Run `M09_BOMExtractor.RunBOMExtraction()` or execute from the Excel interface **Output**: - "BOM提取结果" worksheet - Contains extracted materials with parameters @@ -288,6 +334,14 @@ The project includes custom skills in `.claude/skills/`: - `OUTPUT_SHEET_NAME` = "BOM提取结果" - Output worksheet name - `BOMLIB_START_ROW` = 2 - BOM库 data starts from row 2 (row 1 is header) +**Mapping Table Configuration**: +- `MAPPING_SHEET_NAME` = "对照表" - Mapping table worksheet name +- `MAPPING_COL_LCFW_KEY` = 1 - Column A for lcfw raw values +- `MAPPING_COL_LCFW_VAL` = 2 - Column B for lcfw mapped values +- `MAPPING_COL_AZXS_KEY` = 4 - Column D for azxs raw values +- `MAPPING_COL_AZXS_VAL` = 5 - Column E for azxs mapped values +- `MAPPING_START_ROW` = 3 - Mapping data starts from row 3 + **Input Column Configuration**: - `INPUT_COL_MODEL` = "型号" - `INPUT_COL_PRODUCT_MODEL` = "产品型号" @@ -369,6 +423,7 @@ AutoBOM/ │ │ ├── M04_Config.bas # Shared - Constants and configuration │ │ ├── M05_PreProcessor.bas # BOM Configuration System - Preprocessing │ │ ├── M06_ModelParser.bas # BOM Extraction System - Model parsing +│ │ ├── M06A_Mapper.bas # BOM Extraction System - Value mapping (NEW) │ │ ├── M06B_TestRunner.bas # BOM Extraction System - Unit tests │ │ ├── M07_BOMMatcher.bas # BOM Extraction System - BOM matching │ │ ├── M08_ComponentProcessor.bas # BOM Extraction System - Component handling diff --git a/VBA_BOMConverter/Modules/M04_Config.bas b/VBA_BOMConverter/Modules/M04_Config.bas index 1963af0..10c413a 100644 --- a/VBA_BOMConverter/Modules/M04_Config.bas +++ b/VBA_BOMConverter/Modules/M04_Config.bas @@ -74,6 +74,16 @@ Public Function GetBOMConditionFields() As Variant ) End Function +' ------------------------------------------------------------------------------ +' 映射表配置常量 +' ------------------------------------------------------------------------------ +Public Const MAPPING_SHEET_NAME As String = "对照表" +Public Const MAPPING_COL_LCFW_KEY As Long = 1 ' A列 - lcfw原始值 +Public Const MAPPING_COL_LCFW_VAL As Long = 2 ' B列 - lcfw映射值 +Public Const MAPPING_COL_AZXS_KEY As Long = 4 ' D列 - azxs原始值 +Public Const MAPPING_COL_AZXS_VAL As Long = 5 ' E列 - azxs映射值 +Public Const MAPPING_START_ROW As Long = 3 ' 数据起始行 + ' 检查列名是否为条件列 Public Function IsConditionField(ByVal colName As String) As Boolean Dim fields As Variant diff --git a/VBA_BOMConverter/Modules/M06A_Mapper.bas b/VBA_BOMConverter/Modules/M06A_Mapper.bas new file mode 100644 index 0000000..6dee614 --- /dev/null +++ b/VBA_BOMConverter/Modules/M06A_Mapper.bas @@ -0,0 +1,210 @@ +' ============================================================================== +' 模块: M06A_Mapper +' 职责: 值映射模块,为BOM Extraction System提供azxs和lcfw参数映射 +' +' 功能: +' - 从"对照表"工作表加载映射数据 +' - 将原始值转换为"原始值,映射值"格式 +' - 支持azxs(安装形式)和lcfw(量程范围)映射 +' +' 使用场景: +' 产品型号解析后,调用映射器为参数添加映射值,支持双值匹配 +' +' 示例: +' 输入: azxs="A0" +' 输出: azxs="A0,径向" +' +' BOM库中azxs="A0"或"径向"均可匹配成功 +' ============================================================================== +Option Explicit + +' 模块级常量 - 映射表配置 +Private Const MAPPING_COL_LCFW_KEY As Long = 1 ' A列 - lcfw原始值 +Private Const MAPPING_COL_LCFW_VAL As Long = 2 ' B列 - lcfw映射值 +Private Const MAPPING_COL_AZXS_KEY As Long = 4 ' D列 - azxs原始值 +Private Const MAPPING_COL_AZXS_VAL As Long = 5 ' E列 - azxs映射值 +Private Const MAPPING_START_ROW As Long = 3 ' 数据起始行 + +' 模块级变量 +Private g_LcfwMapping As Object ' Scripting.Dictionary - lcfw映射表 +Private g_AzxsMapping As Object ' Scripting.Dictionary - azxs映射表 +Private g_Logger As clsErrorLogger +Private g_IsInitialized As Boolean + +' ------------------------------------------------------------------------------ +' 初始化映射器 +' +' 输入: +' logger - 错误记录器 +' wsMapping - 对照表工作表 +' +' 说明: +' 从对照表工作表加载lcfw和azxs映射数据 +' - lcfw映射: A列:B列 (M01->低压, M12->高压) +' - azxs映射: D列:E列 (A0->径向, AT->径向, etc.) +' ------------------------------------------------------------------------------ +Public Sub InitMapper(logger As clsErrorLogger, wsMapping As Worksheet) + Set g_Logger = logger + Set g_LcfwMapping = CreateObject("Scripting.Dictionary") + Set g_AzxsMapping = CreateObject("Scripting.Dictionary") + + ' 从[对照表]加载映射 + Call LoadLcfwMapping(wsMapping) + Call LoadAzxsMapping(wsMapping) + + g_IsInitialized = True +End Sub + +' ------------------------------------------------------------------------------ +' 检查是否已初始化 +' ------------------------------------------------------------------------------ +Public Function IsInitialized() As Boolean + IsInitialized = g_IsInitialized +End Function + +' ------------------------------------------------------------------------------ +' 映射azxs值 +' +' 输入: +' rawValue - 原始azxs值(如"A0") +' +' 输出: +' String - "raw,mapped"格式(如"A0,径向")或原始值(如果未找到映射) +' +' 示例: +' MapAzxs("A0") = "A0,径向" +' MapAzxs("AT") = "AT,径向" +' MapAzxs("XX") = "XX" (未找到映射) +' ------------------------------------------------------------------------------ +Public Function MapAzxs(ByVal rawValue As String) As String + rawValue = Trim(rawValue) + + ' 如果未初始化或值为空,直接返回原始值 + If Not g_IsInitialized Or Len(rawValue) = 0 Then + MapAzxs = rawValue + Exit Function + End If + + ' 查找映射值 + If g_AzxsMapping.Exists(rawValue) Then + Dim mappedValue As String + mappedValue = g_AzxsMapping(rawValue) + MapAzxs = rawValue & "," & mappedValue + Else + ' 未找到映射,返回原始值 + MapAzxs = rawValue + End If +End Function + +' ------------------------------------------------------------------------------ +' 映射lcfw值 +' +' 输入: +' rawValue - 原始lcfw值(如"M01") +' +' 输出: +' String - "raw,mapped"格式(如"M01,低压")或原始值(如果未找到映射) +' +' 示例: +' MapLcfw("M01") = "M01,低压" +' MapLcfw("M12") = "M12,高压" +' MapLcfw("XX") = "XX" (未找到映射) +' ------------------------------------------------------------------------------ +Public Function MapLcfw(ByVal rawValue As String) As String + rawValue = Trim(rawValue) + + ' 如果未初始化或值为空,直接返回原始值 + If Not g_IsInitialized Or Len(rawValue) = 0 Then + MapLcfw = rawValue + Exit Function + End If + + ' 查找映射值 + If g_LcfwMapping.Exists(rawValue) Then + Dim mappedValue As String + mappedValue = g_LcfwMapping(rawValue) + MapLcfw = rawValue & "," & mappedValue + Else + ' 未找到映射,返回原始值 + MapLcfw = rawValue + End If +End Function + +' ------------------------------------------------------------------------------ +' 加载lcfw映射(从A列:B列,列1:2) +' +' 数据格式: +' Row 3+: M01 | 低压 +' M12 | 高压 +' etc. +' ------------------------------------------------------------------------------ +Private Sub LoadLcfwMapping(wsMapping As Worksheet) + Dim lastRow As Long + lastRow = wsMapping.Cells(wsMapping.Rows.count, 1).End(xlUp).row + + Dim i As Long + Dim key As String, val As String + + ' 从第3行开始读取 + For i = MAPPING_START_ROW To lastRow + key = Trim(CStr(wsMapping.Cells(i, MAPPING_COL_LCFW_KEY).Value)) + val = Trim(CStr(wsMapping.Cells(i, MAPPING_COL_LCFW_VAL).Value)) + + If Len(key) > 0 And Len(val) > 0 Then + If Not g_LcfwMapping.Exists(key) Then + g_LcfwMapping.Add key, val + End If + End If + Next i +End Sub + +' ------------------------------------------------------------------------------ +' 加载azxs映射(从D列:E列,列4:5) +' +' 数据格式: +' Row 3+: A0 | 径向 +' AT | 径向 +' B0 | 下轴向 +' etc. +' ------------------------------------------------------------------------------ +Private Sub LoadAzxsMapping(wsMapping As Worksheet) + Dim lastRow As Long + lastRow = wsMapping.Cells(wsMapping.Rows.count, MAPPING_COL_AZXS_KEY).End(xlUp).row + + Dim i As Long + Dim key As String, val As String + + ' 从第3行开始读取 + For i = MAPPING_START_ROW To lastRow + key = Trim(CStr(wsMapping.Cells(i, MAPPING_COL_AZXS_KEY).Value)) + val = Trim(CStr(wsMapping.Cells(i, MAPPING_COL_AZXS_VAL).Value)) + + If Len(key) > 0 And Len(val) > 0 Then + If Not g_AzxsMapping.Exists(key) Then + g_AzxsMapping.Add key, val + End If + End If + Next i +End Sub + +' ------------------------------------------------------------------------------ +' 测试辅助函数:获取lcfw映射值 +' ------------------------------------------------------------------------------ +Public Function GetLcfwMappedValue(key As String) As String + If g_LcfwMapping.Exists(key) Then + GetLcfwMappedValue = g_LcfwMapping(key) + Else + GetLcfwMappedValue = "" + End If +End Function + +' ------------------------------------------------------------------------------ +' 测试辅助函数:获取azxs映射值 +' ------------------------------------------------------------------------------ +Public Function GetAzxsMappedValue(key As String) As String + If g_AzxsMapping.Exists(key) Then + GetAzxsMappedValue = g_AzxsMapping(key) + Else + GetAzxsMappedValue = "" + End If +End Function diff --git a/VBA_BOMConverter/Modules/M06_ModelParser.bas b/VBA_BOMConverter/Modules/M06_ModelParser.bas index 8fe1a88..463eac6 100644 --- a/VBA_BOMConverter/Modules/M06_ModelParser.bas +++ b/VBA_BOMConverter/Modules/M06_ModelParser.bas @@ -96,27 +96,63 @@ Public Function ParseProductModel(ByVal modelString As String) As Object ' 步骤5: 提取各参数 ' 注意:segments(0)是"型号-公称外径",需要从segments(1)开始提取参数 + + ' 提取原始值 + Dim azxsRaw As String, bkxsRaw As String + Dim gcljRaw As String, jyczRaw As String + Dim lcfwRaw As String, fjgnRaw As String + If UBound(segments) - LBound(segments) + 1 >= 2 Then - params("azxs") = ExtractAzxs(segments(1)) + azxsRaw = ExtractAzxs(segments(1)) End If If UBound(segments) - LBound(segments) + 1 >= 3 Then - params("bkxs") = ExtractBkxs(segments(2)) + bkxsRaw = ExtractBkxs(segments(2)) End If If UBound(segments) - LBound(segments) + 1 >= 4 Then - Dim gclj As String, jycz As String - Call ExtractGcljAndJycz(segments(3), gclj, jycz) - params("gclj") = gclj - params("jycz") = jycz + Call ExtractGcljAndJycz(segments(3), gcljRaw, jyczRaw) End If If UBound(segments) - LBound(segments) + 1 >= 5 Then - params("lcfw") = ExtractLcfw(segments(4)) + lcfwRaw = ExtractLcfw(segments(4)) End If If UBound(segments) - LBound(segments) + 1 >= 6 Then - params("fjgn") = ExtractFjgn(segments, 5) + fjgnRaw = ExtractFjgn(segments, 5) + End If + + ' 步骤6: 应用映射(如果映射器已初始化) + If M06A_Mapper.IsInitialized() Then + ' 应用azxs和lcfw映射,返回双值格式(raw,mapped) + If Len(azxsRaw) > 0 Then + params("azxs") = M06A_Mapper.MapAzxs(azxsRaw) + End If + If Len(lcfwRaw) > 0 Then + params("lcfw") = M06A_Mapper.MapLcfw(lcfwRaw) + End If + Else + ' 映射器未初始化,使用原始值 + If Len(azxsRaw) > 0 Then + params("azxs") = azxsRaw + End If + If Len(lcfwRaw) > 0 Then + params("lcfw") = lcfwRaw + End If + End If + + ' 添加其他参数(不需要映射) + If Len(bkxsRaw) > 0 Then + params("bkxs") = bkxsRaw + End If + If Len(gcljRaw) > 0 Then + params("gclj") = gcljRaw + End If + If Len(jyczRaw) > 0 Then + params("jycz") = jyczRaw + End If + If Len(fjgnRaw) > 0 Then + params("fjgn") = fjgnRaw End If Set ParseProductModel = params diff --git a/VBA_BOMConverter/Modules/M07_BOMMatcher.bas b/VBA_BOMConverter/Modules/M07_BOMMatcher.bas index 76189cf..00bc5e9 100644 --- a/VBA_BOMConverter/Modules/M07_BOMMatcher.bas +++ b/VBA_BOMConverter/Modules/M07_BOMMatcher.bas @@ -257,7 +257,27 @@ Public Function EvaluateCellCondition( _ Exit Function End If - ' 精确匹配 + ' 支持双值匹配(如 "A0,径向" 可以匹配 "A0" 或 "径向") + ' 检查参数值是否包含逗号(表示有映射值) + If InStr(paramValue, ",") > 0 Then + Dim paramValues As Variant + paramValues = Split(paramValue, ",") + + ' 只要参数中的任意一个值匹配单元格值,即认为匹配 + Dim i As Long + For i = LBound(paramValues) To UBound(paramValues) + If Trim(CStr(paramValues(i))) = cellStr Then + EvaluateCellCondition = True + Exit Function + End If + Next i + + ' 所有值都不匹配 + EvaluateCellCondition = False + Exit Function + End If + + ' 单值精确匹配 EvaluateCellCondition = (paramValue = cellStr) Exit Function diff --git a/VBA_BOMConverter/Modules/M09_BOMExtractor.bas b/VBA_BOMConverter/Modules/M09_BOMExtractor.bas index 711b995..76170bc 100644 --- a/VBA_BOMConverter/Modules/M09_BOMExtractor.bas +++ b/VBA_BOMConverter/Modules/M09_BOMExtractor.bas @@ -21,6 +21,9 @@ ' ============================================================================== Option Explicit +' 模块级常量 - 映射表配置 +Private Const MAPPING_SHEET_NAME As String = "对照表" + ' 模块级变量 Private g_Logger As clsErrorLogger Private g_BOMWorkbook As Workbook @@ -92,6 +95,18 @@ Public Function RunBOMExtraction() As String M07_BOMMatcher.InitBOMMatcher g_Logger M08_ComponentProcessor.InitComponentProcessor g_Logger + ' 初始化映射器(新增) + If WorksheetExists(MAPPING_SHEET_NAME) Then + Dim wsMapping As Worksheet + Set wsMapping = ThisWorkbook.Sheets(MAPPING_SHEET_NAME) + M06A_Mapper.InitMapper g_Logger, wsMapping + Else + If Not g_Logger Is Nothing Then + g_Logger.RecordWarning 0, "M09.RunBOMExtraction", "MappingTableMissing", _ + "未找到对照表工作表,azxs和lcfw将仅使用原始值匹配", "" + End If + End If + ' 步骤4: 处理每个产品型号 Application.StatusBar = "正在处理产品型号..." @@ -1026,4 +1041,21 @@ Private Function OpenBOMLibrary() As Workbook ErrorHandler: Set OpenBOMLibrary = Nothing +End Function + +' ------------------------------------------------------------------------------ +' 检查工作表是否存在 +' +' 输入: +' sheetName - 工作表名称 +' +' 输出: +' Boolean - True表示工作表存在,False表示不存在 +' ------------------------------------------------------------------------------ +Private Function WorksheetExists(ByVal sheetName As String) As Boolean + On Error Resume Next + Dim ws As Worksheet + Set ws = ThisWorkbook.Sheets(sheetName) + WorksheetExists = Not ws Is Nothing + On Error GoTo 0 End Function \ No newline at end of file diff --git a/docs/RunBOMExtraction_运行机制详解.md b/docs/RunBOMExtraction_运行机制详解.md index c9d081d..a9ddea0 100644 --- a/docs/RunBOMExtraction_运行机制详解.md +++ b/docs/RunBOMExtraction_运行机制详解.md @@ -50,6 +50,7 @@ graph TB subgraph "处理层" M06[M06_ModelParser.bas
型号解析器] + M06A[M06A_Mapper.bas
值映射器] M07[M07_BOMMatcher.bas
BOM匹配器] M08[M08_ComponentProcessor.bas
部件处理器] end @@ -61,6 +62,7 @@ graph TB subgraph "数据源" Input[产品型号工作表] + Mapping[对照表工作表] BOMLib[BOM库.xlsx] end @@ -70,12 +72,15 @@ graph TB M01 --> M09 M09 --> M06 + M09 --> M06A M09 --> M07 M09 --> M08 M09 --> M04 M09 --> Logger M06 --> Input + M06A --> Mapping + M06 --> M06A M07 --> BOMLib M08 --> BOMLib @@ -84,9 +89,11 @@ graph TB style M01 fill:#e1f5ff style M09 fill:#fff4e1 style M06 fill:#e8f5e9 + style M06A fill:#f8bbd9 style M07 fill:#e8f5e9 style M08 fill:#e8f5e9 style Input fill:#f3e5f5 + style Mapping fill:#fff9c4 style BOMLib fill:#f3e5f5 style Output fill:#f3e5f5 ``` @@ -95,9 +102,9 @@ graph TB ## 数据流图 -**关键架构:两阶段验证(Two-Phase Validation)** +**关键架构:两阶段验证(Two-Phase Validation)+ 值映射(Value Mapping)** -BOM提取系统采用**两阶段验证架构**,将匹配结果的收集与验证分离: +BOM提取系统采用**两阶段验证架构**,将匹配结果的收集与验证分离,并集成**值映射功能**以支持灵活的参数匹配: ```mermaid flowchart LR @@ -117,10 +124,17 @@ flowchart LR C1 --> C8[fjgn: Y3] end + subgraph "值映射阶段" + C4 --> M1[M06A_Mapper.MapAzxs
A0 → A0,径向] + C7 --> M2[M06A_Mapper.MapLcfw
M04 → M04,高压] + M1 --> P1[azxs: A0,径向
双值格式] + M2 --> P2[lcfw: M04,高压
双值格式] + end + subgraph "Phase 1: 收集阶段(不记录错误)" - C2 & C3 & C4 & C5 & C6 & C7 & C8 --> D[遍历所有BOM库工作表] - D --> E1[接头工作表
MatchBOMRecord] - D --> E2[弹性元件工作表
MatchBOMRecord] + C2 & C3 & P1 & C5 & C6 & P2 & C8 --> D[遍历所有BOM库工作表] + D --> E1[接头工作表
MatchBOMRecord
支持双值匹配] + D --> E2[弹性元件工作表
MatchBOMRecord
支持双值匹配] D --> E3[机芯工作表
MatchBOMRecord] D --> E4[部件工作表
MatchBOMRecord + M08_ComponentProcessor] E1 --> F1[存储匹配结果
不记录错误] @@ -150,13 +164,17 @@ flowchart LR style F2 fill:#e8f5e9 style F3 fill:#e8f5e9 style F4 fill:#e8f5e9 + style M1 fill:#f8bbd9 + style M2 fill:#f8bbd9 + style P1 fill:#fff9c4 + style P2 fill:#fff9c4 ``` --- ## 核心流程 -**关键架构:两阶段验证(Two-Phase Validation)** +**关键架构:两阶段验证(Two-Phase Validation)+ 值映射(Value Mapping)** ```mermaid flowchart TD @@ -170,18 +188,28 @@ flowchart TD CheckBOM -->|不存在| Error2[返回错误:
未找到BOM库.xlsx] CheckBOM -->|存在| OpenBOM[打开BOM库工作簿] - OpenBOM --> LoopStart{遍历每个产品型号} + OpenBOM --> InitModules[初始化各模块
M06_ModelParser
M07_BOMMatcher
M08_ComponentProcessor] + + InitModules --> InitMapper{对照表存在?} + InitMapper -->|是| InitMapperYes[初始化M06A_Mapper
加载映射数据] + InitMapper -->|否| InitMapperNo[记录警告
仅使用原始值] + InitMapperYes --> LoopStart{遍历每个产品型号} + InitMapperNo --> LoopStart LoopStart --> ParseModel[M06_ModelParser
解析产品型号] ParseModel --> ParseSuccess{解析成功?} ParseSuccess -->|失败| LogParseError[记录解析错误
继续下一个型号] - ParseSuccess -->|成功| ExtractParams[提取8个参数] + ParseSuccess -->|成功| ExtractParams[提取原始参数] - ExtractParams --> Phase1[Phase 1: 收集匹配结果] + ExtractParams --> ApplyMapping{映射器已初始化?} + ApplyMapping -->|是| MapValues[M06A_Mapper
应用值映射
azxs, lcfw] + ApplyMapping -->|否| UseRawValues[使用原始值] + MapValues --> Phase1[Phase 1: 收集匹配结果] + UseRawValues --> Phase1 Phase1 --> CollectLoop[遍历所有BOM库工作表] - CollectLoop --> MatchBOM[M07_BOMMatcher
匹配BOM记录] + CollectLoop --> MatchBOM[M07_BOMMatcher
匹配BOM记录
支持双值匹配] MatchBOM --> IsComponent{是部件工作表?} IsComponent -->|是| ProcessComp[M08_ComponentProcessor
处理部件特殊逻辑] @@ -228,6 +256,7 @@ flowchart TD style Start fill:#c8e6c9 style End fill:#ffcdd2 style ParseModel fill:#e1f5fe + style MapValues fill:#f8bbd9 style MatchBOM fill:#fff9c4 style ProcessComp fill:#f8bbd9 style Phase1 fill:#e3f2fd @@ -239,6 +268,8 @@ flowchart TD style LogParseError fill:#ffebee style RecordErrors fill:#ffcdd2 style RecordWarnings fill:#fff9c4 + style InitMapperYes fill:#e8f5e9 + style InitMapperNo fill:#fff9c4 ``` --- @@ -253,9 +284,11 @@ sequenceDiagram participant M01 as M01_Main participant M09 as M09_BOMExtractor participant M06 as M06_ModelParser + participant M06A as M06A_Mapper participant M07 as M07_BOMMatcher participant M08 as M08_ComponentProcessor participant BOM as BOM库.xlsx + participant Mapping as 对照表.xlsx participant Logger as clsErrorLogger User->>M01: 调用 RunBOMExtraction() @@ -265,9 +298,33 @@ sequenceDiagram M09->>M09: 检查产品型号工作表 M09->>M09: 打开BOM库工作簿 + M09->>M06: InitModelParser(logger) + M09->>M07: InitBOMMatcher(logger) + M09->>M08: InitComponentProcessor(logger) + + alt 对照表存在 + M09->>Mapping: 获取对照表工作表 + M09->>M06A: InitMapper(logger, wsMapping) + M06A->>Mapping: 加载映射数据
azxs: A0→径向, etc.
lcfw: M01→低压, etc. + Mapping-->>M06A: 映射数据加载完成 + M06A-->>M09: 映射器初始化完成 + else 对照表不存在 + M09->>Logger: 记录警告
"未找到对照表工作表" + end + loop 遍历每个产品型号 M09->>M06: ParseProductModel(型号字符串) - M06-->>M09: 返回参数字典 + M06->>M06: 提取原始参数
azxs="A0", lcfw="M04" + + alt 映射器已初始化 + M06->>M06A: MapAzxs("A0") + M06A-->>M06: "A0,径向" + M06->>M06A: MapLcfw("M04") + M06A-->>M06: "M04,高压" + M06->>M06: 组装双值参数 + end + + M06-->>M09: 返回参数字典
(含映射值) alt 解析失败 M09->>Logger: 记录解析错误 @@ -279,6 +336,7 @@ sequenceDiagram alt 工作表 = "部件" M09->>M07: MatchBOMRecord(工作表, 参数) + Note over M07: 支持双值匹配
azxs="A0,径向" 可匹配 "A0" 或 "径向" M07-->>M09: 返回匹配结果 alt 匹配成功 @@ -288,6 +346,7 @@ sequenceDiagram end else 其他工作表 M09->>M07: MatchBOMRecord(工作表, 参数) + Note over M07: 支持双值匹配 M07-->>M09: 返回匹配结果 M09->>M09: 存储匹配结果(不记录错误) end @@ -524,7 +583,89 @@ End If --- -### 4. M06_ModelParser.bas - 型号解析器 +### 4. M06A_Mapper.bas - 值映射器 + +**职责**: 为参数值添加映射值,支持双值匹配 + +**映射表配置** (M04_Config): + +| 配置项 | 值 | 说明 | +|--------|-----|------| +| `MAPPING_SHEET_NAME` | "对照表" | 映射表工作表名称 | +| `MAPPING_COL_LCFW_KEY` | 1 (A列) | lcfw原始值列 | +| `MAPPING_COL_LCFW_VAL` | 2 (B列) | lcfw映射值列 | +| `MAPPING_COL_AZXS_KEY` | 4 (D列) | azxs原始值列 | +| `MAPPING_COL_AZXS_VAL` | 5 (E列) | azxs映射值列 | +| `MAPPING_START_ROW` | 3 | 数据起始行 | + +**映射数据结构**: + +**对照表** 工作表格式: + +| 行号 | A列 (lcfw key) | B列 (lcfw val) | D列 (azxs key) | E列 (azxs val) | +|------|----------------|----------------|----------------|----------------| +| 1-2 | (表头) | (表头) | (表头) | (表头) | +| 3 | M01 | 低压 | A0 | 径向 | +| 4 | M12 | 高压 | AT | 径向 | +| 5 | - | - | B0 | 下轴向 | + +**核心功能**: + +```vba +' 初始化映射器 +Public Sub InitMapper(logger As clsErrorLogger, wsMapping As Worksheet) + +' 映射azxs值 +Public Function MapAzxs(ByVal rawValue As String) As String + ' 输入: "A0" + ' 输出: "A0,径向" + +' 映射lcfw值 +Public Function MapLcfw(ByVal rawValue As String) As String + ' 输入: "M01" + ' 输出: "M01,低压" +``` + +**双值格式匹配**: + +参数存储为双值格式后,在BOM匹配时支持灵活匹配: + +| 参数值 | BOM库值 | 匹配结果 | 原因 | +|--------|---------|----------|------| +| `azxs="A0,径向"` | `azxs=A0` | ✅ 匹配 | 参数包含"A0" | +| `azxs="A0,径向"` | `azxs=径向` | ✅ 匹配 | 参数包含"径向" | +| `azxs="A0,径向"` | `azxs=AT` | ❌ 不匹配 | 参数不包含"AT" | + +**优雅降级**: + +如果"对照表"工作表不存在: +- 系统记录警告(非阻塞) +- 继续使用原始值进行匹配 +- 功能正常运行,仅失去映射增强 + +**未映射值处理**: + +如果值在映射表中不存在: +- 仅返回原始值(无逗号) +- 匹配时退化为精确匹配 +- 不影响其他参数的匹配 + +**示例流程**: + +``` +1. 产品型号: YTHN-100.A0.531.G123.M04.Y3 +2. M06_ModelParser提取: azxs="A0", lcfw="M04" +3. M06A_Mapper映射: + - azxs: "A0" → "A0,径向" + - lcfw: "M04" → "M04,高压" +4. BOM库匹配: + - BOM库有azxs="径向" → 匹配成功 + - BOM库有lcfw="高压" → 匹配成功 +``` + +--- + +### 5. M06_ModelParser.bas - 型号解析器 **职责**: 从产品型号字符串中提取参数 @@ -537,28 +678,47 @@ End If **提取的参数**: -| 参数 | 描述 | 示例值 | 转换规则 | -|------|------|--------|----------| -| xh | 型号 | YTHN | 从YTHN-100提取横线前部分 | -| gcwj | 公称外径 | 100 | 从YTHN-100提取横线后数字 | -| azxs | 安装形式 | A0 | 直接提取,无转换 | -| bkxs | 表壳形式 | 531 | 直接提取 | -| gclj | 过程连接 | G12 | 从G123去除末位 | -| jycz | 接液材质 | 3 | 从G123提取末位 | -| lcfw | 量程范围 | M04 | 直接提取 | -| fjgn | 附加功能 | Y3 | 直接提取(多段用逗号连接) | +| 参数 | 描述 | 原始值 | 映射后值 | 转换规则 | +|------|------|--------|----------|----------| +| xh | 型号 | YTHN | YTHN | 从YTHN-100提取横线前部分(无映射) | +| gcwj | 公称外径 | 100 | 100 | 从YTHN-100提取横线后数字(无映射) | +| azxs | 安装形式 | A0 | A0,径向 | 直接提取,然后应用M06A_Mapper映射 | +| bkxs | 表壳形式 | 531 | 531 | 直接提取(无映射) | +| gclj | 过程连接 | G12 | G12 | 从G123去除末位(无映射) | +| jycz | 接液材质 | 3 | 3 | 从G123提取末位(无映射) | +| lcfw | 量程范围 | M04 | M04,高压 | 直接提取,然后应用M06A_Mapper映射 | +| fjgn | 附加功能 | Y3 | Y3 | 直接提取,多段用逗号连接(无映射) | + +**值映射集成**: + +```vba +' 1. 提取原始值 +azxsRaw = ExtractAzxs(segments(1)) ' "A0" +lcfwRaw = ExtractLcfw(segments(4)) ' "M04" + +' 2. 应用映射(如果映射器已初始化) +If M06A_Mapper.IsInitialized() Then + params("azxs") = M06A_Mapper.MapAzxs(azxsRaw) ' "A0,径向" + params("lcfw") = M06A_Mapper.MapLcfw(lcfwRaw) ' "M04,高压" +Else + ' 映射器未初始化,使用原始值 + params("azxs") = azxsRaw ' "A0" + params("lcfw") = lcfwRaw ' "M04" +End If +``` **核心函数**: ```vba Public Function ParseProductModel(ByVal modelStr As String) As Object ' 返回包含8个参数的字典 + ' 如果M06A_Mapper已初始化,azxs和lcfw将包含映射值 End Function ``` --- -### 5. M07_BOMMatcher.bas - BOM匹配器 +### 6. M07_BOMMatcher.bas - BOM匹配器 **职责**: 根据参数在BOM库中查找匹配记录 @@ -568,9 +728,44 @@ End Function |----------|----------| | 空白 | 通配符,匹配所有 | | `!value` | 负向匹配,不等于该值 | -| `value` | 精确匹配 | +| `value` | 精确匹配或双值匹配 | | `fjgn`列 | 包含匹配,value包含在参数中 | +**双值匹配** (新增功能): + +当参数包含映射值时(如`azxs="A0,径向"`),支持灵活匹配: + +``` +' EvaluateCellCondition函数逻辑 +IF paramValue包含逗号 THEN + ' 拆分为多个值 + paramValues = Split(paramValue, ",") + + ' 只要任意一个值匹配单元格值,即认为匹配 + FOR EACH pv IN paramValues + IF pv = cellStr THEN + RETURN True ' 匹配成功 + END IF + NEXT + + RETURN False ' 所有值都不匹配 +ELSE + ' 单值精确匹配 + RETURN (paramValue = cellStr) +END IF +``` + +**双值匹配示例**: + +| 参数值 | BOM库单元格值 | 匹配结果 | 说明 | +|--------|---------------|----------|------| +| `azxs="A0,径向"` | `azxs=A0` | ✅ 匹配 | 参数包含"A0" | +| `azxs="A0,径向"` | `azxs=径向` | ✅ 匹配 | 参数包含"径向" | +| `azxs="A0,径向"` | `azxs=AT` | ❌ 不匹配 | 参数不包含"AT" | +| `lcfw="M01,低压"` | `lcfw=M01` | ✅ 匹配 | 参数包含"M01" | +| `lcfw="M01,低压"` | `lcfw=低压` | ✅ 匹配 | 参数包含"低压" | +| `lcfw="M01,低压"` | `lcfw=高压` | ❌ 不匹配 | 参数不包含"高压" | + **匹配逻辑**: ``` @@ -583,7 +778,14 @@ End Function ELSE IF 列名 = "fjgn" THEN IF 参数包含单元格值 THEN 匹配成功 ELSE 匹配失败 + ELSE IF 参数包含逗号 THEN + ' 双值匹配:检查任意一个值是否匹配 + FOR EACH pv IN Split(参数, ",") + IF pv = 单元格值 THEN 匹配成功 + NEXT + 匹配失败 ELSE + ' 单值精确匹配 IF 参数 = 单元格值 THEN 匹配成功 ELSE 匹配失败 END IF @@ -597,9 +799,13 @@ NEXT - 0条 = 未找到匹配 - 多条 = 匹配不唯一 +**向后兼容**: +- 单值参数(无逗号)退化为精确匹配 +- 不影响现有的非映射参数 + --- -### 6. M08_ComponentProcessor.bas - 部件处理器 +### 7. M08_ComponentProcessor.bas - 部件处理器 **职责**: 处理"部件"类物料的特殊逻辑 @@ -727,15 +933,17 @@ flowchart TD Dictionary { "xh": "YTHN", "gcwj": "100", - "azxs": "A0", + "azxs": "A0,径向", // 双值格式(原始值,映射值) "bkxs": "531", "gclj": "G12", "jycz": "3", - "lcfw": "M04", + "lcfw": "M04,高压", // 双值格式(原始值,映射值) "fjgn": "Y3" } ``` +**注意**: 如果M06A_Mapper未初始化或值未找到映射,则退化为单值格式。 + **列映射字典** (M07_BOMMatcher): ```vba Dictionary { @@ -1002,7 +1210,11 @@ INVALID.MODEL.FORMAT | M09_BOMExtractor | `MatchAllMaterialTypesWithValidation()` | 匹配所有物料类型(两阶段验证) | | M09_BOMExtractor | `ValidateAllMatchResults()` | 统一验证所有匹配结果(跨工作表规则) | | M06_ModelParser | `ParseProductModel()` | 解析型号参数 | -| M07_BOMMatcher | `MatchBOMRecord()` | 匹配BOM记录 | +| M06A_Mapper | `InitMapper()` | 初始化值映射器 | +| M06A_Mapper | `MapAzxs()` | 映射azxs参数值 | +| M06A_Mapper | `MapLcfw()` | 映射lcfw参数值 | +| M07_BOMMatcher | `MatchBOMRecord()` | 匹配BOM记录(支持双值匹配) | +| M07_BOMMatcher | `EvaluateCellCondition()` | 评估单元格条件(支持双值匹配) | | M07_BOMMatcher | `BuildHeaderMapping()` | 构建列映射 | | M08_ComponentProcessor | `ProcessComponentRecord()` | 处理部件特殊逻辑 | @@ -1013,10 +1225,13 @@ INVALID.MODEL.FORMAT | 配置项 | 值 | 说明 | |--------|-----|------| | 输入工作表名 | "产品型号" | 可含"型号"列 | +| 映射表工作表名 | "对照表" | 可选,用于值映射 | | BOM库文件名 | "BOM库.xlsx" | 必须在同一目录 | | 输出工作表名 | "BOM提取结果" | 自动创建/覆盖 | | 数据起始行 | 2 | 行1为表头 | +| 映射表数据起始行 | 3 | 行1-2为表头 | | 支持的物料类型 | 接头, 弹性元件, 机芯, 部件, 边 | 可扩展 | +| 支持的映射参数 | azxs, lcfw | 从对照表加载映射 | --- @@ -1028,11 +1243,12 @@ INVALID.MODEL.FORMAT --- -**文档版本**: 2.0 +**文档版本**: 3.0 **最后更新**: 2026-02-12 **作者**: Claude Code -**状态**: 更新完成 - 新增两阶段验证架构文档 +**状态**: 更新完成 - 新增值映射功能文档 **版本历史**: +- v3.0 (2026-02-12): 重大更新 - 添加值映射(M06A_Mapper)功能文档,更新架构图、数据流图、核心流程图、模块交互时序图 - v2.0 (2026-02-12): 重大更新 - 添加两阶段验证架构详解,更新数据流图、核心流程图、模块交互时序图 - v1.0 (2026-02-12): 初稿完成