# BOM匹配错误判断机制详解
## 目录
1. [系统概述](#1-系统概述)
2. [核心匹配算法 (M07_BOMMatcher)](#2-核心匹配算法-m07_bommatcher)
3. [错误类型与检测](#3-错误类型与检测)
4. [决策树与流程图](#4-决策树与流程图)
5. [错误场景实例](#5-错误场景实例)
6. [部件特殊处理](#6-部件特殊处理)
7. [错误报告机制](#7-错误报告机制)
8. [快速参考](#8-快速参考)
---
**重大更新 (v2.0 - 2025-02-12):**
- ✅ 实施两阶段验证机制(收集 → 验证)
- ✅ 新增警告类型(非阻断性错误)
- ✅ 部件/接头/弹性元件交叉验证增强
- ✅ 错误报告增加"类型"列(错误/警告)
---
## 1. 系统概述
### 1.1 什么是BOM匹配?
BOM匹配是AutoBOM系统的核心功能,它根据产品型号中提取的参数(如`azxs`、`bkxs`、`gclj`、`jycz`、`lcfw`、`fjgn`等),在BOM库中查找**恰好1条**匹配的物料记录。
**核心目标:精确匹配(1:1匹配)**
| 匹配结果 | 状态 | 说明 |
|---------|------|------|
| 0条记录 | ❌ 失败 | 未找到匹配物料 |
| 1条记录 | ✅ 成功 | 唯一匹配,可提取物料信息 |
| 2+条记录 | ❌ 失败 | 模糊匹配,无法确定唯一物料 |
### 1.2 为什么错误判断至关重要?
```
┌─────────────────────────────────────────────────────────┐
│ 错误判断的业务价值 │
├─────────────────────────────────────────────────────────┤
│ ✓ 防止错误物料流入生产环节 │
│ ✓ 避免停工待料(库存不足) │
│ ✓ 减少人工纠错成本 │
│ ✓ 保证BOM准确性(可追溯性) │
│ ✓ 提高生产效率和产品质量 │
└─────────────────────────────────────────────────────────┘
```
### 1.3 匹配哲学:精确匹配原则
AutoBOM系统采用**严格的一对一匹配原则**,并采用**两阶段验证机制**:
```mermaid
graph LR
A[产品型号参数] --> B{阶段1: 收集
所有工作表匹配结果}
B --> C{阶段2: 统一验证}
C -->|0条匹配| D[❌ 失败
记录错误]
C -->|1条匹配| E[✅ 成功
提取物料]
C -->|2+条匹配| F[❌ 失败
记录错误]
C -->|特殊情况| G[⚠️ 警告
记录但不阻断]
style D fill:#ff6b6b
style E fill:#51cf66
style F fill:#ff6b6b
style G fill:#ffd43b
```
**两阶段验证的优势:**
- **阶段1(收集)**: 不立即判断错误,保留所有匹配结果
- **阶段2(验证)**: 统一分析,支持交叉验证
- **警告机制**: 非阻断性问题(如部件与子件共存)记录警告但不停止处理
**拒绝"模糊匹配"的原因:**
- 0条匹配 → 可能遗漏关键参数或BOM库不完整
- 2+条匹配 → 存在歧义,可能导致发错料
---
## 2. 核心匹配算法 (M07_BOMMatcher)
### 2.1 MatchBOMRecord() 函数详解
`MatchBOMRecord()` 是BOM匹配的主入口函数,位于 `M07_BOMMatcher.bas` 模块。
**函数签名:**
```vba
Public Function MatchBOMRecord( _
ByVal ws As Worksheet, _
ByVal params As Object _
) As Object
```
**输入参数:**
- `ws`: BOM库工作表对象(如"接头"、"弹性元件"等)
- `params`: 从产品型号中提取的参数字典
**输出结构:**
```vba
' 返回字典包含以下键:
result("success") ' Boolean - 是否恰好匹配1条
result("rowCount") ' Long - 匹配到的记录数量
result("rowNums") ' Collection - 匹配到的行号集合
result("message") ' String - 结果描述
```
### 2.2 匹配流程图
```mermaid
flowchart TD
Start([开始匹配]) --> InputValidate{验证输入}
InputValidate -->|ws为空| Error1[返回: 工作表为空]
InputValidate -->|params为空| Error2[返回: 参数字典为空]
InputValidate -->|验证通过| ReadData[读取工作表数据到数组]
ReadData --> CheckEmpty{是否有数据?}
CheckEmpty -->|无数据| Error3[返回: 工作表无数据]
CheckEmpty -->|有数据| BuildMap[构建表头映射]
BuildMap --> LoopStart[遍历数据行]
LoopStart --> EvalRow{评估该行
是否匹配?}
EvalRow -->|匹配| AddRow[添加到匹配集合]
EvalRow -->|不匹配| NextRow{下一行?}
AddRow --> NextRow
NextRow -->|是| LoopStart
NextRow -->|否| CheckCount{检查匹配数量}
CheckCount -->|0条| Error4[返回: 未找到匹配记录]
CheckCount -->|1条| Success[返回: 匹配成功]
CheckCount -->|2+条| Error5[返回: 匹配到N条记录]
style Success fill:#51cf66
style Error1 fill:#ff6b6b
style Error2 fill:#ff6b6b
style Error3 fill:#ff6b6b
style Error4 fill:#ff6b6b
style Error5 fill:#ff6b6b
```
### 2.3 EvaluateCellCondition() 单元格匹配规则
`EvaluateCellCondition()` 是最核心的判断逻辑,决定单个单元格是否满足匹配条件。
**函数签名:**
```vba
Public Function EvaluateCellCondition( _
ByVal cellValue As Variant, _
ByVal paramValue As String, _
ByVal fieldName As String _
) As Boolean
```
#### 2.3.1 匹配规则优先级
```mermaid
flowchart TD
A[评估单元格条件] --> B{单元格为空?}
B -->|是| C[✅ 通配符匹配
返回True]
B -->|否| D{以!=开头?}
D -->|是| E[否定匹配
paramValue ≠ notValue?]
D -->|否| F{字段是fjgn?}
F -->|是| G[包含匹配
InStr判断]
F -->|否| H[精确匹配
paramValue = cellValue?]
E --> I{条件满足?}
G --> I
H --> I
I -->|是| J[返回True]
I -->|否| K[返回False]
style C fill:#51cf66
style J fill:#51cf66
style K fill:#ff6b6b
```
#### 2.3.2 详细规则说明
| 规则类型 | BOM库单元格值 | 提取参数值 | 匹配结果 | 说明 |
|---------|--------------|-----------|---------|------|
| **空单元格** | `(空)` | 任意值 | ✅ 匹配 | 空单元格作为通配符,匹配所有值 |
| **否定匹配** | `!=A0` | `A0` | ❌ 不匹配 | 参数值等于否定值时失败 |
| **否定匹配** | `!=A0` | `AT` | ✅ 匹配 | 参数值不等于否定值时成功 |
| **fjgn包含** | `N1` | `N1,N2,N3` | ✅ 匹配 | 使用`InStr()`判断包含关系 |
| **fjgn包含** | `N4` | `N1,N2,N3` | ❌ 不匹配 | 不包含时失败 |
| **精确匹配** | `A0` | `A0` | ✅ 匹配 | 字符串完全相等 |
| **精确匹配** | `A0` | `AT` | ❌ 不匹配 | 字符串不相等 |
**代码实现:**
```vba
' 1. 空单元格:通配符
If IsEmpty(cellValue) Or Len(Trim(CStr(cellValue))) = 0 Then
EvaluateCellCondition = True
Exit Function
End If
' 2. 否定条件 (!=开头)
If Left(cellStr, 2) = "!=" Then
notValue = Trim(Mid(cellStr, 3))
EvaluateCellCondition = (paramValue <> notValue)
Exit Function
End If
' 3. fjgn字段:包含匹配
If LCase(fieldName) = "fjgn" Then
EvaluateCellCondition = CheckFjgnMatch(cellStr, paramValue)
Exit Function
End If
' 4. 精确匹配
EvaluateCellCondition = (paramValue = cellStr)
```
### 2.4 AND逻辑要求
匹配算法采用**AND逻辑**:所有参数列必须同时满足条件,该行才匹配成功。
```mermaid
graph LR
A[参数1: azxs=A0] --> B{匹配?}
B -->|否| C[❌ 整行不匹配]
B -->|是| D[参数2: gclj=M20]
D --> E{匹配?}
E -->|否| C
E -->|是| F[参数3: jycz=1]
F --> G{匹配?}
G -->|否| C
G -->|是| H[✅ 整行匹配]
style C fill:#ff6b6b
style H fill:#51cf66
```
**代码实现:**
```vba
Private Function EvaluateConditionRow( _
ByRef bomData As Variant, _
ByVal rowIdx As Long, _
ByVal headerMap As Object, _
ByVal params As Object _
) As Boolean
Dim paramKey As Variant
' 遍历所有参数
For Each paramKey In params.keys
Dim paramValue As String
paramValue = CStr(params(paramKey))
' 检查BOM库中是否有该列
If headerMap.Exists(CStr(paramKey)) Then
Dim colIdx As Long
colIdx = headerMap(CStr(paramKey))
' 获取单元格值
Dim cellValue As Variant
cellValue = bomData(rowIdx, colIdx)
' 评估单元格条件
If Not EvaluateCellCondition(cellValue, paramValue, CStr(paramKey)) Then
' 只要有一个条件不满足,该行就不匹配
EvaluateConditionRow = False
Exit Function
End If
End If
Next paramKey
' 所有条件都满足
EvaluateConditionRow = True
End Function
```
---
## 3. 错误类型与检测
### 3.1 错误类型总览
```mermaid
graph TD
A[BOM匹配错误] --> B[匹配数量错误]
A --> C[系统错误]
A --> D[部件验证错误]
B --> B1[0条匹配
No Match]
B --> B2[2+条匹配
Multiple Match]
C --> C1[工作表为空]
C --> C2[参数字典为空]
C --> C3[工作表无数据]
C --> C4[运行时异常]
D --> D1[部件+子件共存]
D --> D2[接头数量≠弹性元件数量]
D --> D3[缺少部件和子件]
style B1 fill:#ff6b6b
style B2 fill:#ff6b6b
style C1 fill:#ffa94d
style C2 fill:#ffa94d
style C3 fill:#ffa94d
style C4 fill:#ffa94d
style D1 fill:#845ef7
style D2 fill:#845ef7
style D3 fill:#845ef7
```
### 3.2 匹配数量错误
#### 3.2.1 0条匹配 (No Match)
**触发条件:** 在BOM库中未找到任何满足条件的记录
**错误信息:** `"未找到匹配记录"`
**常见原因:**
1. BOM库中确实没有该配置的物料
2. 产品型号解析错误(参数提取不正确)
3. BOM库配置条件过于严格
4. 参数值拼写错误
**检测位置:** `M07_BOMMatcher.bas:128-130`
```vba
If matchingRows.count = 0 Then
result("success") = False
result("message") = "未找到匹配记录"
End If
```
#### 3.2.2 2+条匹配 (Multiple Match)
**触发条件:** 在BOM库中找到多条满足条件的记录
**错误信息:** `"匹配到N条记录(需要恰好1条)"`
**常见原因:**
1. BOM库中存在重复记录
2. 配置条件不够细化(列数不足或值过于宽泛)
3. 空单元格过多(通配符导致模糊匹配)
**检测位置:** `M07_BOMMatcher.bas:134-137`
```vba
Else
result("success") = False
result("message") = "匹配到" & matchingRows.count & "条记录(需要恰好1条)"
End If
```
### 3.3 系统错误
| 错误类型 | 错误信息 | 触发条件 | 检测位置 |
|---------|---------|---------|---------|
| 工作表为空 | `"工作表为空"` | `ws Is Nothing` | `M07_BOMMatcher.bas:62-68` |
| 参数字典为空 | `"参数字典为空"` | `params Is Nothing Or params.count = 0` | `M07_BOMMatcher.bas:71-78` |
| 工作表无数据 | `"工作表无数据"` | `lastRow < BOMLIB_START_ROW` | `M07_BOMMatcher.bas:90-96` |
| 运行时异常 | `"系统错误: {Err.Description}"` | `Err.Number <> 0` | `M07_BOMMatcher.bas:142-152` |
### 3.4 部件验证错误
部件物料有特殊的组合验证规则,详见[第6章:部件特殊处理](#6-部件特殊处理)。
---
## 4. 决策树与流程图
### 4.1 完整匹配流程决策树(两阶段验证)
```mermaid
flowchart TD
A[开始: 接收产品型号] --> B[解析型号提取参数]
B --> C{参数提取成功?}
C -->|否| Err1[记录错误: 型号解析失败]
C -->|是| D[阶段1: 收集所有工作表匹配结果]
D --> E{是'部件'工作表?}
E -->|是| F[调用M08_ComponentProcessor
存入resultsDict]
E -->|否| G[调用M07_BOMMatcher
存入resultsDict]
F --> H{更多工作表?}
G --> H
H -->|是| D
H -->|否| I[阶段2: 统一验证所有结果]
I --> J{基础验证
非特殊工作表}
J --> K{特殊验证
部件/接头/弹性元件}
K --> L{验证通过?}
L -->|否| ErrV[记录验证错误]
L -->|是| M{有警告?}
M -->|是| Warn[记录警告
不阻断流程]
M -->|否| N[生成输出行]
Warn --> N
Err1 --> O[写入错误报告]
ErrV --> O
Warn --> O
N --> P[写入结果工作表]
style Err1 fill:#ff6b6b
style ErrV fill:#ff6b6b
style Warn fill:#ffd43b
style N fill:#51cf66
```
### 4.2 错误检测决策树
```mermaid
flowchart TD
Start([匹配结果检测]) --> CheckCount{匹配数量?}
CheckCount -->|0| TypeA[No Match
未找到匹配记录]
CheckCount -->|1| Success[Success
匹配成功]
CheckCount -->|2+| TypeB[Multiple Match
匹配到N条记录]
TypeA --> AnalyzeA[分析原因]
TypeB --> AnalyzeB[分析原因]
AnalyzeA --> CauseA1{BOM库缺失?}
CauseA1 -->|是| FixA1[补充BOM库记录]
CauseA1 -->|否| CauseA2{解析错误?}
CauseA2 -->|是| FixA2[检查型号解析逻辑]
CauseA2 -->|否| FixA3[检查配置条件]
AnalyzeB --> CauseB1{重复记录?}
CauseB1 -->|是| FixB1[删除重复记录]
CauseB1 -->|否| CauseB2{条件过宽?}
CauseB2 -->|是| FixB2[细化配置列]
CauseB2 -->|否| FixB3[减少空单元格]
style Success fill:#51cf66
style TypeA fill:#ff6b6b
style TypeB fill:#ff6b6b
style FixA1 fill:#4dabf7
style FixA2 fill:#4dabf7
style FixA3 fill:#4dabf7
style FixB1 fill:#4dabf7
style FixB2 fill:#4dabf7
style FixB3 fill:#4dabf7
```
### 4.3 EvaluateCellCondition 逻辑流程
```mermaid
flowchart TD
A[开始评估单元格] --> B{cellValue为空
或IsEmpty?}
B -->|是| C[返回True
通配符匹配]
B -->|否| D[转换cellValue为字符串]
D --> E{以'!='开头?}
E -->|是| F[提取notValue]
F --> G{paramValue <> notValue?}
G -->|是| H[返回True]
G -->|否| I[返回False]
E -->|否| J{fieldName是'fjgn'?}
J -->|是| K[调用CheckFjgnMatch]
K --> L{InStr包含判断}
L -->|是| H
L -->|否| I
J -->|否| M[精确匹配]
M --> N{paramValue = cellStr?}
N -->|是| H
N -->|否| I
style C fill:#51cf66
style H fill:#51cf66
style I fill:#ff6b6b
```
### 4.4 部件验证流程
```mermaid
flowchart TD
A[验证部件组合] --> B{物料集合为空?}
B -->|是| Err1[返回: 物料列表为空]
B -->|否| C[统计各类型物料数量]
C --> D{有1个部件
且无子件?}
D -->|是| Success1[验证通过: 1个部件]
D -->|否| E{无部件
且有1个接头+1个弹性元件?}
E -->|是| Success2[验证通过: 1个接头+1个弹性元件]
E -->|否| F[组合异常判断]
F --> G{部件>1?}
G -->|是| Err2[部件数量为N(应为1)]
G -->|否| H{有部件且存在子件?}
H -->|是| Err3[同时存在部件和子件]
H -->|否| I{接头数≠弹性元件数?}
I -->|是| Err4[接头数N ≠ 弹性元件数M]
I -->|否| J{无部件且无子件?}
J -->|是| Err5[缺少部件和子件]
J -->|否| Err6[未知异常组合]
style Success1 fill:#51cf66
style Success2 fill:#51cf66
style Err1 fill:#ff6b6b
style Err2 fill:#ff6b6b
style Err3 fill:#ff6b6b
style Err4 fill:#ff6b6b
style Err5 fill:#ff6b6b
style Err6 fill:#ff6b6b
```
### 4.5 错误报告序列图
```mermaid
sequenceDiagram
participant User as 用户
participant M09 as M09_BOMExtractor
participant M07 as M07_BOMMatcher
participant M08 as M08_ComponentProcessor
participant Logger as clsErrorLogger
User->>M09: 运行BOM提取
M09->>M07: MatchBOMRecord(ws, params)
alt 匹配失败
M07-->>M09: {success:False, message:"..."}
M09->>Logger: Record(0, "M09...", "BOMMatchError", message)
else 匹配成功
M07-->>M09: {success:True, rowNums:Collection}
end
M09->>M08: ValidateComponentCombination(materials)
alt 验证失败
M08-->>M09: {valid:False, message:"..."}
M09->>Logger: Record(0, "M09...", "ValidationError", message)
end
M09->>M09: 生成输出行(包含remarks)
M09->>Logger: HasErrors?
alt 有错误
Logger-->>M09: True
M09->>Logger: PrintReport(Workbook)
Logger->>Logger: 创建"错误报告_"工作表
end
M09-->>User: 返回处理结果消息
```
---
## 5. 错误场景实例
### 5.1 场景1:0条匹配 - BOM库缺失
**输入条件:**
- 产品型号:`YTHN-100 M20 A0 1 M01 N1`
- 提取参数:`{azxs: "A0", gclj: "M20", jycz: "1", lcfw: "M01", fjgn: "N1"}`
- BOM库"接头"工作表:
| azxs | gclj | jycz | lcfw | 物料名称 |
|------|------|------|------|----------|
| AT | M20 | 1 | M01 | 接头AT |
| AH | M20 | 1 | M01 | 接头AH |
**匹配过程:**
```vba
' 第1行: azxs=AT vs param="A0"
EvaluateCellCondition("AT", "A0", "azxs") → False (精确匹配失败)
' 整行不匹配
' 第2行: azxs=AH vs param="A0"
EvaluateCellCondition("AH", "A0", "azxs") → False (精确匹配失败)
' 整行不匹配
' 最终结果: matchingRows.count = 0
```
**错误输出:**
```javascript
{
success: false,
rowCount: 0,
rowNums: [],
message: "未找到匹配记录"
}
```
**解决方案:**
1. 检查是否需要添加 `azxs=A0` 的记录到BOM库
2. 确认型号解析是否正确(A0是否应为AT)
### 5.2 场景2:0条匹配 - 否定条件不满足
**输入条件:**
- 提取参数:`{azxs: "A0", gclj: "M20"}`
- BOM库"接头"工作表:
| azxs | gclj | 物料名称 |
|------|------|----------|
| !=A0 | M20 | 接头非A0 |
**匹配过程:**
```vba
' azxs列: cellValue="!=A0", paramValue="A0"
EvaluateCellCondition("!=A0", "A0", "azxs") → False (否定匹配: "A0" = "A0")
' 整行不匹配
' 最终结果: matchingRows.count = 0
```
**错误输出:**
```javascript
{
success: false,
rowCount: 0,
message: "未找到匹配记录"
}
```
**解决方案:**
- 该记录明确排除 `azxs=A0`,这是预期行为
- 如需支持 `A0`,需添加新记录或修改否定条件
### 5.3 场景3:2+条匹配 - 重复记录
**输入条件:**
- 提取参数:`{azxs: "A0", gclj: "M20"}`
- BOM库"接头"工作表:
| azxs | gclj | lcfw | 物料名称 | 物料编码 |
|------|------|------|----------|----------|
| A0 | M20 | | 接头A0-1 | JT-001 |
| A0 | M20 | | 接头A0-2 | JT-002 |
**匹配过程:**
```vba
' 第1行:
' azxs: "A0" = "A0" → True
' gclj: "M20" = "M20" → True
' lcfw: "" (空) → True (通配符)
' → 匹配成功
' 第2行:
' azxs: "A0" = "A0" → True
' gclj: "M20" = "M20" → True
' lcfw: "" (空) → True (通配符)
' → 匹配成功
' 最终结果: matchingRows.count = 2
```
**错误输出:**
```javascript
{
success: false,
rowCount: 2,
rowNums: [2, 3],
message: "匹配到2条记录(需要恰好1条)"
}
```
**解决方案:**
1. 删除重复记录
2. 细化配置条件(如添加`lcfw`列区分)
### 5.4 场景4:2+条匹配 - 空单元格过多
**输入条件:**
- 提取参数:`{azxs: "A0", gclj: "M20"}`
- BOM库"接头"工作表:
| azxs | gclj | jycz | lcfw | fjgn | 物料名称 |
|------|------|------|------|------|----------|
| A0 | M20 | | | | 接头配置1 |
| A0 | M20 | | | | 接头配置2 |
| A0 | M20 | | | | 接头配置3 |
**匹配过程:**
```vba
' 所有3行的 jycz、lcfw、fjgn 都是空单元格
' 空单元格 → 通配符 → 全部返回True
' 导致3行都匹配
' 最终结果: matchingRows.count = 3
```
**错误输出:**
```javascript
{
success: false,
rowCount: 3,
rowNums: [2, 3, 4],
message: "匹配到3条记录(需要恰好1条)"
}
```
**解决方案:**
1. 补充缺失的配置列值
2. 添加新的区分列(如`bkxs`、`特殊要求`等)
### 5.5 场景5:fjgn包含匹配
**输入条件:**
- 提取参数:`{azxs: "A0", fjgn: "N1,N2,N3"}`
- BOM库"接头"工作表:
| azxs | fjgn | 物料名称 |
|------|------|----------|
| A0 | N1 | 带N1功能 |
| A0 | N4 | 带N4功能 |
**匹配过程:**
```vba
' 第1行:
' azxs: "A0" = "A0" → True
' fjgn: CheckFjgnMatch("N1", "N1,N2,N3")
' → InStr("N1,N2,N3", "N1") > 0 → True
' → 匹配成功
' 第2行:
' azxs: "A0" = "A0" → True
' fjgn: CheckFjgnMatch("N4", "N1,N2,N3")
' → InStr("N1,N2,N3", "N4") = 0 → False
' → 不匹配
' 最终结果: matchingRows.count = 1, success = true
```
**正确输出:**
```javascript
{
success: true,
rowCount: 1,
rowNums: [2],
message: "匹配成功"
}
```
### 5.6 场景6:部件验证错误
**输入条件:**
- 提取参数:`{azxs: "A0", gclj: "M20"}`
- 匹配结果物料集合:
```javascript
[
{materialType: "接头", materialName: "接头A0", ...},
{materialType: "接头", materialName: "接头AT", ...}, // 重复
{materialType: "弹性元件", materialName: "元件M20", ...}
]
```
**验证过程:**
```vba
' 统计:
' componentCount = 0
' jointCount = 2
' elementCount = 1
' 验证规则: jointCount (2) ≠ elementCount (1)
' → 验证失败
```
**验证结果:**
```javascript
{
valid: false,
message: "部件组合异常: 接头数量(2)≠弹性元件数量(1)"
}
```
**错误输出行:**
| 原始产品型号 | azxs | ... | 物料类型 | 物料名称 | 提取备注 |
|-------------|------|-----|----------|----------|----------|
| YTHN-100... | A0 | ... | 接头 | 接头A0 | 部件组合异常: 接头数量(2)≠弹性元件数量(1) |
**解决方案:**
1. 检查BOM库配置是否导致多次匹配
2. 确保接头和弹性元件一对一配对
---
## 6. 部件特殊处理
### 6.1 部件物料特性
部件物料在AutoBOM系统中具有特殊地位:
```
┌──────────────────────────────────────────────────────────┐
│ 部件物料结构 │
├──────────────────────────────────────────────────────────┤
│ 1条"部件"记录 = { │
│ - 部件本体(优先选择) │
│ - 子件1: 接头(备选方案) │
│ - 子件2: 弹性元件(备选方案) │
│ } │
│ │
│ 选择逻辑: │
│ ✓ 有库存 → 返回部件 │
│ ✗ 无库存 → 返回接头 + 弹性元件 │
└──────────────────────────────────────────────────────────┘
```
### 6.2 部件处理流程
```mermaid
flowchart TD
A[开始处理'部件'工作表] --> B[调用MatchBOMRecord]
B --> C{匹配成功?}
C -->|否| Err[记录匹配错误
添加错误物料]
C -->|是| D[获取匹配行号]
D --> E[构建表头映射]
E --> F{检查部件库存}
F -->|有库存| G[提取部件物料信息]
F -->|无库存| H[提取子件信息]
G --> I[返回部件物料集合]
H --> J[提取接头信息]
H --> K[提取弹性元件信息]
J --> L[返回子件集合]
K --> L
style Err fill:#ff6b6b
style G fill:#51cf66
style L fill:#51cf66
```
### 6.3 部件组合验证规则(两阶段验证)
#### ✅ 正确组合(验证通过)
| 组合类型 | 部件数 | 接头数 | 弹性元件数 | 状态 | 说明 |
|---------|-------|-------|-----------|------|------|
| 纯部件 | 1 | 0 | 0 | ✅ 有效 | [部件]工作表返回1个部件 |
| 纯子件 | 0 | 1 | 1 | ✅ 有效 | [部件]工作表返回1个接头+1个弹性元件 |
| 独立子件 | 0 | 1 | 1 | ✅ 有效 | [接头]和[弹性元件]工作表各返回1条 |
#### ⚠️ 警告组合(验证通过,记录警告)
| 组合类型 | 部件数 | 接头数 | 弹性元件数 | 警告类型 | 说明 |
|---------|-------|-------|-----------|---------|------|
| 部件+接头 | 1 | 1+ | 任意 | ComponentConflict | 存在[部件]物料,但[接头]工作表也匹配到,已忽略 |
| 部件+弹性元件 | 1 | 任意 | 1+ | ComponentConflict | 存在[部件]物料,但[弹性元件]工作表也匹配到,已忽略 |
| 子件+独立接头 | 0 | 2+ | 1+ | ComponentConflict | [部件]工作表已返回接头,但[接头]工作表也匹配到,已忽略 |
| 子件+独立元件 | 0 | 1+ | 2+ | ComponentConflict | [部件]工作表已返回弹性元件,但[弹性元件]工作表也匹配到,已忽略 |
#### ❌ 异常组合(验证失败)
| 异常类型 | 部件数 | 接头数 | 弹性元件数 | 错误信息 |
|---------|-------|-------|-----------|---------|
| 部件过多(部件表) | >1 | - | - | [部件]工作表匹配到N条记录 |
| 部件过多(总计数) | >1 | - | - | 部件数量为N(应为1) |
| 接头过多(部件表) | 0 | 2+ | 任意 | [部件]工作表匹配到2+个接头 |
| 接头过多(接头表) | 0 | 2+ | 任意 | [接头]工作表匹配到N条记录 |
| 弹性元件过多(部件表) | 0 | 任意 | 2+ | [部件]工作表匹配到2+个弹性元件 |
| 弹性元件过多(元件表) | 0 | 任意 | 2+ | [弹性元件]工作表匹配到N条记录 |
| 接头弹性不匹配 | 0 | N | M (N≠M) | 接头数量N ≠ 弹性元件数量M |
| 完全缺失 | 0 | 0 | 0 | 部件、接头、弹性元件均未匹配或组合不完整 |
| 数量不对称 | 0 | 1 | 0 | 接头和弹性元件数量不匹配 |
| 数量不对称 | 0 | 0 | 1 | 接头和弹性元件数量不匹配 |
### 6.4 ValidateComponentCombination() 代码逻辑
```vba
Public Function ValidateComponentCombination(ByVal materials As Collection) As Object
Dim componentCount As Long, jointCount As Long, elementCount As Long
' 统计各类型物料数量
For Each mat In materials
Select Case mat("materialType")
Case "部件": componentCount = componentCount + 1
Case "接头": jointCount = jointCount + 1
Case "弹性元件": elementCount = elementCount + 1
End Select
Next mat
' 规则1: 只有1个部件
If componentCount = 1 And jointCount = 0 And elementCount = 0 Then
result("valid") = True
result("message") = "验证通过:1个部件"
Exit Function
End If
' 规则2: 没有部件,恰好1个接头和1个弹性元件
If componentCount = 0 And jointCount = 1 And elementCount = 1 Then
result("valid") = True
result("message") = "验证通过:1个接头+1个弹性元件"
Exit Function
End If
' 其他情况都是异常
result("valid") = False
result("message") = BuildErrorMessage(componentCount, jointCount, elementCount)
End Function
```
### 6.5 库存检查接口(预留)
当前版本的库存检查函数默认返回 `True`(库存充足),预留接口供未来扩展:
```vba
Private Function CheckComponentInventory( _
ByVal wsComponent As Worksheet, _
ByVal rowNum As Long, _
ByVal headerMap As Object _
) As Boolean
' TODO: 连接库存系统查询实际库存
' 示例扩展:
' If headerMap.Exists("库存数量") Then
' stockQty = CLng(wsComponent.Cells(rowNum, headerMap("库存数量")).Value)
' CheckComponentInventory = (stockQty > 0)
' End If
CheckComponentInventory = True ' 当前默认有库存
End Function
```
**未来扩展方向:**
1. 查询ERP系统API
2. 读取库存Excel表
3. 连接数据库库存表
---
## 7. 错误报告机制
### 7.1 clsErrorLogger 类模块(v2.0)
`clsErrorLogger` 是系统的错误日志记录器,负责收集、存储和报告所有匹配错误和警告。
**类结构(v2.0):**
```vba
' 私有变量
Private pErrors As Collection ' 错误集合
Private pWarnings As Collection ' 警告集合
' 公开方法
Public Sub Record(RowIndex As Long, SourceFunc As String, ErrorType As String, Desc As String, Context As String)
Public Sub RecordWarning(RowIndex As Long, SourceFunc As String, WarningType As String, Desc As String, Context As String)
Public Property Get HasErrors() As Boolean
Public Property Get HasWarnings() As Boolean
Public Property Get HasIssues() As Boolean ' 有错误或警告
Public Sub PrintReport(targetWb As Workbook)
```
**v2.0 新增功能:**
- ✅ 支持警告类型(`RecordWarning`方法)
- ✅ 区分错误和警告(`HasErrors`、`HasWarnings`、`HasIssues`属性)
- ✅ 错误报告增加"类型"列(错误/警告)
- ✅ 警告行黄色高亮,错误行红色高亮
### 7.2 错误和警告记录结构
#### 7.2.1 错误记录(Record方法)
每条错误记录包含5个字段:
| 字段名 | 类型 | 说明 | 示例 |
|--------|------|------|------|
| RowIndex | Long | 发生错误的行号 | 0(无特定行) |
| SourceFunc | String | 来源模块/函数 | "M09.ValidateAllMatchResults" |
| ErrorType | String | 错误类型 | "BOMMatchError" |
| Desc | String | 详细描述 | "接头 未找到匹配记录" |
| Context | String | 原始数据上下文 | ""(可选) |
**错误类型枚举:**
- `BOMMatchError`: BOM库匹配错误(0条或多条)
- `ValidationError`: 部件组合验证错误
- `SystemError`: 系统运行时异常
#### 7.2.2 警告记录(RecordWarning方法)
每条警告记录包含5个字段:
| 字段名 | 类型 | 说明 | 示例 |
|--------|------|------|------|
| RowIndex | Long | 发生警告的行号 | 0(无特定行) |
| SourceFunc | String | 来源模块/函数 | "M09.ValidateAllMatchResults" |
| WarningType | String | 警告类型 | "ComponentConflict" |
| Desc | String | 详细描述 | "存在[部件]物料,但[接头]工作表也匹配到1条记录,已忽略" |
| Context | String | 原始数据上下文 | ""(可选) |
**警告类型枚举:**
- `ComponentConflict`: 部件与子件冲突(非阻断性)
### 7.3 错误记录流程
```mermaid
sequenceDiagram
participant Func as 业务函数
participant Logger as clsErrorLogger
participant Collection as pErrors Collection
participant WB as Workbook
Func->>Logger: Record(0, "M09...", "BOMMatchError", "接头 未匹配", "")
activate Logger
Logger->>Collection: Add Array(0, "M09...", "BOMMatchError", "接头 未匹配", "")
Note over Collection: 存储到内存集合
Collection--xLogger: 确认添加
deactivate Logger
Note over Func: 继续处理其他物料...
Func->>Logger: HasErrors?
Logger->>Logger: pErrors.count > 0
Logger-->>Func: True
Func->>Logger: PrintReport(Workbook)
activate Logger
Logger->>WB: Worksheets.Add()
Logger->>WB: 写入表头 (A1:E1)
Logger->>Collection: 遍历所有错误
loop 每条错误记录
Collection-->>Logger: Array(RowIndex, SourceFunc, ErrorType, Desc, Context)
Logger->>WB: 写入到数据行
end
Logger->>WB: Columns.AutoFit()
Logger--xFunc: 完成
deactivate Logger
```
### 7.4 错误报告格式(v2.0)
生成的错误报告工作表包含**6列**:
| 类型 | 原表行号 | 来源模块 | 错误类型 | 详细描述 | 原始数据 |
|------|---------|---------|---------|---------|---------|
| 错误 | 0 | M09.ValidateAllMatchResults | BOMMatchError | 接头 未找到匹配记录 | |
| 错误 | 0 | M09.ValidateAllMatchResults | BOMMatchError | 弹性元件 未找到匹配记录 | |
| 警告 | 0 | M09.ValidateAllMatchResults | ComponentConflict | 存在[部件]物料,但[接头]工作表也匹配到1条记录,已忽略 | |
| 错误 | 0 | M09.ProcessSingleModel | ValidationError | 部件组合异常: 接头数量(2)≠弹性元件数量(1) | |
**格式化特性(v2.0):**
- ✅ 表头加粗、灰色背景
- ✅ 错误行红色背景
- ✅ 警告行黄色背景
- ✅ 自动列宽调整
- ✅ 工作表命名: `"错误报告_" & Format(Now, "hhmmss")`
### 7.5 错误和警告信息传播路径(v2.0)
```mermaid
graph LR
A[M07_BOMMatcher.MatchBOMRecord] -->|返回结果| B[M09_BOMExtractor.MatchAllMaterialTypesWithValidation]
B -->|存入resultsDict| C[ValidateAllMatchResults]
C -->|检测到错误| D[clsErrorLogger.Record]
C -->|检测到警告| E[clsErrorLogger.RecordWarning]
D --> F[存储到pErrors集合]
E --> G[存储到pWarnings集合]
F --> H{HasIssues?}
G --> H
H -->|True| I[Logger.PrintReport]
H -->|False| J[跳过报告生成]
I --> K[生成错误和警告报告工作表]
```
### 7.6 用户通知机制(v2.0)
在 `M09_BOMExtractor.RunBOMExtraction()` 函数中,通过返回消息通知用户:
```vba
Dim msg As String
msg = "BOM提取完成!" & vbCrLf & _
"处理型号数: " & totalModels & vbCrLf & _
"提取物料数: " & allResults.count & vbCrLf & _
"成功数: " & successCount & vbCrLf & _
"异常数: " & errorCount
If g_Logger.HasErrors Then
msg = msg & vbCrLf & vbCrLf & "发现错误,已生成错误报告工作表。"
ElseIf g_Logger.HasWarnings Then
msg = msg & vbCrLf & vbCrLf & "发现警告,已生成错误报告工作表。"
End If
RunBOMExtraction = msg
```
**示例消息(有错误):**
```
BOM提取完成!
处理型号数: 100
提取物料数: 350
成功数: 320
异常数: 30
发现错误,已生成错误报告工作表。
```
**示例消息(仅警告):**
```
BOM提取完成!
处理型号数: 100
提取物料数: 350
成功数: 350
异常数: 0
发现警告,已生成错误报告工作表。
```
**示例消息(无问题):**
```
BOM提取完成!
处理型号数: 100
提取物料数: 350
成功数: 350
异常数: 0
```
---
## 8. 快速参考
### 8.1 匹配规则速查表
| 规则类型 | BOM库单元格 | 提取参数 | 匹配结果 | 代码位置 |
|---------|------------|---------|---------|---------|
| **通配符** | `(空)` | 任意值 | ✅ 匹配 | `M07_BOMMatcher.bas:237-241` |
| **否定** | `!=A0` | `A0` | ❌ 不匹配 | `M07_BOMMatcher.bas:247-252` |
| **否定** | `!=A0` | `AT` | ✅ 匹配 | `M07_BOMMatcher.bas:247-252` |
| **fjgn包含** | `N1` | `N1,N2` | ✅ 匹配 | `M07_BOMMatcher.bas:282-301` |
| **精确** | `A0` | `A0` | ✅ 匹配 | `M07_BOMMatcher.bas:261-262` |
| **精确** | `A0` | `AT` | ❌ 不匹配 | `M07_BOMMatcher.bas:261-262` |
### 8.2 错误和警告类型速查表(v2.0)
#### 错误类型
| 错误类型 | 错误信息 | 触发条件 | 处理建议 |
|---------|---------|---------|---------|
| **No Match** | `未找到匹配记录` | 匹配0条 | 补充BOM库或检查参数 |
| **Multiple Match** | `匹配到N条记录(需要恰好1条)` | 匹配2+条 | 删除重复或细化条件 |
| **工作表为空** | `工作表为空` | `ws Is Nothing` | 检查BOM库文件 |
| **参数字典为空** | `参数字典为空` | `params.count = 0` | 检查型号解析 |
| **工作表无数据** | `工作表无数据` | `lastRow < START_ROW` | 检查BOM库数据 |
| **部件组合异常** | `同时存在部件和子件` | 验证失败 | 检查部件处理逻辑 |
| **部件、接头、弹性元件均未匹配** | `部件、接头、弹性元件均未匹配或组合不完整` | 特殊验证失败 | 检查BOM库配置 |
| **接头和弹性元件数量不匹配** | `接头和弹性元件数量不匹配` | 特殊验证失败 | 确保接头和弹性元件一对一 |
#### 警告类型(v2.0新增)
| 警告类型 | 警告信息 | 触发条件 | 影响 |
|---------|---------|---------|------|
| **ComponentConflict** | `存在[部件]物料,但[接头]工作表也匹配到N条记录,已忽略` | 部件表返回部件,接头表也有匹配 | ⚠️ 非阻断,仅记录 |
| **ComponentConflict** | `[部件]工作表已返回接头,但[接头]工作表也匹配到N条记录,已忽略` | 部件表返回子件,接头表也有匹配 | ⚠️ 非阻断,仅记录 |
### 8.3 部件验证规则速查(v2.0)
#### 验证结果分类
| 部件数 | 接头数 | 弹性元件数 | 验证结果 | 说明 |
|-------|-------|-----------|---------|------|
| 1 | 0 | 0 | ✅ 有效 | 纯部件组合 |
| 0 | 1 | 1 | ✅ 有效 | 纯子件组合 |
| 1 | 1+ | 任意 | ⚠️ 警告 | 部件+接头冲突 |
| 1 | 任意 | 1+ | ⚠️ 警告 | 部件+弹性元件冲突 |
| >1 | - | - | ❌ 异常 | 部件过多 |
| 1 | >0 | >0 | ❌ 异常 | 部件子件共存(旧逻辑) |
| 0 | N | M (N≠M) | ❌ 异常 | 数量不匹配 |
| 0 | 0 | 0 | ❌ 异常 | 完全缺失 |
#### 警告场景详解(v2.0新增)
| 场景 | [部件]工作表 | [接头]工作表 | [弹性元件]工作表 | 结果 |
|------|------------|------------|---------------|------|
| 部件+接头冲突 | 返回1个部件 | 返回1+个接头 | 任意或无 | ⚠️ 使用部件,忽略接头表 |
| 部件+元件冲突 | 返回1个部件 | 任意或无 | 返回1+个弹性元件 | ⚠️ 使用部件,忽略弹性元件表 |
| 子件+独立接头 | 返回1个接头+1个弹性元件 | 返回1+个接头 | 返回1+个弹性元件 | ⚠️ 使用部件表的子件,忽略独立表 |
| 子件+独立元件 | 返回1个接头+1个弹性元件 | 返回1+个接头 | 返回1+个弹性元件 | ⚠️ 使用部件表的子件,忽略独立表 |
### 8.4 故障排查指南
#### 问题:未找到匹配记录
**排查步骤:**
1. ✅ 检查型号解析是否正确
2. ✅ 确认BOM库中是否有对应记录
3. ✅ 检查配置条件是否过于严格
4. ✅ 验证否定条件(`!=`)是否误排除
#### 问题:匹配到多条记录
**排查步骤:**
1. ✅ 检查是否有重复记录
2. ✅ 统计空单元格数量(过多会导致模糊匹配)
3. ✅ 评估是否需要添加区分列
4. ✅ 确认配置列值是否足够细化
#### 问题:部件组合验证失败
**排查步骤:**
1. ✅ 确认BOM库"接头"和"弹性元件"是否都匹配
2. ✅ 检查是否存在重复匹配(如接头匹配到2条)
3. ✅ 验证部件库存检查逻辑(如已实现)
4. ✅ 审查M08_ComponentProcessor日志输出
### 8.5 关键函数位置索引(v2.0)
| 函数名 | 模块 | 行号 | 功能描述 |
|--------|------|------|---------|
| `MatchBOMRecord` | M07_BOMMatcher | 55-153 | BOM库匹配主入口 |
| `EvaluateCellCondition` | M07_BOMMatcher | 230-266 | 单元格条件评估 |
| `CheckFjgnMatch` | M07_BOMMatcher | 282-301 | fjgn包含匹配 |
| `ProcessComponentRecord` | M08_ComponentProcessor | 58-137 | 部件处理主入口 |
| `ValidateComponentCombination` | M08_ComponentProcessor | 423-507 | 部件组合验证(已弃用,移至M09) |
| `ProcessSingleModel` | M09_BOMExtractor | 304-365 | 单型号处理流程 |
| `MatchAllMaterialTypesWithValidation` | M09_BOMExtractor | 387-550 | **新增** 两阶段匹配(收集→验证) |
| `ValidateAllMatchResults` | M09_BOMExtractor | 552-750 | **新增** 统一验证所有匹配结果 |
| `ToArray` | M09_BOMExtractor | 751-762 | **新增** Collection转数组辅助函数 |
| `Record` | clsErrorLogger | 16-19 | 错误记录 |
| `RecordWarning` | clsErrorLogger | 37-39 | **新增** 警告记录 |
| `HasWarnings` | clsErrorLogger | 27-29 | **新增** 是否有警告 |
| `HasIssues` | clsErrorLogger | 32-34 | **新增** 是否有问题(错误或警告) |
| `PrintReport` | clsErrorLogger | 42-105 | 生成错误报告(支持警告) |
### 8.6 调试技巧
#### 1. 启用调试输出
在VBA编辑器中设置**立即窗口**可见(Ctrl+G),查看调试输出:
```vba
Debug.Print "=== 处理工作表: [" & sheetName & "] ==="
Debug.Print " -> 匹配结果: " & matchResult("success") & ", 行数: " & matchResult("rowCount")
Debug.Print " -> 匹配行号: " & rowNum
```
#### 2. 断点设置
在关键位置设置断点,逐步执行:
- `M07_BOMMatcher.MatchBOMRecord` - 观察匹配过程
- `M09_BOMExtractor.MatchAllMaterialTypes` - 观察错误记录
- `clsErrorLogger.Record` - 观察错误捕获
#### 3. 监视变量
添加监视变量:
- `params` - 查看提取的参数字典
- `matchingRows` - 查看匹配行集合
- `result("message")` - 查看错误消息
#### 4. 单元测试
参考 `M99_TestRunner.bas` 编写单元测试:
```vba
Sub Test_BOMMatcher_NoMatch()
Dim params As Object
Set params = CreateObject("Scripting.Dictionary")
params.Add "azxs", "INVALID"
Dim result As Object
Set result = M07_BOMMatcher.MatchBOMRecord(wsJoint, params)
Debug.Assert result("success") = False
Debug.Assert result("rowCount") = 0
Debug.Assert InStr(result("message"), "未找到") > 0
End Sub
```
---
## 附录A:相关文档
- [M06_ModelParser 型号解析机制](./M06_ModelParser_型号解析机制.md) - 了解参数提取过程
- [M08_ComponentProcessor 部件处理逻辑](./M08_ComponentProcessor_部件处理逻辑.md) - 深入理解部件特殊处理
- [RunBOMExtraction 完整流程](./RunBOMExtraction_流程详解.md) - 端到端流程说明
---
## 附录B:修订历史
| 版本 | 日期 | 作者 | 修订内容 |
|------|------|------|---------|
| 1.0 | 2025-02-12 | Claude | 初始版本,完整覆盖错误判断机制 |
| 2.0 | 2025-02-12 | Claude | **重大更新**:实施两阶段验证机制,新增警告类型,更新部件/接头/弹性元件交叉验证 |
---
**文档状态:** ✅ 完成
**最后更新:** 2025-02-12
**维护者:** AutoBOM开发团队