Files
MES/yawei-mes/.tasks/2026-03-27_v2.0.014_BOM单多级优化.md

304 lines
10 KiB
Markdown
Raw Permalink Normal View History

2026-04-02 10:38:23 +08:00
# 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<NewBom> 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<String, Object> 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<Map<String, Object>> 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<Long> 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 |
| 成本会计 | 一键计算整机(含所有子级)的真实材料成本 |
| 生产计划 | 展开清单可直接输出为生产领料参考清单 |
| 系统 | 复用现有表结构,改动小,风险可控 |