Browse Source

BOM复制后,临时表tree结构问题

java8
han\hanst 2 months ago
parent
commit
a22c1501ca
  1. 4
      src/main/java/com/xujie/sys/modules/part/mapper/BomSearch3Mapper.java
  2. 132
      src/main/java/com/xujie/sys/modules/part/service/impl/BomSearch3ServiceImpl.java
  3. 110
      src/main/resources/mapper/part/BomSearch3Mapper.xml

4
src/main/java/com/xujie/sys/modules/part/mapper/BomSearch3Mapper.java

@ -93,4 +93,8 @@ public interface BomSearch3Mapper {
void saveRoutingHeaderFromTemp(@Param("bomId") Long bomId, @Param("createBy") String createBy); void saveRoutingHeaderFromTemp(@Param("bomId") Long bomId, @Param("createBy") String createBy);
void saveRoutingDetailFromTemp(@Param("bomId") Long bomId, @Param("createBy") String createBy); void saveRoutingDetailFromTemp(@Param("bomId") Long bomId, @Param("createBy") String createBy);
void updateBomDetailStatusToReleased(@Param("bomId") Long bomId, @Param("createBy") String createBy);
void updateRoutingDetailStatusToReleased(@Param("bomId") Long bomId, @Param("createBy") String createBy);
} }

132
src/main/java/com/xujie/sys/modules/part/service/impl/BomSearch3ServiceImpl.java

@ -611,6 +611,10 @@ public class BomSearch3ServiceImpl implements BomSearch3Service {
// 2. 新增BOM detail明细表 // 2. 新增BOM detail明细表
log.info("步骤2: 保存BOM detail"); log.info("步骤2: 保存BOM detail");
bomSearch3Mapper.saveBomDetailFromTemp(data.getBomId(), data.getCreateBy()); bomSearch3Mapper.saveBomDetailFromTemp(data.getBomId(), data.getCreateBy());
// 2.1 更新 Tentative 状态为 Released
log.info("步骤2.1: 更新BOM detail状态为Released");
bomSearch3Mapper.updateBomDetailStatusToReleased(data.getBomId(), data.getCreateBy());
// 3. 新增Routing header主表 调整顺序必须先保存Routing // 3. 新增Routing header主表 调整顺序必须先保存Routing
log.info("步骤3: 保存Routing header"); log.info("步骤3: 保存Routing header");
@ -619,6 +623,10 @@ public class BomSearch3ServiceImpl implements BomSearch3Service {
// 4. 新增Routing detail明细表 // 4. 新增Routing detail明细表
log.info("步骤4: 保存Routing detail"); log.info("步骤4: 保存Routing detail");
bomSearch3Mapper.saveRoutingDetailFromTemp(data.getBomId(), data.getCreateBy()); bomSearch3Mapper.saveRoutingDetailFromTemp(data.getBomId(), data.getCreateBy());
// 4.1 更新 Tentative 状态为 Released
log.info("步骤4.1: 更新Routing detail状态为Released");
bomSearch3Mapper.updateRoutingDetailStatusToReleased(data.getBomId(), data.getCreateBy());
// 5. 新增Routing component工艺路线 关键步骤生成新的operation_id // 5. 新增Routing component工艺路线 关键步骤生成新的operation_id
log.info("步骤5: 保存Routing component"); log.info("步骤5: 保存Routing component");
@ -631,7 +639,7 @@ public class BomSearch3ServiceImpl implements BomSearch3Service {
// 7. 新增BOM节点树到快速创建BOM表 // 7. 新增BOM节点树到快速创建BOM表
log.info("步骤7: 保存BOM节点树"); log.info("步骤7: 保存BOM节点树");
// data.getPartNo() = 原BOM的物料编码, bomTreeHeader.getPartNo() = 新BOM的物料编码 // data.getPartNo() = 原BOM的物料编码, bomTreeHeader.getPartNo() = 新BOM的物料编码
saveQuicklyBomInfoFromTemp(data.getSite(), data.getPartNo(), bomTreeHeader.getPartNo(), data.getCreateBy());
saveQuicklyBomInfoFromTemp(data.getBomId(), data.getSite(), data.getPartNo(), bomTreeHeader.getPartNo(), data.getCreateBy());
log.info("=== 完整BOM数据保存完成 ==="); log.info("=== 完整BOM数据保存完成 ===");
@ -669,65 +677,111 @@ public class BomSearch3ServiceImpl implements BomSearch3Service {
} }
/** /**
* 复制原BOM的节点树到新的mainPart下
* 从临时表保存BOM节点树到快速创建表支持多级节点变更
*
* <p><b>关键修复逻辑</b></p>
* <ul>
* <li> 错误方式从原BOM复制数据只修改根节点物料</li>
* <li> 正确方式从临时表读取所有节点包含所有层级的变更</li>
* <li> 使用 bomId 精确查询当前BOM的临时数据</li>
* <li> 按层级排序确保父子关系正确</li>
* </ul>
*
* @param bomId BOM ID必须用于精确查询临时表数据
* @param site 站点 * @param site 站点
* @param originalPartNo 原BOM物料编码被复制的
* @param newPartNo 新BOM物料编码复制目标
* @param originalPartNo 原BOM物料编码
* @param newPartNo 新BOM物料编码根节点
* @param createBy 创建人 * @param createBy 创建人
*/ */
private void saveQuicklyBomInfoFromTemp(String site, String originalPartNo, String newPartNo, String createBy) {
log.info("=== 开始复制BOM节点树到快速创建表 ===");
log.info("site: {}, 原BOM物料: {}, 新BOM物料: {}, 创建人: {}", site, originalPartNo, newPartNo, createBy);
private void saveQuicklyBomInfoFromTemp(Long bomId, String site, String originalPartNo, String newPartNo, String createBy) {
log.info("=== 开始从临时表保存BOM节点树到快速创建表 ===");
log.info("bomId: {}, site: {}, 原BOM物料: {}, 新BOM根节点物料: {}, 创建人: {}", bomId, site, originalPartNo, newPartNo, createBy);
// 1. 关键修复从临时表查询所有节点包含所有层级的变更后part_no
List<CopyTempBomTree> tempNodes = copyTempBomTreeMapper.selectList(
new LambdaQueryWrapper<CopyTempBomTree>()
.eq(CopyTempBomTree::getBomId, bomId)
.eq(CopyTempBomTree::getUsername, createBy)
.orderByAsc(CopyTempBomTree::getLevel)
.orderByAsc(CopyTempBomTree::getId)
);
if (tempNodes.isEmpty()) {
log.warn("临时表中没有BOM节点树数据,不执行保存。bomId: {}, createBy: {}", bomId, createBy);
return;
}
log.info("从临时表找到变更后的节点树,共{}个节点", tempNodes.size());
// 查询原BOM的节点树结构
// 2. 查询原BOM的节点树模板信息仅用于获取template_nonode_id等元数据
List<PlmQuicklyBomInfoEntity> originalNodes = bomSearch3Mapper.queryOriginalBomNodeTree(site, originalPartNo); List<PlmQuicklyBomInfoEntity> originalNodes = bomSearch3Mapper.queryOriginalBomNodeTree(site, originalPartNo);
if (originalNodes.isEmpty()) {
log.warn("原BOM没有节点树数据,不执行复制。originalPartNo: {}", originalPartNo);
log.warn("这可能是因为原BOM不是通过快速创建BOM功能创建的");
return;
// 3. 构建原节点的 (level -> originalNode) 映射用于匹配模板信息
Map<Integer, PlmQuicklyBomInfoEntity> levelToOriginalNodeMap = new HashMap<>();
for (int i = 0; i < originalNodes.size(); i++) {
levelToOriginalNodeMap.put(i, originalNodes.get(i));
} }
log.info("找到原BOM节点树,共{}个节点", originalNodes.size());
// 复制节点树修改mainPart和part_no根节点
// 4. 正确逻辑使用临时表的所有数据变更后的part_no+ 原模板的结构信息
List<PlmQuicklyBomInfoEntity> newNodes = new ArrayList<>(); List<PlmQuicklyBomInfoEntity> newNodes = new ArrayList<>();
for (PlmQuicklyBomInfoEntity originalNode : originalNodes) {
for (int i = 0; i < tempNodes.size(); i++) {
CopyTempBomTree tempNode = tempNodes.get(i);
PlmQuicklyBomInfoEntity newNode = new PlmQuicklyBomInfoEntity(); PlmQuicklyBomInfoEntity newNode = new PlmQuicklyBomInfoEntity();
// 复制所有字段保留原节点结构
newNode.setSite(originalNode.getSite());
newNode.setTemplateNo(originalNode.getTemplateNo());
newNode.setNodeId(originalNode.getNodeId());
newNode.setNodeName(originalNode.getNodeName());
newNode.setId(originalNode.getId());
newNode.setpId(originalNode.getpId());
newNode.setEngChgLevel(originalNode.getEngChgLevel());
newNode.setRoutingRevision(originalNode.getRoutingRevision());
newNode.setCreateBy(createBy);
// 修改mainPart为新物料编码
// 使用临时表的数据包含变更后的part_no
newNode.setPartNo(tempNode.getPartNo());
newNode.setSite(tempNode.getSite());
newNode.setEngChgLevel(String.valueOf(tempNode.getEngChgLevel()));
newNode.setRoutingRevision(String.valueOf(tempNode.getEngChgLevel()));
newNode.setMainPart(newPartNo); newNode.setMainPart(newPartNo);
newNode.setCreateBy(createBy);
// 如果是根节点p_id = '0'修改part_no为新物料编码
if ("0".equals(originalNode.getpId())) {
newNode.setPartNo(newPartNo);
log.info("复制根节点: id={}, 原partNo={} → 新partNo={}",
originalNode.getId(), originalNode.getPartNo(), newPartNo);
// 尝试从原节点获取模板信息
PlmQuicklyBomInfoEntity originalNode = levelToOriginalNodeMap.get(i);
if (originalNode != null) {
// 使用原模板的树形结构信息
newNode.setTemplateNo(originalNode.getTemplateNo());
newNode.setNodeId(originalNode.getNodeId());
newNode.setNodeName(originalNode.getNodeName());
newNode.setId(originalNode.getId());
newNode.setpId(originalNode.getpId());
log.info("节点[{}]: 层级={}, part_no={} (原={}, 已变更={}), 模板id={}",
i, tempNode.getLevel(), tempNode.getPartNo(),
originalNode.getPartNo(), tempNode.getChangeFlag(), originalNode.getId());
} else { } else {
// 子节点保持原来的part_no
newNode.setPartNo(originalNode.getPartNo());
log.debug("复制子节点: id={}, partNo={}", originalNode.getId(), originalNode.getPartNo());
// 没有原模板使用默认值
newNode.setNodeId(String.valueOf(tempNode.getLevel()));
newNode.setNodeName("Node-Level" + tempNode.getLevel());
newNode.setId(String.valueOf(tempNode.getLevel()));
newNode.setpId(tempNode.getParentId() == 0 ? "0" : String.valueOf(tempNode.getLevel() - 1));
log.info("节点[{}]: 层级={}, part_no={} (新增节点, 无原模板), 变更标识={}",
i, tempNode.getLevel(), tempNode.getPartNo(), tempNode.getChangeFlag());
} }
newNodes.add(newNode); newNodes.add(newNode);
} }
// 批量插入
// 5. 批量插入
if (!newNodes.isEmpty()) { if (!newNodes.isEmpty()) {
bomSearch3Mapper.batchInsertQuicklyBomInfo(newNodes); bomSearch3Mapper.batchInsertQuicklyBomInfo(newNodes);
log.info("=== BOM节点树复制完成!共复制{}个节点 ===", newNodes.size());
log.info("新BOM物料编码: {}, 可在quicklyCreateBom页面查看", newPartNo);
log.info("=== BOM节点树保存完成!共保存{}个节点 ===", newNodes.size());
log.info("新BOM根节点物料编码: {}, 可在quicklyCreateBom页面查看", newPartNo);
// 打印变更摘要
int changeCount = 0;
for (int i = 0; i < newNodes.size(); i++) {
PlmQuicklyBomInfoEntity newNode = newNodes.get(i);
PlmQuicklyBomInfoEntity originalNode = levelToOriginalNodeMap.get(i);
if (originalNode != null && !newNode.getPartNo().equals(originalNode.getPartNo())) {
log.info(" ✓ 节点[{}] 物料已变更: {} → {}",
i, originalNode.getPartNo(), newNode.getPartNo());
changeCount++;
}
}
log.info("✅ 变更统计: 共{}个节点发生物料变更", changeCount);
} }
} }

110
src/main/resources/mapper/part/BomSearch3Mapper.xml

@ -722,12 +722,12 @@
insert into plm_bom_detail insert into plm_bom_detail
(site, bu_no, part_no, eng_chg_level, bom_type, alternative_no, alternative_description, status, min_lot_qty, default_flag, note_text, create_date, create_by) (site, bu_no, part_no, eng_chg_level, bom_type, alternative_no, alternative_description, status, min_lot_qty, default_flag, note_text, create_date, create_by)
select distinct select distinct
site,
bu_no,
part_no,
eng_chg_level,
bom_type,
alternative_no,
t.site,
t.bu_no,
t.part_no,
t.eng_chg_level,
t.bom_type,
t.alternative_no,
'*' as alternative_description, '*' as alternative_description,
'Released' as status, 'Released' as status,
1 as min_lot_qty, 1 as min_lot_qty,
@ -735,9 +735,37 @@
null as note_text, null as note_text,
getDate() as create_date, getDate() as create_date,
#{createBy} as create_by #{createBy} as create_by
from plm_copy_temp_bom_tree
where bom_id = #{bomId} and username = #{createBy} and change_flag = 'Y' and level = 0
from plm_copy_temp_bom_tree t
where t.bom_id = #{bomId} and t.username = #{createBy} and t.change_flag = 'Y'
and not exists (
select 1 from plm_bom_detail d
where d.site = t.site
and d.bu_no = t.bu_no
and d.part_no = t.part_no
and d.eng_chg_level = t.eng_chg_level
and d.bom_type = t.bom_type
and d.alternative_no = t.alternative_no
)
</insert> </insert>
<!-- 更新 Tentative 状态为 Released -->
<update id="updateBomDetailStatusToReleased">
update plm_bom_detail
set status = 'Released'
where status = 'Tentative'
and exists (
select 1 from plm_copy_temp_bom_tree t
where t.bom_id = #{bomId}
and t.username = #{createBy}
and t.change_flag = 'Y'
and t.site = plm_bom_detail.site
and t.bu_no = plm_bom_detail.bu_no
and t.part_no = plm_bom_detail.part_no
and t.eng_chg_level = plm_bom_detail.eng_chg_level
and t.bom_type = plm_bom_detail.bom_type
and t.alternative_no = plm_bom_detail.alternative_no
)
</update>
<!-- 删除已存在的Routing header(用于复制前清理)--> <!-- 删除已存在的Routing header(用于复制前清理)-->
<delete id="deleteRoutingHeaderForCopy"> <delete id="deleteRoutingHeaderForCopy">
@ -754,17 +782,25 @@
insert into plm_routing_header insert into plm_routing_header
(site, bu_no, part_no, routing_revision, routing_type, note_text, phase_in_date, create_date, create_by) (site, bu_no, part_no, routing_revision, routing_type, note_text, phase_in_date, create_date, create_by)
select distinct select distinct
site,
bu_no,
part_no,
eng_chg_level as routing_revision,
bom_type as routing_type,
t.site,
t.bu_no,
t.part_no,
t.eng_chg_level as routing_revision,
t.bom_type as routing_type,
null as note_text, null as note_text,
getDate() as phase_in_date, getDate() as phase_in_date,
getDate() as create_date, getDate() as create_date,
#{createBy} as create_by #{createBy} as create_by
from plm_copy_temp_bom_tree
where bom_id = #{bomId} and username = #{createBy} and change_flag = 'Y' and level = 0
from plm_copy_temp_bom_tree t
where t.bom_id = #{bomId} and t.username = #{createBy} and t.change_flag = 'Y'
and not exists (
select 1 from plm_routing_header h
where h.site = t.site
and h.bu_no = t.bu_no
and h.part_no = t.part_no
and h.routing_revision = t.eng_chg_level
and h.routing_type = t.bom_type
)
</insert> </insert>
<!-- 删除已存在的Routing detail(用于复制前清理)--> <!-- 删除已存在的Routing detail(用于复制前清理)-->
@ -782,18 +818,46 @@
insert into plm_routing_detail insert into plm_routing_detail
(site, bu_no, part_no, routing_revision, routing_type, alternative_no, status, create_date, create_by) (site, bu_no, part_no, routing_revision, routing_type, alternative_no, status, create_date, create_by)
select distinct select distinct
site,
bu_no,
part_no,
eng_chg_level as routing_revision,
bom_type as routing_type,
alternative_no,
t.site,
t.bu_no,
t.part_no,
t.eng_chg_level as routing_revision,
t.bom_type as routing_type,
t.alternative_no,
'Released' as status, 'Released' as status,
getDate() as create_date, getDate() as create_date,
#{createBy} as create_by #{createBy} as create_by
from plm_copy_temp_bom_tree
where bom_id = #{bomId} and username = #{createBy} and change_flag = 'Y' and level = 0
from plm_copy_temp_bom_tree t
where t.bom_id = #{bomId} and t.username = #{createBy} and t.change_flag = 'Y'
and not exists (
select 1 from plm_routing_detail d
where d.site = t.site
and d.bu_no = t.bu_no
and d.part_no = t.part_no
and d.routing_revision = t.eng_chg_level
and d.routing_type = t.bom_type
and d.alternative_no = t.alternative_no
)
</insert> </insert>
<!-- 更新 Tentative 状态为 Released -->
<update id="updateRoutingDetailStatusToReleased">
update plm_routing_detail
set status = 'Released'
where status = 'Tentative'
and exists (
select 1 from plm_copy_temp_bom_tree t
where t.bom_id = #{bomId}
and t.username = #{createBy}
and t.change_flag = 'Y'
and t.site = plm_routing_detail.site
and t.bu_no = plm_routing_detail.bu_no
and t.part_no = plm_routing_detail.part_no
and t.eng_chg_level = plm_routing_detail.routing_revision
and t.bom_type = plm_routing_detail.routing_type
and t.alternative_no = plm_routing_detail.alternative_no
)
</update>
<!-- 查询原BOM的节点树结构(用于复制)--> <!-- 查询原BOM的节点树结构(用于复制)-->
<select id="queryOriginalBomNodeTree" resultType="com.xujie.sys.modules.part.entity.PlmQuicklyBomInfoEntity"> <select id="queryOriginalBomNodeTree" resultType="com.xujie.sys.modules.part.entity.PlmQuicklyBomInfoEntity">

Loading…
Cancel
Save