feat: add preprocessing with value mapping to BOM Extraction System
All checks were successful
NTFY Notification / notify (push) Successful in 4s

Add a preprocessing step to the BOM Extraction System to enhance parameter
extraction with mapping values from "对照表" worksheet. For azxs (安装形式)
and lcfw (量程范围), the system now stores both the raw parsed value AND the
mapped value from the lookup table, enabling flexible dual-value matching.

Changes:
- Create M06A_Mapper.bas: New module for value mapping
  - Load azxs/lcfw mappings from "对照表" worksheet
  - Return dual-value format "raw,mapped" (e.g., "A0,径向")
  - Support graceful degradation if "对照表" not found
  - Define constants locally to avoid VBA cross-module reference issues

- Modify M06_ModelParser.bas:
  - Extract raw parameter values first
  - Apply mapping via M06A_Mapper if initialized
  - Store dual values for azxs and lcfw parameters

- Modify M07_BOMMatcher.bas:
  - Update EvaluateCellCondition() to support dual-value matching
  - Split by comma and check if ANY value matches
  - Backward compatible with single-value parameters

- Modify M09_BOMExtractor.bas:
  - Initialize M06A_Mapper during RunBOMExtraction()
  - Add WorksheetExists() helper function
  - Log warning if "对照表" worksheet not found
  - Define MAPPING_SHEET_NAME constant locally

- Modify M04_Config.bas:
  - Add mapping table configuration constants
  - MAPPING_SHEET_NAME, MAPPING_COL_LCFW_KEY, etc.

- Update CLAUDE.md:
  - Document new M06A_Mapper module
  - Update data flow diagram with mapping step
  - Document dual-value matching behavior
  - Update system architecture diagram

- Update docs/RunBOMExtraction_运行机制详解.md:
  - Add M06A_Mapper to all diagrams and documentation
  - Add detailed value mapping section
  - Update sequence diagrams with mapping flow
  - Update parameter examples with dual-value format
  - Bump documentation version to 3.0

Example:
  Input:  Product model "YTHN-100.A0.531.G123.M04.Y3"
  Parse:  azxs="A0", lcfw="M04"
  Map:    azxs="A0,径向", lcfw="M04,高压"
  Match:  BOM库 with azxs="径向" OR "A0" → MATCH

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Misaka_Company
2026-02-12 17:52:03 +08:00
parent 21f5f1c299
commit 1d5bad86f7
7 changed files with 626 additions and 47 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -50,6 +50,7 @@ graph TB
subgraph "处理层"
M06[M06_ModelParser.bas<br/>型号解析器]
M06A[M06A_Mapper.bas<br/>值映射器]
M07[M07_BOMMatcher.bas<br/>BOM匹配器]
M08[M08_ComponentProcessor.bas<br/>部件处理器]
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<br/>A0 → A0,径向]
C7 --> M2[M06A_Mapper.MapLcfw<br/>M04 → M04,高压]
M1 --> P1[azxs: A0,径向<br/>双值格式]
M2 --> P2[lcfw: M04,高压<br/>双值格式]
end
subgraph "Phase 1: 收集阶段(不记录错误)"
C2 & C3 & C4 & C5 & C6 & C7 & C8 --> D[遍历所有BOM库工作表]
D --> E1[接头工作表<br/>MatchBOMRecord]
D --> E2[弹性元件工作表<br/>MatchBOMRecord]
C2 & C3 & P1 & C5 & C6 & P2 & C8 --> D[遍历所有BOM库工作表]
D --> E1[接头工作表<br/>MatchBOMRecord<br/>支持双值匹配]
D --> E2[弹性元件工作表<br/>MatchBOMRecord<br/>支持双值匹配]
D --> E3[机芯工作表<br/>MatchBOMRecord]
D --> E4[部件工作表<br/>MatchBOMRecord + M08_ComponentProcessor]
E1 --> F1[存储匹配结果<br/>不记录错误]
@@ -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[返回错误:<br/>未找到BOM库.xlsx]
CheckBOM -->|存在| OpenBOM[打开BOM库工作簿]
OpenBOM --> LoopStart{遍历每个产品型号}
OpenBOM --> InitModules[初始化各模块<br/>M06_ModelParser<br/>M07_BOMMatcher<br/>M08_ComponentProcessor]
InitModules --> InitMapper{对照表存在?}
InitMapper -->|是| InitMapperYes[初始化M06A_Mapper<br/>加载映射数据]
InitMapper -->|否| InitMapperNo[记录警告<br/>仅使用原始值]
InitMapperYes --> LoopStart{遍历每个产品型号}
InitMapperNo --> LoopStart
LoopStart --> ParseModel[M06_ModelParser<br/>解析产品型号]
ParseModel --> ParseSuccess{解析成功?}
ParseSuccess -->|失败| LogParseError[记录解析错误<br/>继续下一个型号]
ParseSuccess -->|成功| ExtractParams[提取8个参数]
ParseSuccess -->|成功| ExtractParams[提取原始参数]
ExtractParams --> Phase1[Phase 1: 收集匹配结果]
ExtractParams --> ApplyMapping{映射器已初始化?}
ApplyMapping -->|是| MapValues[M06A_Mapper<br/>应用值映射<br/>azxs, lcfw]
ApplyMapping -->|否| UseRawValues[使用原始值]
MapValues --> Phase1[Phase 1: 收集匹配结果]
UseRawValues --> Phase1
Phase1 --> CollectLoop[遍历所有BOM库工作表]
CollectLoop --> MatchBOM[M07_BOMMatcher<br/>匹配BOM记录]
CollectLoop --> MatchBOM[M07_BOMMatcher<br/>匹配BOM记录<br/>支持双值匹配]
MatchBOM --> IsComponent{是部件工作表?}
IsComponent -->|是| ProcessComp[M08_ComponentProcessor<br/>处理部件特殊逻辑]
@@ -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: 加载映射数据<br/>azxs: A0→径向, etc.<br/>lcfw: M01→低压, etc.
Mapping-->>M06A: 映射数据加载完成
M06A-->>M09: 映射器初始化完成
else 对照表不存在
M09->>Logger: 记录警告<br/>"未找到对照表工作表"
end
loop 遍历每个产品型号
M09->>M06: ParseProductModel(型号字符串)
M06-->>M09: 返回参数字典
M06->>M06: 提取原始参数<br/>azxs="A0", lcfw="M04"
alt 映射器已初始化
M06->>M06A: MapAzxs("A0")
M06A-->>M06: "A0,径向"
M06->>M06A: MapLcfw("M04")
M06A-->>M06: "M04,高压"
M06->>M06: 组装双值参数
end
M06-->>M09: 返回参数字典<br/>(含映射值)
alt 解析失败
M09->>Logger: 记录解析错误
@@ -279,6 +336,7 @@ sequenceDiagram
alt 工作表 = "部件"
M09->>M07: MatchBOMRecord(工作表, 参数)
Note over M07: 支持双值匹配<br/>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): 初稿完成