Major changes: - Remove dependency on separate [领料配置] sheet - Load all data from [平台配置清单] in a single pass - Add "类别选用条件" (CategorySelectCondition) field to support dynamic category filtering - Add "66代码" (Code66) field for 66-system integration - Refactor completeness check logic to only verify required categories Data model changes: - clsMaterialItem: Add CategorySelectCondition and Code66 properties - clsCategory: Add CategorySelectCondition property and IsRequiredForModel() method - clsBOMManager: Refactor LoadData() to use single data source - clsBOMManager: Add GetRequiredCategories() method - clsBOMManager: Update GetValidMaterialsByModel() to use category selection conditions New workflow: - LoadData() now takes only wsPlatform parameter (removed wsConfig) - Categories are filtered by "类别选用条件" before completeness check - Categories with empty selection condition are always required - Categories with non-empty condition are checked against model specifications Documentation: - Update CLAUDE.md with new sheet structure - Add comprehensive refactoring plan document - Add v2.0 LoadData flow analysis with Mermaid diagrams - Add platform configuration sheet reference Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
19 KiB
重构计划:整合 [领料配置] 到 [平台配置清单]
一、背景概述
1.1 重构目标
将原有的 [领料配置] 表格的功能整合到 [平台配置清单] 中,简化数据结构,统一管理。
1.2 变化原因
- 原有设计:物料基础信息在 [平台配置清单],类别配置在 [领料配置],数据分散
- 新设计:所有信息集中在 [平台配置清单] 一张表,便于维护
二、新工作表结构分析
2.1 新表结构
[平台配置清单] 表结构(第3行标题):
| 列 | 字段名 | 说明 | 示例 |
|---|---|---|---|
| A | 行号 | 数据行序号 | 10, 20, 30 |
| B | 模块 | 模块分类 | 316L部件 |
| C | 代号 | 物料代号 | 01011009557 |
| D | 名称 | 物料名称 | 径向低压接头部件 |
| E | 数量 | 物料数量 | 1.0 |
| F | 选择条件 | 物料选用条件 | gclj=M20 AND jycz=1 |
| G | 备注 | 备注信息 | |
| H | 类别 | ⭐ 新增:物料所属类别 | 部件 |
| I | 上层类别 | ⭐ 新增:父类别名称 | |
| J | 类别选用条件 | ⭐ 新增:判断该类别是否需要 | lcfw=M02 |
| K | 66代码 | ⭐ 新增:66系统代码 | 66021009557 |
2.2 字段说明
新增字段详解
-
类别(H列)
- 类型:字符串
- 说明:物料所属的类别名称
- 示例:
部件、表壳、接头、机芯 - 为空:表示该物料不属于任何类别(不需要领料)
-
上层类别(I列)
- 类型:字符串
- 说明:父类别的名称,用于构建类别层级树
- 示例:
接头的上层类别是部件 - 为空:表示该类别是根类别
-
类别选用条件(J列) ⭐ 关键字段
- 类型:字符串(逻辑表达式)
- 说明:用于判断该类别是否需要被选用
- 格式:与"选择条件"字段相同的逻辑表达式
- 判断规则:
- 为空:该类别总是需要(无条件)
- 不为空:需要用型号提取的条件去匹配,匹配成功才需要
- 示例:
lcfw=M02:只有当量程是 M02 时才需要这个类别gclj=M20 AND jycz=1:只有当过程连接是 M20 且接液材质是 1 时才需要
-
66代码(K列)
- 类型:字符串
- 说明:66系统使用的代码(具体用途待补充)
- 示例:
66021009557
三、核心业务逻辑变化
3.1 旧逻辑(重构前)
有效性检查规则:
- 所有根类别都必须匹配到至少1个物料
- 每个根类别(或其子类别)必须有且仅有1个物料匹配
- 如果有任何一个类别不满足,则物料集合不完整
问题:
- 无法表达"某些型号不需要某些类别"的情况
- 例如:某些型号可能不需要"机芯"类别
3.2 新逻辑(重构后)
有效性检查规则:
- 第一步:确定哪些类别是该型号需要的
- 遍历所有有物料的类别
- 如果"类别选用条件"为空 → 该类别总是需要
- 如果"类别选用条件"不为空 → 用型号条件去匹配,匹配成功才需要
- 第二步:只检查需要的类别是否有物料匹配
- 对于需要的类别,必须有且仅有1个物料匹配
- 不需要的类别不参与完整性检查
示例场景:
型号:YTHN-100.A0.532.M203.M16.Y3 (量程 M16)
| 类别 | 类别选用条件 | 是否需要 | 说明 |
|---|---|---|---|
| 表壳 | (空) | ✅ 需要 | 无条件,总是需要 |
| 部件 | lcfw=M02 | ❌ 不需要 | 型号量程是 M16,不满足条件 |
| 接头 | lcfw=M16 | ✅ 需要 | 型号量程是 M16,满足条件 |
| 机芯 | gclj=M20 | ❌ 不需要 | 型号过程连接不是 M20 |
结果:只需要检查 表壳 和 接头 类别的物料完整性
四、代码影响分析
4.1 需要修改的模块和方法
1. clsBOMManager 类 ⭐ 核心改动
1.1 LoadData 方法
' 旧签名
Public Sub LoadData(wsConfig As Worksheet, wsPlatform As Worksheet)
' 新签名
Public Sub LoadData(wsPlatform As Worksheet)
变化:
- ❌ 移除
wsConfig参数(不再需要 [领料配置] 表) - ✅ 所有数据从 [平台配置清单] 读取
- ✅ 读取列数增加:从 A-F 扩展到 A-K
读取列对应:
' 第一步:加载物料和类别信息(一次性完成)
For i = 4 To lastRow
' 基础信息
mat.code = Trim(wsPlatform.Cells(i, "C").value) ' 代号
mat.Name = Trim(wsPlatform.Cells(i, "D").value) ' 名称
mat.Quantity = CDbl(wsPlatform.Cells(i, "E").value) ' 数量
mat.Condition = Trim(wsPlatform.Cells(i, "F").value) ' 选择条件
' ⭐ 新增:类别相关字段
catName = Trim(wsPlatform.Cells(i, "H").value) ' 类别
parentCatName = Trim(wsPlatform.Cells(i, "I").value) ' 上层类别
catSelectCond = Trim(wsPlatform.Cells(i, "J").value) ' 类别选用条件
code66 = Trim(wsPlatform.Cells(i, "K").value) ' 66代码
Next i
流程变化:
旧流程(三步):
1. 从 [平台配置清单] 加载物料基础信息
2. 从 [领料配置] 加载类别信息
3. 建立类别层级关系
新流程(两步):
1. 从 [平台配置清单] 加载物料和类别信息(一次性)
2. 建立类别层级关系
1.2 GetValidMaterialsByModel 方法
变化:
- ✅ 需要先判断哪些类别是该型号需要的
- ✅ 只检查需要的类别的完整性
新逻辑:
Public Function GetValidMaterialsByModel(modelStr As String) As collection
' 1. 解析型号并提取条件
Set conditions = ParseModelAndExtractConditions(modelStr)
' 2. ⭐ 新增:确定需要的类别列表
Dim requiredCategories As collection
Set requiredCategories = GetRequiredCategories(conditions)
' 3. 只检查需要的类别
For Each cat In requiredCategories
Set catResult = CheckCategoryCompleteness(cat, matcher, conditions)
' ...
Next cat
End Function
1.3 GetRequiredCategories 方法(新增)
功能:根据型号条件,确定哪些类别是需要的
Private Function GetRequiredCategories(conditions As Object) As collection
' 遍历所有类别
' - 类别选用条件为空 → 添加到需要列表
' - 类别选用条件不为空 → 用 conditions 去匹配
' - 匹配成功 → 添加到需要列表
' - 匹配失败 → 不添加
End Function
1.4 CheckCategoryCompleteness 方法
变化:
- 无需修改,因为调用者已经过滤了不需要的类别
- 逻辑保持不变:检查需要的类别是否有且仅有1个物料匹配
2. clsCategory 类 ⭐ 需要扩展
新增属性:
Public CategorySelectCondition As String ' ⭐ 新增:类别选用条件
Public IsRequired As Boolean ' ⭐ 新增:是否需要(计算属性)
新增方法:
' 检查该类别是否需要(根据型号条件)
Public Function IsRequiredForModel(conditions As Object) As Boolean
If Me.CategorySelectCondition = "" Then
' 选用条件为空,总是需要
IsRequiredForModel = True
Else
' 需要用 conditions 去匹配 CategorySelectCondition
Dim matcher As New clsConditionMatcher
IsRequiredForModel = matcher.IsMatch(Me.CategorySelectCondition, conditions)
End If
End Function
3. clsMaterialItem 类 ⭐ 需要扩展
新增属性:
Public CategorySelectCondition As String ' ⭐ 新增:类别选用条件
Public Code66 As String ' ⭐ 新增:66代码
说明:
- 物料需要保存所属类别的"类别选用条件"
- 便于后续判断该物料是否需要
4.2 不需要修改的方法
以下方法不受影响,无需修改:
- ✅
GetRootCategories()- 获取根类别 - ✅
GetCategory(categoryName)- 获取类别对象 - ✅
GetMaterialsByModel()- 根据型号获取物料(不涉及完整性检查) - ✅
GetMaterialsForPicking()- 获取领料物料 - ✅
ParseModelAndExtractConditions()- 解析型号 - ✅
FilterCategoryWithSubcategories()- 类别筛选 - ✅
CollectAllMaterials()- 收集物料 - ✅
PrintCategoryTree()- 打印类别树
五、重构实施计划
5.1 实施步骤
阶段一:数据模型扩展(1-2小时)
任务清单:
-
✅ 修改 clsMaterialItem 类
- 添加
CategorySelectCondition属性 - 添加
Code66属性 - 更新类注释
- 添加
-
✅ 修改 clsCategory 类
- 添加
CategorySelectCondition属性 - 添加
IsRequiredForModel()方法 - 更新类注释
- 添加
-
✅ 添加测试用例
- 测试类别选用条件的匹配逻辑
- 测试空条件和非空条件的情况
阶段二:LoadData 方法重构(2-3小时)
任务清单:
-
✅ 修改方法签名
' 旧 Public Sub LoadData(wsConfig As Worksheet, wsPlatform As Worksheet) ' 新 Public Sub LoadData(wsPlatform As Worksheet) -
✅ 重写加载逻辑
- 移除第二步(从 [领料配置] 加载)
- 合并第一步和第二步(一次性从 [平台配置清单] 加载所有字段)
- 更新列索引映射:
- H列:类别
- I列:上层类别
- J列:类别选用条件
- K列:66代码
-
✅ 更新类别创建逻辑
- 创建类别时,设置
CategorySelectCondition属性 - 创建物料时,设置
CategorySelectCondition和Code66属性
- 创建类别时,设置
-
✅ 更新文档
- 更新
clsBOMManager_LoadData流程分析.md - 更新方法注释
- 更新
阶段三:完整性检查逻辑重构(2-3小时)
任务清单:
-
✅ 新增 GetRequiredCategories 方法
Private Function GetRequiredCategories(conditions As Object) As collection ' 遍历所有类别,判断是否需要 ' 返回需要的类别列表 End Function -
✅ 修改 GetValidMaterialsByModel 方法
- 在检查完整性前,先调用
GetRequiredCategories() - 只对需要的类别调用
CheckCategoryCompleteness() - 更新方法注释
- 在检查完整性前,先调用
-
✅ 测试完整性检查
- 测试所有类别都需要的情况(向后兼容)
- 测试部分类别不需要的情况(新逻辑)
- 测试边界情况(所有类别都不需要)
阶段四:测试与验证(2-3小时)
任务清单:
-
✅ 单元测试
- 测试
LoadData加载新表结构 - 测试
IsRequiredForModel()方法 - 测试
GetRequiredCategories()方法 - 测试
GetValidMaterialsByModel()新逻辑
- 测试
-
✅ 集成测试
- 使用真实数据测试完整流程
- 验证结果与预期一致
-
✅ 回归测试
- 运行
modModelParserTest中的所有测试 - 确保旧功能不受影响
- 运行
阶段五:文档更新(1小时)
任务清单:
-
✅ 更新 CLAUDE.md
- 更新 [平台配置清单] 表结构说明
- 移除 [领料配置] 相关说明
- 更新"类别选用条件"业务逻辑说明
-
✅ 更新参考文档
- 更新
clsBOMManager使用说明.md - 更新
clsBOMManager_LoadData流程分析.md
- 更新
-
✅ 添加迁移指南
- 创建
数据迁移指南.md - 说明如何将旧数据迁移到新结构
- 创建
5.2 风险评估
| 风险项 | 风险等级 | 缓解措施 |
|---|---|---|
| 数据结构变化导致现有代码报错 | 🔴 高 | 充分的单元测试和回归测试 |
| 类别选用条件逻辑理解偏差 | 🟡 中 | 详细的业务场景测试 |
| 向后兼容性问题 | 🟡 中 | 保留旧版本的 LoadData 方法(重命名为 LoadDataV1) |
| 性能影响 | 🟢 低 | 新逻辑只增加一次类别过滤,影响极小 |
5.3 时间估算
| 阶段 | 预计时间 | 负责人 |
|---|---|---|
| 阶段一:数据模型扩展 | 1-2 小时 | |
| 阶段二:LoadData 重构 | 2-3 小时 | |
| 阶段三:完整性检查重构 | 2-3 小时 | |
| 阶段四:测试与验证 | 2-3 小时 | |
| 阶段五:文档更新 | 1 小时 | |
| 总计 | 8-12 小时 |
六、数据迁移方案
6.1 从旧表到新表
旧 [领料配置] 结构:
| A列 | B列 | C列 | D列 |
|---|---|---|---|
| 物料代号 | 物料名称 | 类别 | 上层类别 |
新 [平台配置清单] 结构:
| ... | H列 | I列 | J列 | K列 |
|---|---|---|---|---|
| ... | 类别 | 上层类别 | 类别选用条件 | 66代码 |
迁移步骤:
-
在 [平台配置清单] 中添加新列:
- H列:类别
- I列:上层类别
- J列:类别选用条件
- K列:66代码
-
从 [领料配置] 复制数据:
H2 = [领料配置]!A2 ' 物料代号 → 类别(需要匹配) I2 = [领料配置]!C2 ' 类别 J2 = [领料配置]!D2 ' 上层类别 -
使用 VLOOKUP 匹配物料:
H4 = VLOOKUP(C4, [领料配置]!A:D, 3, FALSE) ' 类别 I4 = VLOOKUP(C4, [领料配置]!A:D, 4, FALSE) ' 上层类别 -
手动填写类别选用条件和66代码
七、测试用例设计
7.1 类别选用条件测试
| 测试场景 | 类别选用条件 | 型号条件 | 预期结果 |
|---|---|---|---|
| 无条件(总是需要) | (空) | 任意 | ✅ 需要 |
| 量程匹配 | lcfw=M02 | lcfw=M02 | ✅ 需要 |
| 量程不匹配 | lcfw=M02 | lcfw=M16 | ❌ 不需要 |
| 复杂条件匹配 | gclj=M20 AND jycz=1 | gclj=M20, jycz=1 | ✅ 需要 |
| 复杂条件不匹配 | gclj=M20 AND jycz=1 | gclj=M20, jycz=2 | ❌ 不需要 |
| OR 条件匹配 | lcfw=M02 OR lcfw=M16 | lcfw=M16 | ✅ 需要 |
7.2 完整性检查测试
| 测试场景 | 需要的类别 | 匹配结果 | 预期 IsComplete |
|---|---|---|---|
| 全部匹配 | 表壳, 部件, 接头 | 全部有1个 | ✅ True |
| 缺失一个 | 表壳, 部件, 接头 | 部件缺失 | ❌ False |
| 部分类别不需要 | 表壳, 部件 | 表壳有, 部件无(但部件不需要) | ✅ True |
| 多于一个匹配 | 表壳, 部件 | 部件有2个 | ❌ False |
八、向后兼容性策略
8.1 保留旧版本方法
' 旧版本(保留但不推荐使用)
Public Sub LoadDataV1(wsConfig As Worksheet, wsPlatform As Worksheet)
' 保留原有实现
End Sub
' 新版本(推荐使用)
Public Sub LoadData(wsPlatform As Worksheet)
' 新实现
End Sub
8.2 迁移期支持
在迁移期间,两个版本同时存在:
- 新代码使用
LoadData(wsPlatform) - 旧代码可以继续使用
LoadDataV1(wsConfig, wsPlatform)
迁移完成后,可以移除 LoadDataV1 方法。
九、检查清单
重构前
- 备份现有代码
- 备份 [领料配置] 和 [平台配置清单] 表
- 确认测试环境可用
重构中
- 按阶段顺序执行
- 每个阶段完成后进行测试
- 及时更新文档
重构后
- 运行完整的测试套件
- 验证所有功能正常
- 更新用户文档
- 通知团队成员变更内容
十、后续优化建议
-
性能优化
- 缓存类别选用条件的匹配结果
- 避免重复解析型号
-
功能扩展
- 支持类别优先级(当多个类别都匹配时,优先选择哪个)
- 支持类别别名(一个类别多个名称)
-
用户体验
- 在 Excel 中添加类别选用条件的验证工具
- 提供类别选用条件的构建器(图形化界面)
附录:关键代码示例
A. 修改后的 LoadData 方法(伪代码)
Public Sub LoadData(wsPlatform As Worksheet)
Dim i As Long, lastRow As Long
Dim mat As clsMaterialItem
Dim cat As clsCategory
' 读取数据
lastRow = wsPlatform.Cells(wsPlatform.Rows.Count, "C").End(xlUp).row
For i = 4 To lastRow
' 1. 创建物料对象
Set mat = New clsMaterialItem
mat.code = Trim(wsPlatform.Cells(i, "C").value)
mat.Name = Trim(wsPlatform.Cells(i, "D").value)
mat.Quantity = CDbl(wsPlatform.Cells(i, "E").value)
mat.Condition = Trim(wsPlatform.Cells(i, "F").value)
' ⭐ 读取类别相关字段
Dim catName As String
Dim parentCatName As String
Dim catSelectCond As String
Dim code66 As String
catName = Trim(wsPlatform.Cells(i, "H").value & "")
parentCatName = Trim(wsPlatform.Cells(i, "I").value & "")
catSelectCond = Trim(wsPlatform.Cells(i, "J").value & "")
code66 = Trim(wsPlatform.Cells(i, "K").value & "")
' ⭐ 保存到物料对象
mat.Category = catName
mat.ParentCategory = parentCatName
mat.CategorySelectCondition = catSelectCond
mat.Code66 = code66
' 保存到字典
If mat.code <> "" Then
Set dictAllMaterials(mat.code) = mat
End If
' ⭐ 创建或更新类别对象
If catName <> "" Then
If Not dictCategories.Exists(catName) Then
Set cat = New clsCategory
cat.categoryName = catName
cat.ParentCategoryName = parentCatName
cat.CategorySelectCondition = catSelectCond ' ⭐ 新增
Set dictCategories(catName) = cat
End If
' 将物料添加到类别
dictCategories(catName).AddMaterial mat
End If
Next i
' 建立类别层级关系(逻辑不变)
' ...
End Sub
B. 新增 GetRequiredCategories 方法(伪代码)
Private Function GetRequiredCategories(conditions As Object) As collection
Dim result As New collection
Dim matcher As New clsConditionMatcher
' 遍历所有类别
Dim key As Variant
For Each key In dictCategories.Keys
Dim cat As clsCategory
Set cat = dictCategories(key)
' 检查该类别是否需要
If cat.IsRequiredForModel(conditions) Then
result.Add cat
End If
Next key
Set GetRequiredCategories = result
End Function
C. 新增 clsCategory.IsRequiredForModel 方法(伪代码)
Public Function IsRequiredForModel(conditions As Object) As Boolean
If Me.CategorySelectCondition = "" Then
' 选用条件为空,总是需要
IsRequiredForModel = True
Else
' 需要用 conditions 去匹配 CategorySelectCondition
Dim matcher As New clsConditionMatcher
IsRequiredForModel = matcher.IsMatch(Me.CategorySelectCondition, conditions)
End If
End Function
文档版本:v1.0 创建日期:2026-01-29 最后更新:2026-01-29