Files
AutoBOM/docs/BOM匹配错误判断机制详解.md
Misaka_Company 53b3568412
All checks were successful
NTFY Notification / notify (push) Successful in 3s
feat: add special validation for edge material based on azxs parameter
Implement edge material validation logic that behaves differently based on azxs value:
- When azxs is A0, Z0, or B0: Edge material is NOT required (0 matches = OK, 1+ matches = ERROR)
- When azxs is AH, AT, BH, BT, BZ, ZH, ZT, or ZZ: Standard validation applies (exactly 1 match required)
- Supports dual-value azxs format (e.g., "A0,径向" extracts "A0" for validation)

Changes:
- M09_BOMExtractor.bas:
  - Update MatchAllMaterialTypesWithValidation to pass params to validation
  - Update ValidateAllMatchResults signature to accept params parameter
  - Add "边" to special sheets array
  - Implement Phase 3.5: Edge material validation with azxs-based rules
  - Update function header comments

- docs/BOM匹配错误判断机制详解.md:
  - Add section 6.5: Edge material special handling
  - Update error type table with EdgeMaterialError
  - Update function index and version history

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-12 18:31:33 +08:00

47 KiB
Raw Blame History

BOM匹配错误判断机制详解

目录

  1. 系统概述
  2. 核心匹配算法 (M07_BOMMatcher)
  3. 错误类型与检测
  4. 决策树与流程图
  5. 错误场景实例
  6. 部件特殊处理
  7. 错误报告机制
  8. 快速参考

重大更新 (v2.1 - 2025-02-12):

  • 新增边材料特殊验证基于azxs参数
  • 支持A0/Z0/B0不需要边的规则
  • 支持AH/AT/BH/BT/BZ/ZH/ZT/ZZ需要边的规则

重大更新 (v2.0 - 2025-02-12):

  • 实施两阶段验证机制(收集 → 验证)
  • 新增警告类型(非阻断性错误)
  • 部件/接头/弹性元件交叉验证增强
  • 错误报告增加"类型"列(错误/警告)

1. 系统概述

1.1 什么是BOM匹配

BOM匹配是AutoBOM系统的核心功能它根据产品型号中提取的参数azxsbkxsgcljjyczlcfwfjgn在BOM库中查找恰好1条匹配的物料记录。

核心目标精确匹配1:1匹配

匹配结果 状态 说明
0条记录 失败 未找到匹配物料
1条记录 成功 唯一匹配,可提取物料信息
2+条记录 失败 模糊匹配,无法确定唯一物料

1.2 为什么错误判断至关重要?

┌─────────────────────────────────────────────────────────┐
│              错误判断的业务价值                          │
├─────────────────────────────────────────────────────────┤
│ ✓ 防止错误物料流入生产环节                             │
│ ✓ 避免停工待料(库存不足)                             │
│ ✓ 减少人工纠错成本                                     │
│ ✓ 保证BOM准确性可追溯性                            │
│ ✓ 提高生产效率和产品质量                               │
└─────────────────────────────────────────────────────────┘

1.3 匹配哲学:精确匹配原则

AutoBOM系统采用严格的一对一匹配原则,并采用两阶段验证机制

graph LR
    A[产品型号参数] --> B{阶段1: 收集<br/>所有工作表匹配结果}
    B --> C{阶段2: 统一验证}
    C -->|0条匹配| D[❌ 失败<br/>记录错误]
    C -->|1条匹配| E[✅ 成功<br/>提取物料]
    C -->|2+条匹配| F[❌ 失败<br/>记录错误]
    C -->|特殊情况| G[⚠️ 警告<br/>记录但不阻断]

    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 模块。

函数签名:

Public Function MatchBOMRecord( _
    ByVal ws As Worksheet, _
    ByVal params As Object _
) As Object

输入参数:

  • ws: BOM库工作表对象如"接头"、"弹性元件"等)
  • params: 从产品型号中提取的参数字典

输出结构:

' 返回字典包含以下键:
result("success")   ' Boolean - 是否恰好匹配1条
result("rowCount")  ' Long - 匹配到的记录数量
result("rowNums")   ' Collection - 匹配到的行号集合
result("message")   ' String - 结果描述

2.2 匹配流程图

flowchart TD
    Start([开始匹配]) --> InputValidate{验证输入}
    InputValidate -->|ws为空| Error1[返回: 工作表为空]
    InputValidate -->|params为空| Error2[返回: 参数字典为空]
    InputValidate -->|验证通过| ReadData[读取工作表数据到数组]

    ReadData --> CheckEmpty{是否有数据?}
    CheckEmpty -->|无数据| Error3[返回: 工作表无数据]
    CheckEmpty -->|有数据| BuildMap[构建表头映射]

    BuildMap --> LoopStart[遍历数据行]
    LoopStart --> EvalRow{评估该行<br/>是否匹配?}
    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() 是最核心的判断逻辑,决定单个单元格是否满足匹配条件。

函数签名:

Public Function EvaluateCellCondition( _
    ByVal cellValue As Variant, _
    ByVal paramValue As String, _
    ByVal fieldName As String _
) As Boolean

2.3.1 匹配规则优先级

flowchart TD
    A[评估单元格条件] --> B{单元格为空?}
    B -->|是| C[✅ 通配符匹配<br/>返回True]

    B -->|否| D{以!=开头?}
    D -->|是| E[否定匹配<br/>paramValue ≠ notValue?]

    D -->|否| F{字段是fjgn?}
    F -->|是| G[包含匹配<br/>InStr判断]

    F -->|否| H[精确匹配<br/>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 不匹配 字符串不相等

代码实现:

' 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逻辑:所有参数列必须同时满足条件,该行才匹配成功。

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

代码实现:

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 错误类型总览

graph TD
    A[BOM匹配错误] --> B[匹配数量错误]
    A --> C[系统错误]
    A --> D[部件验证错误]

    B --> B1[0条匹配<br/>No Match]
    B --> B2[2+条匹配<br/>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

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

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章部件特殊处理


4. 决策树与流程图

4.1 完整匹配流程决策树(两阶段验证)

flowchart TD
    A[开始: 接收产品型号] --> B[解析型号提取参数]
    B --> C{参数提取成功?}
    C -->|否| Err1[记录错误: 型号解析失败]
    C -->|是| D[阶段1: 收集所有工作表匹配结果]

    D --> E{是'部件'工作表?}
    E -->|是| F[调用M08_ComponentProcessor<br/>存入resultsDict]
    E -->|否| G[调用M07_BOMMatcher<br/>存入resultsDict]

    F --> H{更多工作表?}
    G --> H
    H -->|是| D
    H -->|否| I[阶段2: 统一验证所有结果]

    I --> J{基础验证<br/>非特殊工作表}
    J --> K{特殊验证<br/>部件/接头/弹性元件}

    K --> L{验证通过?}
    L -->|否| ErrV[记录验证错误]
    L -->|是| M{有警告?}

    M -->|是| Warn[记录警告<br/>不阻断流程]
    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 错误检测决策树

flowchart TD
    Start([匹配结果检测]) --> CheckCount{匹配数量?}

    CheckCount -->|0| TypeA[No Match<br/>未找到匹配记录]
    CheckCount -->|1| Success[Success<br/>匹配成功]
    CheckCount -->|2+| TypeB[Multiple Match<br/>匹配到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 逻辑流程

flowchart TD
    A[开始评估单元格] --> B{cellValue为空<br/>或IsEmpty?}

    B -->|是| C[返回True<br/>通配符匹配]

    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 部件验证流程

flowchart TD
    A[验证部件组合] --> B{物料集合为空?}

    B -->|是| Err1[返回: 物料列表为空]

    B -->|否| C[统计各类型物料数量]

    C --> D{有1个部件<br/>且无子件?}
    D -->|是| Success1[验证通过: 1个部件]

    D -->|否| E{无部件<br/>且有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 错误报告序列图

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 场景10条匹配 - 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

匹配过程:

' 第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

错误输出:

{
    success: false,
    rowCount: 0,
    rowNums: [],
    message: "未找到匹配记录"
}

解决方案:

  1. 检查是否需要添加 azxs=A0 的记录到BOM库
  2. 确认型号解析是否正确A0是否应为AT

5.2 场景20条匹配 - 否定条件不满足

输入条件:

  • 提取参数:{azxs: "A0", gclj: "M20"}
  • BOM库"接头"工作表:
    azxs gclj 物料名称
    !=A0 M20 接头非A0

匹配过程:

' azxs列: cellValue="!=A0", paramValue="A0"
EvaluateCellCondition("!=A0", "A0", "azxs") → False (否定匹配: "A0" = "A0")
' 整行不匹配

' 最终结果: matchingRows.count = 0

错误输出:

{
    success: false,
    rowCount: 0,
    message: "未找到匹配记录"
}

解决方案:

  • 该记录明确排除 azxs=A0,这是预期行为
  • 如需支持 A0,需添加新记录或修改否定条件

5.3 场景32+条匹配 - 重复记录

输入条件:

  • 提取参数:{azxs: "A0", gclj: "M20"}
  • BOM库"接头"工作表:
    azxs gclj lcfw 物料名称 物料编码
    A0 M20 接头A0-1 JT-001
    A0 M20 接头A0-2 JT-002

匹配过程:

' 第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

错误输出:

{
    success: false,
    rowCount: 2,
    rowNums: [2, 3],
    message: "匹配到2条记录需要恰好1条"
}

解决方案:

  1. 删除重复记录
  2. 细化配置条件(如添加lcfw列区分)

5.4 场景42+条匹配 - 空单元格过多

输入条件:

  • 提取参数:{azxs: "A0", gclj: "M20"}
  • BOM库"接头"工作表:
    azxs gclj jycz lcfw fjgn 物料名称
    A0 M20 接头配置1
    A0 M20 接头配置2
    A0 M20 接头配置3

匹配过程:

' 所有3行的 jycz、lcfw、fjgn 都是空单元格
' 空单元格 → 通配符 → 全部返回True
' 导致3行都匹配

' 最终结果: matchingRows.count = 3

错误输出:

{
    success: false,
    rowCount: 3,
    rowNums: [2, 3, 4],
    message: "匹配到3条记录需要恰好1条"
}

解决方案:

  1. 补充缺失的配置列值
  2. 添加新的区分列(如bkxs特殊要求等)

5.5 场景5fjgn包含匹配

输入条件:

  • 提取参数:{azxs: "A0", fjgn: "N1,N2,N3"}
  • BOM库"接头"工作表:
    azxs fjgn 物料名称
    A0 N1 带N1功能
    A0 N4 带N4功能

匹配过程:

' 第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

正确输出:

{
    success: true,
    rowCount: 1,
    rowNums: [2],
    message: "匹配成功"
}

5.6 场景6部件验证错误

输入条件:

  • 提取参数:{azxs: "A0", gclj: "M20"}
  • 匹配结果物料集合:
    [
        {materialType: "接头", materialName: "接头A0", ...},
        {materialType: "接头", materialName: "接头AT", ...},  // 重复
        {materialType: "弹性元件", materialName: "元件M20", ...}
    ]
    

验证过程:

' 统计:
'   componentCount = 0
'   jointCount = 2
'   elementCount = 1

' 验证规则: jointCount (2) ≠ elementCount (1)
' → 验证失败

验证结果:

{
    valid: false,
    message: "部件组合异常: 接头数量(2)≠弹性元件数量(1)"
}

错误输出行:

原始产品型号 azxs ... 物料类型 物料名称 提取备注
YTHN-100... A0 ... 接头 接头A0 部件组合异常: 接头数量(2)≠弹性元件数量(1)

解决方案:

  1. 检查BOM库配置是否导致多次匹配
  2. 确保接头和弹性元件一对一配对

6. 部件特殊处理

6.1 部件物料特性

部件物料在AutoBOM系统中具有特殊地位

┌──────────────────────────────────────────────────────────┐
│                   部件物料结构                         │
├──────────────────────────────────────────────────────────┤
│ 1条"部件"记录 = {                                      │
│   - 部件本体(优先选择)                                │
│   - 子件1: 接头(备选方案)                             │
│   - 子件2: 弹性元件(备选方案)                         │
│ }                                                      │
│                                                        │
│ 选择逻辑:                                              │
│   ✓ 有库存 → 返回部件                                   │
│   ✗ 无库存 → 返回接头 + 弹性元件                        │
└──────────────────────────────────────────────────────────┘

6.2 部件处理流程

flowchart TD
    A[开始处理'部件'工作表] --> B[调用MatchBOMRecord]

    B --> C{匹配成功?}
    C -->|否| Err[记录匹配错误<br/>添加错误物料]

    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() 代码逻辑

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(库存充足),预留接口供未来扩展:

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. 连接数据库库存表

6.5 边特殊处理

6.5.1 边物料特性

边物料在AutoBOM系统中具有特殊规则根据azxs(安装形式)参数值决定是否需要边物料。

┌──────────────────────────────────────────────────────────┐
│                   边物料规则                             │
├──────────────────────────────────────────────────────────┤
│ azxs = A0, Z0, B0   → 不需要边0条=OK1+条=ERROR     │
│ azxs = AH, AT, BH, BT, BZ, ZH, ZT, ZZ → 需要边1条=OK  │
│ 其他azxs值         → 按常规验证1条=OK                │
└──────────────────────────────────────────────────────────┘

6.5.2 边验证流程

flowchart TD
    A[验证边材料] --> B{获取azxs参数}
    B --> C{提取rawAzxs值<br/>处理双值格式}

    C --> D{azxs in A0/Z0/B0?}
    D -->|是| E[不需要边模式]
    E --> F{边匹配数>0?}
    F -->|是| G[❌ 错误: 不应有边]
    F -->|否| H[✅ 通过: 无边物料]

    D -->|否| I{azxs in AH/AT/<br/>BH/BT/BZ/ZH/ZT/ZZ?}
    I -->|是| J[需要边模式]
    J --> K{边匹配数=1?}
    K -->|是| L[✅ 通过: 有边物料]
    K -->|否| M[❌ 错误: 0条或2+条]

    I -->|否| N[常规验证模式]
    N --> O{边匹配数=1?}
    O -->|是| L
    O -->|否| M

    style H fill:#51cf66
    style L fill:#51cf66
    style G fill:#ff6b6b
    style M fill:#ff6b6b

6.5.3 边验证规则表

azxs值 需要边? 0条匹配 1条匹配 2+条匹配
A0 不需要 OK ERROR ERROR
Z0 不需要 OK ERROR ERROR
B0 不需要 OK ERROR ERROR
AH 需要 ERROR OK ERROR
AT 需要 ERROR OK ERROR
BH 需要 ERROR OK ERROR
BT 需要 ERROR OK ERROR
BZ 需要 ERROR OK ERROR
ZH 需要 ERROR OK ERROR
ZT 需要 ERROR OK ERROR
ZZ 需要 ERROR OK ERROR
其他 常规 ERROR OK ERROR

6.5.4 ValidateEdgeMaterial() 代码逻辑

边材料验证集成在 M09_BOMExtractor.ValidateAllMatchResults() 函数的 Phase 3.5

' ========================================
' 3.5. 特殊验证:边 (Edge) 材料
' ========================================
Dim edgeResult As Object
Set edgeResult = Nothing

If resultsDict.Exists(BOMLIB_SHEET_EDGE) Then
    Set edgeResult = resultsDict(BOMLIB_SHEET_EDGE)
End If

' 检查azxs参数值
Dim azxsValue As String
Dim rawAzxs As String
azxsValue = ""

If params.Exists("azxs") Then
    azxsValue = CStr(params("azxs"))
    ' 提取原始azxs值处理双值格式如"A0,径向"
    If InStr(azxsValue, ",") > 0 Then
        rawAzxs = Trim(CStr(Split(azxsValue, ",")(0)))
    Else
        rawAzxs = Trim(azxsValue)
    End If
End If

' 定义不需要边的azxs值
Dim noEdgeAzxs As Variant
noEdgeAzxs = Array("A0", "Z0", "B0")

' 定义需要边的azxs值有后缀H/T/Z的值
Dim hasEdgeAzxs As Variant
hasEdgeAzxs = Array("AH", "AT", "BH", "BT", "BZ", "ZH", "ZT", "ZZ")

' 情况A: azxs为A0/Z0/B0不应该有边
' ...(验证逻辑见完整代码)

' 情况B: azxs为AH/AT/BH/BT/BZ/ZH/ZT/ZZ走常规判断
' ...(验证逻辑见完整代码)

6.5.5 边验证错误类型

错误类型 错误信息 触发条件 检测位置
EdgeMaterialError [边]工作表匹配到N条记录但azxs=X不需要边物料 azxs为A0/Z0/B0且边匹配数>0 M09_BOMExtractor.bas:763-766
No Match [边]未匹配到记录 azxs为AH/AT等且边匹配数=0 M09_BOMExtractor.bas:789-792
Multiple Match [边]匹配到N条记录 azxs为AH/AT等且边匹配数>1 M09_BOMExtractor.bas:793-795

6.5.6 设计决策:为什么边需要特殊处理?

业务背景:

  • 边材料的作用:某些产品配置需要在表盘边缘添加边框或装饰条
  • azxs依赖性边的需求取决于安装形式azxs参数
    • A0/Z0/B0基础安装形式:产品设计上不需要边
    • AH/AT/BH/BT/BZ/ZH/ZT/ZZ带后缀的安装形式:需要边作为装饰或功能性部件

技术实现考量:

  1. 避免误报如果对A0/Z0/B0型号使用标准验证0条匹配会被误判为错误
  2. 精确控制对于AH/AT等型号仍然需要严格执行1条匹配规则
  3. 双值兼容:支持"A0,径向"格式的azxs参数提取原始值进行判断

可扩展性: 本实现建立的模式可复用于其他需要参数依赖验证的物料类型:

' 未来可扩展为:
Function GetAzxsCategory(params As Object) As String
    ' 返回: NO_EDGE_REQUIRED / EDGE_REQUIRED / STANDARD
End Function

7. 错误报告机制

7.1 clsErrorLogger 类模块v2.0

clsErrorLogger 是系统的错误日志记录器,负责收集、存储和报告所有匹配错误和警告。

类结构v2.0

' 私有变量
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方法)
  • 区分错误和警告(HasErrorsHasWarningsHasIssues属性)
  • 错误报告增加"类型"列(错误/警告)
  • 警告行黄色高亮,错误行红色高亮

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 错误记录流程

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

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() 函数中,通过返回消息通知用户:

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库配置
接头和弹性元件数量不匹配 接头和弹性元件数量不匹配 特殊验证失败 确保接头和弹性元件一对一
EdgeMaterialError [边]工作表匹配到N条记录但azxs=X不需要边物料 azxs为A0/Z0/B0且边匹配数>0 检查BOM库配置或产品型号
边未匹配到记录 [边]未匹配到记录 azxs为AH/AT等且边匹配数=0 补充BOM库边记录
边匹配多条记录 [边]匹配到N条记录 azxs为AH/AT等且边匹配数>1 删除重复或细化边配置条件

警告类型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 581-815 新增 统一验证所有匹配结果,包含边材料特殊验证
ToArray M09_BOMExtractor 816-826 新增 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查看调试输出

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 编写单元测试:

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相关文档


附录B修订历史

版本 日期 作者 修订内容
1.0 2025-02-12 Claude 初始版本,完整覆盖错误判断机制
2.0 2025-02-12 Claude 重大更新:实施两阶段验证机制,新增警告类型,更新部件/接头/弹性元件交叉验证
2.1 2025-02-12 Claude 新增边材料特殊验证逻辑基于azxs参数的条件验证

文档状态: 完成 最后更新: 2025-02-12v2.1 - 边材料特殊验证) 维护者: AutoBOM开发团队