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): 初稿完成