diff --git a/src/main/java/com/spring/modules/quote/service/impl/QuoteDetailBomTreeServiceImpl.java b/src/main/java/com/spring/modules/quote/service/impl/QuoteDetailBomTreeServiceImpl.java index af34c25e..a20585b1 100644 --- a/src/main/java/com/spring/modules/quote/service/impl/QuoteDetailBomTreeServiceImpl.java +++ b/src/main/java/com/spring/modules/quote/service/impl/QuoteDetailBomTreeServiceImpl.java @@ -50,6 +50,9 @@ public class QuoteDetailBomTreeServiceImpl extends ServiceImpl ifsConFactory = resolveIfsConFactory(); - Long newParentId = bomTree.getParentId(); + newParentId = bomTree.getParentId(); Integer newLevel = Optional.ofNullable(tree.getLevel()).orElse(0); - BomNodeData nodeData = collectBomData(detail, newParentId, newLevel, ifsConFactory); - - // 数据已就绪,开始执行 DML(事务持有锁的时间仅限于纯 DB 写入阶段) - List ids = getAllChildIds(detail, bomTree.getId()); - ids.add(bomTree.getId()); - lambdaUpdate().in(QuoteDetailBomTree::getId, ids).remove(); - quoteDetailBomService.lambdaUpdate().in(QuoteDetailBom::getTreeId, ids).remove(); - quoteDetailRoutingService.lambdaUpdate().in(QuoteDetailRouting::getTreeId, ids).remove(); - - long bomId = 0; - if (nodeData != null) { - bomId = doSaveBomDataRecursive(nodeData, detail, newParentId); - } - quoteDetailBomService.lambdaUpdate() - .set(QuoteDetailBom::getBomId, bomId) - .eq(QuoteDetailBom::getBomId, bomTree.getId()) - .update(); + nodeData = collectBomData(detail, newParentId, newLevel, ifsConFactory); } else { detail = new QuoteDetail(); detail.setQuoteId(tree.getQuoteId()); @@ -383,22 +372,53 @@ public class QuoteDetailBomTreeServiceImpl extends ServiceImpl ifsConFactory = resolveIfsConFactory(); - BomNodeData nodeData = collectBomData(detail, 0L, 0, ifsConFactory); - if (nodeData != null) { - doSaveBomDataRecursive(nodeData, detail, 0L); - } + nodeData = collectBomData(detail, 0L, 0, ifsConFactory); } - if (Boolean.TRUE.equals(tree.getRecalculateCost()) && detail != null) { - log.info("[BOM_SWITCH] Recalculate cost is enabled, executing cost calculation for QuoteDetailId: {}", detail.getId()); - try { - quoteDetailService.queryQuoteDetailCost(detail); - log.info("[BOM_SWITCH] Cost calculation completed successfully for QuoteDetailId: {}", detail.getId()); - } catch (Exception e) { - log.error("[BOM_SWITCH] Cost calculation failed for QuoteDetailId: {}, Error: {}", detail.getId(), e.getMessage(), e); - throw new RuntimeException("成本计算失败: " + e.getMessage()); + // 数据已就绪,开启短事务执行纯 DB 写入和成本计算 + final QuoteDetail finalDetail = detail; + final BomNodeData finalNodeData = nodeData; + final Long finalNewParentId = newParentId; + final QuoteDetailBomTree finalBomTree = bomTree; + + transactionTemplate.execute(status -> { + // 1. 执行 BOM 结构切换的 DB 写入 + if (Objects.nonNull(tree.getId())) { + List ids = getAllChildIds(finalDetail, finalBomTree.getId()); + ids.add(finalBomTree.getId()); + lambdaUpdate().in(QuoteDetailBomTree::getId, ids).remove(); + quoteDetailBomService.lambdaUpdate().in(QuoteDetailBom::getTreeId, ids).remove(); + quoteDetailRoutingService.lambdaUpdate().in(QuoteDetailRouting::getTreeId, ids).remove(); + + long bomId = 0; + if (finalNodeData != null) { + bomId = doSaveBomDataRecursive(finalNodeData, finalDetail, finalNewParentId); + } + quoteDetailBomService.lambdaUpdate() + .set(QuoteDetailBom::getBomId, bomId) + .eq(QuoteDetailBom::getBomId, finalBomTree.getId()) + .update(); + } else { + if (finalNodeData != null) { + doSaveBomDataRecursive(finalNodeData, finalDetail, 0L); + } } - } + + // 2. 重新计算成本 (如果开启) + // 将其放在同一个事务中,如果计算失败,BOM 切换也会一并回滚,保证数据一致性 + if (Boolean.TRUE.equals(tree.getRecalculateCost()) && finalDetail != null) { + log.info("[BOM_SWITCH] Recalculate cost is enabled, executing cost calculation for QuoteDetailId: {}", finalDetail.getId()); + try { + quoteDetailService.queryQuoteDetailCost(finalDetail); + log.info("[BOM_SWITCH] Cost calculation completed successfully for QuoteDetailId: {}", finalDetail.getId()); + } catch (Exception e) { + log.error("[BOM_SWITCH] Cost calculation failed for QuoteDetailId: {}, Error: {}", finalDetail.getId(), e.getMessage(), e); + // 抛出 RuntimeException 以触发 transactionTemplate 回滚 + throw new RuntimeException("成本计算失败: " + e.getMessage()); + } + } + return null; + }); } // =========================================================================