Files
AutoBOM/docs/提取备注数据来源.md
Misaka_Company 04099d25bc docs: add extraction remarks data source documentation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-16 09:58:29 +08:00

445 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 提取备注字段数据来源分析
**文档生成时间**: 2026-03-13
**入口函数**: `MainModule.ProcessProductModels()`
**输出位置**: `BOM 提取结果` 工作表的"提取备注"列(最后一列)
---
## 核心数据流图
```mermaid
flowchart TD
A[ProcessProductModels<br/>主入口] --> B[ProcessSingleModel<br/>处理单个型号]
B --> C{解析产品型号<br/>parser.Parse}
C -->|解析失败 | D["extractNote = <br/>解析失败: + ErrorMessage"]
C -->|解析成功 | E[BomExtractor.ExtractBom<br/>提取 BOM]
E --> F[DetermineRequiredCategories<br/>确定必需类别]
F --> G[MatchItems<br/>匹配物料]
G --> H{匹配数量?}
H -->|0 条 | I[不立即报错<br/>移交 ValidateResult]
H -->|1 条 | J[正常添加到结果集]
H -->|多条 | K["记录错误到 pErrorMessages<br/>类别 X 匹配到多条物料 N 条"]
K --> L[设置 item.MatchError<br/>并添加所有匹配项]
I --> M[ValidateResult<br/>双向覆盖检查]
L --> M
J --> M
M --> N{必需类别存在?}
N -->|被子类覆盖 | O[视为正常<br/>不报错]
N -->|被父类覆盖 | O
N -->|确实缺失 | P["记录错误<br/>必需类别 X 未匹配"]
O --> Q[GetErrorSummary<br/>汇总错误]
P --> Q
Q --> R{bomErrors 为空?}
R -->|非空 | S[extractNote = bomErrors]
R -->|空 | T[extractNote 保持空]
S --> U{matchedItems 为空?}
T --> U
U -->|是 | V{extractNote 为空?}
U -->|否 | W[遍历每个 item]
V -->|是 | X["extractNote = <br/>未匹配到任何物料"]
V -->|否 | Y[保持现有 extractNote]
X --> Z1[CreateOutputRowArray<br/>创建输出行]
Y --> Z1
W --> AA[For Each item<br/>itemNote = extractNote]
AA --> AB{item.MatchError<br/>非空?}
AB -->|是 | AC["拼接itemNote += <br/>; + MatchError"]
AB -->|否 | AD[保持 itemNote]
AC --> AE[CreateOutputRowArray<br/>创建输出行]
AD --> AE
Z1 --> AF[输出到 BOM 提取结果<br/>提取备注列]
AE --> AF
style D fill:#ff6b6b
style K fill:#ffa94d
style P fill:#ff6b6b
style X fill:#51cf66
style AF fill:#339af0,color:#fff
```
---
## 详细数据来源
### 来源 1: 产品型号解析失败
**位置**: `MainModule.bas` 第 184-188 行
**触发条件**: `ProductModelParser.Parse()` 返回 `False`
**错误来源**: `ProductModelParser.ErrorMessage`
```vba
' MainModule.bas:184-188
If Not parser.Parse(modelString) Then
' 解析失败
extractNote = "解析失败:" & parser.ErrorMessage
outputData.Add CreateOutputRowArray(..., extractNote, Nothing)
Exit Sub
End If
```
**可能的错误消息**(来自 `ProductModelParser.cls`:
| 错误场景 | 错误消息示例 | 源码位置 |
|---------|-------------|---------|
| 缺少表头部分 | `型号格式错误:缺少表头部分` | Parse() L73 |
| 缺少'-'分隔符 | `表头格式错误:缺少'-'分隔符` | ParseHeader() L108 |
| 表头结构不完整 | `表头结构不完整:缺少必要字段` | ParseHeader() L114 |
| 过程连接代码格式错误 | `过程连接代码格式错误:长度不足` | ExtractConnectionAndMaterial() L205 |
| 最后一位不是数字 | `过程连接代码格式错误:最后一位不是数字` | ExtractConnectionAndMaterial() L213 |
| 解析异常 | `解析表头异常:[VBA 错误描述]` | ParseHeader() ErrorHandler L131 |
---
### 来源 2: BOM 提取器错误汇总
**位置**: `MainModule.bas` 第 197-200 行
**触发条件**: `BomExtractor.GetErrorSummary()` 返回非空字符串
**错误来源**: `BomExtractor.pErrorMessages` 集合
```vba
' MainModule.bas:197-200
Dim bomErrors As String
bomErrors = BomExtractor.GetErrorSummary
If bomErrors <> "" Then
extractNote = bomErrors
End If
```
**错误汇总逻辑**`BomExtractor.cls` L471-481:
```vba
Public Function GetErrorSummary() As String
If pErrorMessages.Count = 0 Then
GetErrorSummary = ""
Else
Dim result As String
Dim msg As Variant
For Each msg In pErrorMessages
result = result & CStr(msg) & "; " ' 使用"; " 连接
Next msg
GetErrorSummary = result
End If
End Function
```
---
### 来源 3: 多匹配错误
**位置**: `BomExtractor.cls` 第 247-258 行
**触发条件**: 同一类别匹配到多条物料
**错误消息**: `类别 [X] 匹配到多条物料 (N 条)`
```vba
' BomExtractor.cls:247-258
ElseIf categoryMatches.Count = 1 Then
' 正常:匹配到 1 条
pMatchedItems.Add categoryMatches(1)
Else
' 异常:匹配到多条
Dim multiMsg As String
multiMsg = "类别 [" & category & "] 匹配到多条物料 (" & categoryMatches.Count & "条)"
pErrorMessages.Add multiMsg
' 临时处理:输出所有匹配的
Dim tempItem As BomItem
For Each tempItem In categoryMatches
tempItem.MatchError = multiMsg ' ← 设置到 item
pMatchedItems.Add tempItem
Next tempItem
End If
```
**特性**:
- 错误同时添加到 `pErrorMessages`(进入 GetErrorSummary
- 同时设置到 `item.MatchError`(逐行附加)
- 输出所有匹配项,但每条都带警告
---
### 来源 4: 必需类别缺失
**位置**: `BomExtractor.cls` 第 449-451 行
**触发条件**: ValidateResult 检测到必需类别未匹配且无覆盖
**错误消息**: `必需类别 [X] 未匹配`
```vba
' BomExtractor.cls:449-451
If Not isResolved Then
pErrorMessages.Add "必需类别 [" & category & "] 未匹配"
End If
```
**双向覆盖检查逻辑**:
```mermaid
flowchart LR
A[必需类别 X 缺失] --> B{检查 1: 有父类?}
B -->|是 | C{父类别已匹配?}
C -->|是 | D[被父类覆盖<br/>不报错]
C -->|否 | E[检查 2]
B -->|否 | E
E --> F{检查 2: 有子类?}
F -->|是 | G{所有子类都匹配?}
G -->|是 | H[被子类覆盖<br/>不报错]
G -->|否 | I[确实缺失<br/>报错]
F -->|否 | I
D --> J[Continue]
H --> J
I --> K[添加到 pErrorMessages]
style D fill:#51cf66
style H fill:#51cf66
style K fill:#ff6b6b
```
**覆盖场景示例**:
| 场景 | 父类别 | 子类别 1 | 子类别 2 | 结果 |
|------|--------|---------|---------|------|
| 总成优先 | ✅ 部件 (1 条) | ✅ 接头 (2 条) | ✅ 弹性元件 (1 条) | 输出"部件",子类不报错 |
| 散件满足 | ❌ 部件 (缺失) | ✅ 接头 (2 条) | ✅ 弹性元件 (1 条) | 输出子类,父类不报错 |
| 确实缺失 | ❌ 部件 (缺失) | ❌ 接头 (缺失) | ✅ 弹性元件 (1 条) | 报错:"必需类别 [接头] 未匹配" |
---
### 来源 5: 无匹配物料
**位置**: `MainModule.bas` 第 203-208 行
**触发条件**: `matchedItems.Count = 0``extractNote` 为空
**错误消息**: `未匹配到任何物料`
```vba
' MainModule.bas:203-208
If matchedItems.Count = 0 Then
' 没有匹配项
If extractNote = "" Then
extractNote = "未匹配到任何物料"
End If
outputData.Add CreateOutputRowArray(..., extractNote, Nothing)
```
**注意**: 如果已有其他错误(如解析错误),则不会覆盖。
---
### 来源 6: 物料级 MatchError
**位置**: `MainModule.bas` 第 215-223 行
**触发条件**: `BomItem.MatchError` 非空
**错误来源**: 由 `BomExtractor.MatchItems()` 设置(见来源 3
```vba
' MainModule.bas:215-223
For Each item In matchedItems
Dim itemNote As String
itemNote = extractNote
' 添加物料特定的错误
If item.MatchError <> "" Then
If itemNote <> "" Then itemNote = itemNote & "; "
itemNote = itemNote & item.MatchError
End If
' ... 添加到输出
Next item
```
**特性**:
- 每条 BOM 物料单独输出时附加
- 使用 `"; "` 分隔符拼接
- 首行继承全局 `extractNote`,后续行只继承不含物料级错误
---
## 错误消息拼接规则
```mermaid
sequenceDiagram
participant M as MainModule
participant P as ProductModelParser
participant E as BomExtractor
participant I as BomItem
M->>P: Parse modelString
alt 解析失败
P-->>M: 返回 ErrorMessage
Note over M: extractNote = 解析失败 + ErrorMessage
M->>M: 输出单行后结束
else 解析成功
M->>E: ExtractBom conditions
Note over E: 收集错误到 pErrorMessages
Note over E: 设置 item.MatchError
E-->>M: GetErrorSummary
alt bomErrors 非空
Note over M: extractNote = bomErrors
else bomErrors 为空
Note over M: extractNote 保持空
end
alt matchedItems = 0
Note over M: 设为 未匹配到任何物料
M->>M: 输出单行后结束
else matchedItems > 0
loop For Each item
M->>M: itemNote = extractNote
alt item.MatchError 非空
Note over M: itemNote += MatchError
end
M->>M: CreateOutputRowArray
end
end
end
Note over M: 输出到提取备注列
```
**拼接规则总结**:
| 场景 | 拼接方式 | 示例 |
|------|---------|------|
| 全局错误汇总 | 分号连接 | 错误 1; 错误 2 |
| 物料级附加 | 检查非空后加分号 | extractNote + MatchError |
| 多类别缺失 | 逐个添加到集合后汇总 | 必需类别 A 未匹配; 必需类别 B 未匹配 |
| 解析失败 + 其他 | 解析失败时立即退出不叠加 | 解析失败:... |
---
## 输出列定义
**位置**: `MainModule.bas` 第 270 行
```vba
' MainModule.bas:242-270
Private Sub WriteOutputHeader(ws As Worksheet)
' ... 前面的列 ...
ws.Cells(1, col).Value = "66 代码": col = col + 1
ws.Cells(1, col).Value = "提取备注": col = col + 1 ' ← 最后一列
End Sub
```
**数据写入**: `CreateOutputRowArray()` 函数的最后一个元素
```vba
' MainModule.bas:283-339
Private Function CreateOutputRowArray(..., note As String, ...) As Variant()
' ... 填充前面的列 ...
' 备注(最后一列)
rowData(col) = note
CreateOutputRowArray = rowData
End Function
```
---
## 完整错误消息类型汇总表
| 错误类型 | 错误消息模板 | 触发条件 | 源码位置 | 是否可叠加 |
|---------|-------------|---------|---------|----------|
| **解析错误** | `解析失败:[具体原因]` | Parse() 失败 | MainModule.bas:186 | ❌ 单独输出 |
| **多匹配** | `类别 [X] 匹配到多条物料 (N 条)` | 同类别匹配>1 | BomExtractor.cls:250 | ✅ 可叠加 |
| **必需类别缺失** | `必需类别 [X] 未匹配` | ValidateResult 检测缺失 | BomExtractor.cls:450 | ✅ 可叠加 |
| **无匹配** | `未匹配到任何物料` | matchedItems.Count = 0 | MainModule.bas:207 | ❌ 仅当无其他错误 |
| **物料级多匹配** | `类别 [X] 匹配到多条物料 (N 条)` | item.MatchError | MainModule.bas:221 | ✅ 逐行附加 |
---
## 典型输出示例
### 示例 1: 解析失败
```
产品型号Y-100-M203.316SS
提取备注:解析失败:表头结构不完整:缺少必要字段
```
### 示例 2: 正常匹配(无错误)
```
产品型号Y-100-M203.316SS.L100.N2
提取备注:(空)
```
### 示例 3: 多匹配错误
```
产品型号Y-100-M203.316SS.L100.N2
提取备注:类别 [接液材质] 匹配到多条物料 (3 条); 类别 [量程范围] 匹配到多条物料 (2 条);
```
### 示例 4: 必需类别缺失
```
产品型号Y-100-M203.316SS.L100.N2
提取备注:必需类别 [安装形式] 未匹配; 必需类别 [表壳形式] 未匹配;
```
### 示例 5: 无匹配
```
产品型号INVALID-MODEL
提取备注:未匹配到任何物料
```
### 示例 6: 物料级错误附加
```
产品型号Y-100-M203.316SS.L100.N2
行号 | 类别 | 提取备注
-----|------|----------
1 | 接头 | 类别 [接液材质] 匹配到多条物料 (3 条); 类别 [接液材质] 匹配到多条物料 (3 条);
2 | 弹性元件 | 类别 [接液材质] 匹配到多条物料 (3 条);
```
---
## 关键代码路径索引
| 功能 | 文件 | 行号范围 |
|------|------|---------|
| 主入口 | MainModule.bas | 20-150 |
| 单型号处理 | MainModule.bas | 161-235 |
| 输出表头定义 | MainModule.bas | 242-271 |
| 行数据创建 | MainModule.bas | 283-339 |
| 型号解析 | ProductModelParser.cls | 60-91 |
| BOM 提取 | BomExtractor.cls | 130-152 |
| 匹配物料 | BomExtractor.cls | 208-261 |
| 总成逻辑 | BomExtractor.cls | 270-367 |
| 结果验证 | BomExtractor.cls | 376-455 |
| 错误汇总 | BomExtractor.cls | 471-482 |
| 物料数据模型 | BomItem.cls | 1-89 |
---
## 设计特点
**优点**:
1. 错误信息分层清晰(解析层、匹配层、验证层)
2. 支持多错误叠加,不丢失任何警告
3. 双向覆盖检查避免误报(总成/散件场景)
4. 物料级错误逐行附加,便于定位问题
⚠️ **注意事项**:
1. 解析失败时立即退出,不执行后续 BOM 提取
2. 多匹配错误会输出所有匹配项(数据不确定时保留全部)
3. 错误消息使用 `"; "` 分隔,末尾可能有多余分隔符
4. "未匹配到任何物料"仅在无其他错误时显示
---
**文档结束**