|
|
|
@ -94,6 +94,18 @@ public class QuoteDetailBomTreeServiceImpl extends ServiceImpl<QuoteDetailBomTre |
|
|
|
List<BomNodeData> children = new ArrayList<>(); |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 物料成本查询结果: |
|
|
|
* SUCCESS - 首次(池化连接)查询成功; |
|
|
|
* SUCCESS_AFTER_RECONNECT - 首次失败,重试时切换新连接后成功; |
|
|
|
* FAILED - 重试后仍失败(按现有逻辑置 0,不阻断后续流程)。 |
|
|
|
*/ |
|
|
|
private enum CostQueryResult { |
|
|
|
SUCCESS, |
|
|
|
SUCCESS_AFTER_RECONNECT, |
|
|
|
FAILED |
|
|
|
} |
|
|
|
|
|
|
|
// ========================================================================= |
|
|
|
// 公开方法:两阶段执行 |
|
|
|
// ========================================================================= |
|
|
|
@ -277,15 +289,27 @@ public class QuoteDetailBomTreeServiceImpl extends ServiceImpl<QuoteDetailBomTre |
|
|
|
List<CompletableFuture<Void>> futures = needCostComponents.stream() |
|
|
|
.map(component -> CompletableFuture.runAsync(() -> { |
|
|
|
Server conn = null; |
|
|
|
CostQueryResult costQueryResult = CostQueryResult.FAILED; |
|
|
|
try { |
|
|
|
conn = connectionPool.take(); |
|
|
|
getFinalPartCost(component, conn, costCache); |
|
|
|
costQueryResult = getFinalPartCost(component, conn, ifsConFactory, costCache); |
|
|
|
} catch (InterruptedException e) { |
|
|
|
Thread.currentThread().interrupt(); |
|
|
|
log.warn("[BOM_PROCESS] 并行成本查询线程被中断 - PartNo: {}", component.getComponentPart()); |
|
|
|
} finally { |
|
|
|
if (conn != null) { |
|
|
|
connectionPool.offer(conn); |
|
|
|
if (costQueryResult == CostQueryResult.SUCCESS) { |
|
|
|
connectionPool.offer(conn); |
|
|
|
} else { |
|
|
|
// 连接在本次查询中出现异常迹象(或最终失败),替换为新连接,避免“坏连接”反复影响后续组件 |
|
|
|
try { |
|
|
|
connectionPool.offer(ifsConFactory.get()); |
|
|
|
} catch (Exception ex) { |
|
|
|
log.warn("[BOM_PROCESS] IFS连接替换失败,回退复用原连接 - PartNo: {}, Error: {}", |
|
|
|
component.getComponentPart(), ex.getMessage()); |
|
|
|
connectionPool.offer(conn); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
}, IFS_COST_EXECUTOR)) |
|
|
|
@ -825,7 +849,10 @@ public class QuoteDetailBomTreeServiceImpl extends ServiceImpl<QuoteDetailBomTre |
|
|
|
/** |
|
|
|
* 获取物料成本(带重试机制) |
|
|
|
*/ |
|
|
|
private void getFinalPartCost(QuoteDetailBom component, Server ifsServer, Map<String, BigDecimal> costCache) { |
|
|
|
private CostQueryResult getFinalPartCost(QuoteDetailBom component, |
|
|
|
Server ifsServer, |
|
|
|
Supplier<Server> ifsConFactory, |
|
|
|
Map<String, BigDecimal> costCache) { |
|
|
|
final int MAX_RETRY_COUNT = 2; |
|
|
|
final long RETRY_INTERVAL_MS = 500; |
|
|
|
|
|
|
|
@ -839,7 +866,7 @@ public class QuoteDetailBomTreeServiceImpl extends ServiceImpl<QuoteDetailBomTre |
|
|
|
component.setActualPrice(cachedCost); |
|
|
|
component.setQuotePrice(cachedCost); |
|
|
|
log.info("[COST_QUERY] 从缓存读取成功 - PartNo: {}, Site: {}, UnitCost: {}", partNo, site, cachedCost); |
|
|
|
return; |
|
|
|
return CostQueryResult.SUCCESS; |
|
|
|
} |
|
|
|
|
|
|
|
log.info("[COST_QUERY] 开始查询物料成本 - PartNo: {}, Site: {}", partNo, site); |
|
|
|
@ -855,8 +882,9 @@ public class QuoteDetailBomTreeServiceImpl extends ServiceImpl<QuoteDetailBomTre |
|
|
|
for (int retryCount = 1; retryCount <= MAX_RETRY_COUNT; retryCount++) { |
|
|
|
try { |
|
|
|
log.debug("[COST_QUERY] 第 {} 次尝试查询 - PartNo: {}", retryCount, partNo); |
|
|
|
|
|
|
|
map = baseSearchBean.getInventoryValueByPartNo(ifsServer, part); |
|
|
|
// 首次查询使用连接池分配的连接;重试使用新连接,提升偶发连接态异常下的成功率 |
|
|
|
Server currentIfsServer = retryCount == 1 ? ifsServer : ifsConFactory.get(); |
|
|
|
map = baseSearchBean.getInventoryValueByPartNo(currentIfsServer, part); |
|
|
|
|
|
|
|
if (Objects.equals(map.get("resultCode"), "200")) { |
|
|
|
InventoryPartUnitCostSumVo unitCostSumVo = JSONObject.parseObject(map.get("obj"), InventoryPartUnitCostSumVo.class); |
|
|
|
@ -869,7 +897,7 @@ public class QuoteDetailBomTreeServiceImpl extends ServiceImpl<QuoteDetailBomTre |
|
|
|
log.info("[COST_QUERY] 查询成功 - PartNo: {}, Site: {}, UnitCost: {}, 尝试次数: {}", |
|
|
|
partNo, site, unitCost, retryCount); |
|
|
|
success = true; |
|
|
|
break; |
|
|
|
return retryCount == 1 ? CostQueryResult.SUCCESS : CostQueryResult.SUCCESS_AFTER_RECONNECT; |
|
|
|
} else { |
|
|
|
lastErrorMsg = map.get("resultMsg"); |
|
|
|
log.warn("[COST_QUERY] 第 {} 次查询失败 - PartNo: {}, Site: {}, ResultCode: {}, ErrorMsg: {}", |
|
|
|
@ -923,6 +951,7 @@ public class QuoteDetailBomTreeServiceImpl extends ServiceImpl<QuoteDetailBomTre |
|
|
|
component.setQuotePrice(BigDecimal.ZERO); |
|
|
|
costCache.put(cacheKey, BigDecimal.ZERO); |
|
|
|
} |
|
|
|
return CostQueryResult.FAILED; |
|
|
|
} |
|
|
|
|
|
|
|
private QuoteDetailBomTree isComponentBom(QuoteDetail component) { |
|
|
|
|