# BOM单多级优化开发文档 ## 一、现状分析 ### 1.1 现有系统架构 当前 `md_new_bom` 模块为**单层BOM结构**:每个BOM主表记录(`md_new_bom`)对应一个产品的物料信息,其下挂多条BOM明细(`md_new_bom_item`),每条明细直接对应具体物料。 现有表字段中,`parent_id` 和 `has_children` 已预置,但未真正实现多级递归展开。 **现有数据模型:** ``` md_new_bom (1) --< md_new_bom_item (N) └─ materialId (直接指向物料表) ``` ### 1.2 关键代码现状 | 层面 | 文件 | 现状 | |---|---|---| | 实体-主表 | `NewBom.java` | 有 `parentId`、`hasChildren`、`children`(非持久化)字段 | | 实体-明细 | `NewBomItem.java` | 无 `subBomId` 引用字段,明细不能挂子BOM | | Mapper | `NewBomMapper.xml` | `selectNewBomTree` 仅取一层子节点,无递归 | | Service | `NewBomServiceImpl.java` | `calculateBomCost` 仅计算直接子项成本,未递归 | | 前端 | `newBom/index.vue` | 已有树形表格外壳,但展开逻辑不完整 | --- ## 二、需求定义:什么是本系统的多级BOM 本系统面向离散制造业,多级BOM的核心诉求是:**支持子装配(Sub-Assembly)层级**。 产品BOM的明细物料中,一部分为采购/消耗的原材料,另一部分为**半成品组件**(子装配件)。这些组件本身也有BOM。 **目标结构(示例):** ``` 产品: A (整机BOM V1.0) ├── 物料: 螺丝 M3 x 100 pcs (直接材料) ├── 物料: 外壳组件 x 1 pcs (子装配BOM) │ ├── 物料: 外壳塑料件 x 1 (孙子层) │ └── 物料: 卡扣 x 4 (孙子层) └── 物料: PCB主板 x 1 (子装配BOM) ├── 物料: 芯片 xxx x 2 └── 物料: PCB板 x 1 ``` --- ## 三、方案设计 ### 3.1 总体思路 **不动数据库表结构**(`md_new_bom` 主表不变),仅做以下扩展: 1. 在 `md_new_bom_item` 新增 `sub_bom_id` 字段,标识该明细行是否引用了另一个BOM 2. 在 Service 层新增**递归成本计算**方法 3. 前端增强树形展开展示和子BOM展开功能 4. 提供**多级BOM展开视图**,可一键将多层BOM展开为单层物料清单(物料去重合并用量) ### 3.2 数据库变更 **ALTER TABLE `md_new_bom_item`:** ```sql ALTER TABLE `md_new_bom_item` ADD COLUMN `sub_bom_id` bigint(20) DEFAULT NULL COMMENT '引用的子BOM ID(多级BOM用)' AFTER `process_route`, ADD COLUMN `is_sub_bom` tinyint(1) DEFAULT '0' COMMENT '是否子BOM项 0-否 1-是' AFTER `sub_bom_id`, ADD COLUMN `level` int(11) DEFAULT '0' COMMENT 'BOM层级深度' AFTER `is_sub_bom`, ADD KEY `idx_sub_bom_id` (`sub_bom_id`); ``` > **说明:** 保留原 `materialId` 字段,但当 `is_sub_bom=1` 时,`materialId` 指向子装配件的物料ID,`sub_bom_id` 指向该物料对应的BOM ID。 --- ## 四、后端改造 ### 4.1 实体类变更 **`NewBomItem.java`** 新增字段: ```java /** 引用的子BOM ID(多级BOM用) */ private Long subBomId; /** 是否子BOM项 0-否 1-是 */ private Boolean isSubBom; /** BOM层级深度 */ private Integer level; ``` **`NewBom.java`** 新增非持久化字段(用于前端展开): ```java /** 多级展开后的子层BOM列表(仅在多级查询时填充) */ @TableField(exist = false) private List expandedChildren; /** 多级成本(递归计算结果) */ @TableField(exist = false) private BigDecimal expandedMaterialCost; ``` ### 4.2 Service 层新增方法 在 `INewBomService.java` 和 `NewBomServiceImpl.java` 中新增: #### 4.2.1 递归成本计算 ```java /** * 递归计算多级BOM成本(支持无限层级) * @param bomId BOM主键 * @param topQuantity 顶层需求量(用于按比例缩放子级用量) * @return 包含多级明细的成本分析 */ Map calculateMultiLevelBomCost(Long bomId, BigDecimal topQuantity); ``` **核心算法:** ``` calculateMultiLevelBomCost(bomId, quantity): bom = selectNewBomById(bomId) totalMaterialCost = 0 totalLaborCost = bom.laborCost totalManufacturingCost = bom.manufacturingCost for each item in bom.bomItems: scaleFactor = quantity / bom.baseQuantity if item.isSubBom == true: childCost = calculateMultiLevelBomCost(item.subBomId, item.quantity * scaleFactor) totalMaterialCost += childCost.materialCost else: itemCost = item.quantity * scaleFactor * item.unitPrice * (1 + item.lossRate/100) totalMaterialCost += itemCost return { materialCost, laborCost, manufacturingCost, totalCost, itemDetails } ``` #### 4.2.2 完整BOM树查询 ```java /** * 查询BOM多级展开树(递归向下查询指定层级) * @param bomId 起始BOM ID * @param maxLevel 最大展开层级(默认3层,防止性能问题) * @return 带完整子级结构的BOM树 */ NewBom selectBomFullTree(Long bomId, Integer maxLevel); ``` #### 4.2.3 BOM展开清单(物料去重合并) ```java /** * 将多级BOM展开为单层物料清单(含去重合并用量) * @param bomId 起始BOM ID * @param quantity 需求量 * @return 扁平化物料列表(含各层汇总用量) */ List> expandBomToFlatList(Long bomId, BigDecimal quantity); ``` **展开合并算法:** ``` expandBomToFlatList(bomId, quantity): flatList = [] recursiveCollect(bomId, quantity, visitedBomIds): bom = selectBomById(bomId) if bom.id in visitedBomIds: return // 防止循环引用 add bom.id to visitedBomIds scale = quantity / bom.baseQuantity for each item in bom.items: if item.isSubBom: recursiveCollect(item.subBomId, item.quantity * scale, visitedBomIds) else: merge into flatList: 物料ID -> 累加用量 return flatList ``` ### 4.3 Controller 层新增接口 在 `NewBomController.java` 新增端点: | 方法 | 路径 | 说明 | |---|---|---| | `GET` | `/masterdata/newBom/multiLevel/{id}` | 获取BOM多级展开树 | | `GET` | `/masterdata/newBom/cost/multiLevel/{id}` | 多级递归成本计算 | | `GET` | `/masterdata/newBom/expand/{id}` | 展开为扁平物料清单 | ### 4.4 多级展开时需防止的问题 1. **循环引用检测:** A->B->A 的情况,在递归入口处用 `Set visitedBomIds` 记录已访问的BOM ID 2. **最大层级限制:** 默认限制 10 层,超出则提示用户"BOM层级过深" 3. **性能:** 超过 3 层时前端应显示加载状态,后端加缓存(可选) --- ## 五、前端改造 ### 5.1 BOM管理页增强 文件:`mes-ui/src/views/mes/masterdata/newBom/index.vue` **5.1.1 新增"子BOM物料"输入模式** 在新增/编辑BOM明细行时,增加一个切换: - **普通物料行**(默认):选择物料,填写用量、损耗率 - **子BOM行**:选择"子装配物料" + 选择该物料对应的BOM版本 前端在选择物料时,如果该物料在 `md_new_bom` 中有已发布的BOM,则自动提示可挂子BOM。 **5.1.2 明细行组件增强** 在现有的 BOM 明细表格中,每行增加: - 列:`类型` — 显示「普通」或「子BOM」,不同颜色标签区分 - 列:`引用BOM` — 当类型为子BOM时显示引用的BOM编号 - 操作:`展开`按钮 — 仅子BOM行可用,点击后在该行下方嵌入子BOM明细表格 ### 5.2 新增多级BOM视图页 新增 `mes-ui/src/views/mes/masterdata/newBom/multiLevel.vue`: - 输入:BOM选择 + 需求量 - 展示:树形结构,多级展开/折叠 - 每行显示:层级缩进、物料编码、物料名称、用量、成本、是否关键件 - 底部显示:多级总成本汇总 - 按钮:「展开为清单」— 切换到扁平视图(物料去重合并) ### 5.3 API 层新增 文件:`mes-ui/src/api/mes/masterdata/newBom.js` ```javascript // 多级BOM查询 export const getBomMultiLevel = (id, maxLevel) => { return request({ url: `/masterdata/newBom/multiLevel/${id}`, params: { maxLevel } }) } // 多级成本计算 export const getBomMultiLevelCost = (id, quantity) => { return request({ url: `/masterdata/newBom/cost/multiLevel/${id}`, params: { quantity } }) } // BOM展开清单 export const expandBom = (id, quantity) => { return request({ url: `/masterdata/newBom/expand/${id}`, params: { quantity } }) } ``` --- ## 六、实现计划(分三个阶段) ### 第一阶段:基础设施(1-2天) - [ ] 执行 `ALTER TABLE` 新增字段 - [ ] 修改 `NewBomItem.java` 实体,添加 `subBomId`、`isSubBom`、`level` 字段 - [ ] 修改 `NewBom.java` 添加非持久化展开字段 - [ ] 修改 `NewBomMapper.xml` 新增字段映射 - [ ] 单元测试验证字段变更不影响现有CRUD ### 第二阶段:核心逻辑(2-3天) - [ ] 在 `INewBomService.java` 新增 3 个接口方法声明 - [ ] 在 `NewBomServiceImpl.java` 实现 `calculateMultiLevelBomCost`(递归) - [ ] 在 `NewBomServiceImpl.java` 实现 `selectBomFullTree`(递归树查询) - [ ] 在 `NewBomServiceImpl.java` 实现 `expandBomToFlatList`(去重合并) - [ ] 在 `NewBomController.java` 新增 3 个API端点 - [ ] 循环引用和最大层级检测逻辑 ### 第三阶段:前端改造(2-3天) - [ ] 前端 API 层新增 3 个接口调用 - [ ] 修改 BOM 明细行组件,支持子BOM行类型 - [ ] 新增 `multiLevel.vue` 多级BOM视图页 - [ ] 树形展开/折叠交互 - [ ] 扁平化展开清单视图(物料去重合并展示) --- ## 七、风险与注意事项 1. **循环引用:** A BOM -> B BOM -> A BOM 必须被检测并阻止,递归时用 `visitedIds` 集合 2. **性能:** 深层BOM(>5层)成本计算较慢,考虑异步计算或后台任务 3. **数据迁移:** 已有的 BOM 明细数据 `is_sub_bom=0`,`level=0`,无需迁移 4. **版本一致性:** 子BOM引用时,限定只能引用"已发布"状态的BOM 5. **兼容旧数据:** 新字段 `sub_bom_id` 允许 NULL,向下兼容 --- ## 八、预期效果 | 角色 | 收益 | |---|---| | 工艺人员 | 一个页面管理整机BOM,可挂子装配BOM,无需分开维护多张独立BOM | | 成本会计 | 一键计算整机(含所有子级)的真实材料成本 | | 生产计划 | 展开清单可直接输出为生产领料参考清单 | | 系统 | 复用现有表结构,改动小,风险可控 |