|
|
|
@ -123,15 +123,28 @@ public class QuoteDetailBomTreeServiceImpl extends ServiceImpl<QuoteDetailBomTre |
|
|
|
// QuoteDetailBom purchase = getPurchaseComponentPart(detail, bom, componentParts.size()+1); |
|
|
|
// componentParts.add(purchase); |
|
|
|
// } |
|
|
|
log.info("BOM子物料:{}",componentParts); |
|
|
|
|
|
|
|
// 统计日志:记录子物料数量和需要查询成本的数量 |
|
|
|
int totalComponents = componentParts.size(); |
|
|
|
long needCostQueryCount = componentParts.stream().filter(c -> "Y".equals(c.getStatus())).count(); |
|
|
|
log.info("[BOM_PROCESS] 开始处理子物料 - 父物料: {}, 层级: {}, 子物料总数: {}, 需查询成本数: {}", |
|
|
|
bom.getPartNo(), level, totalComponents, needCostQueryCount); |
|
|
|
|
|
|
|
// 3、查询子物料是否存在BOM信息 |
|
|
|
int processedCount = 0; |
|
|
|
int costQuerySuccessCount = 0; |
|
|
|
int costQueryFailCount = 0; |
|
|
|
|
|
|
|
for (QuoteDetailBom component : componentParts) { |
|
|
|
processedCount++; |
|
|
|
component.setCreateBy(detail.getCreateBy()); |
|
|
|
component.setCreateDate(detail.getCreateDate()); |
|
|
|
QuoteDetail quoteDetail = createQuoteDetail(detail, component); |
|
|
|
// 物料是半成品 |
|
|
|
QuoteDetailBomTree bomTree = isComponentBom(quoteDetail); |
|
|
|
if (Objects.nonNull(bomTree) && "Y".equals(component.getBomFlag())) { |
|
|
|
log.debug("[BOM_PROCESS] 递归处理半成品 - PartNo: {}, 当前进度: {}/{}", |
|
|
|
component.getComponentPart(), processedCount, totalComponents); |
|
|
|
long id = initQuoteDetailBomTree(quoteDetail, bom.getId(), level + 1); |
|
|
|
// 如果是BOM 为子料绑定是哪个Bom |
|
|
|
component.setBomId(id); |
|
|
|
@ -143,13 +156,25 @@ public class QuoteDetailBomTreeServiceImpl extends ServiceImpl<QuoteDetailBomTre |
|
|
|
component.setBomFlag("N"); |
|
|
|
} |
|
|
|
if ("Y".equals(component.getStatus())) { |
|
|
|
//getFinalPartCost(component, server); // 获取最终的物料的成本 使用当前用的 |
|
|
|
// 记录查询前的状态,用于判断是否成功 |
|
|
|
BigDecimal beforePrice = component.getUnitPrice(); |
|
|
|
getFinalPartCost(component, ifsCon); |
|
|
|
// 判断成本查询是否成功(成功后价格不为0或有变化) |
|
|
|
if (component.getUnitPrice() != null && component.getUnitPrice().compareTo(BigDecimal.ZERO) > 0) { |
|
|
|
costQuerySuccessCount++; |
|
|
|
} else { |
|
|
|
costQueryFailCount++; |
|
|
|
} |
|
|
|
} |
|
|
|
//新增子物料信息 |
|
|
|
component.setTreeId(bom.getId()); |
|
|
|
quoteDetailBomService.save(component); |
|
|
|
} |
|
|
|
|
|
|
|
// 统计日志:记录处理结果 |
|
|
|
log.info("[BOM_PROCESS] 子物料处理完成 - 父物料: {}, 层级: {}, 处理总数: {}, 成本查询成功: {}, 成本查询失败: {}", |
|
|
|
bom.getPartNo(), level, processedCount, costQuerySuccessCount, costQueryFailCount); |
|
|
|
|
|
|
|
return bom.getId(); |
|
|
|
} |
|
|
|
|
|
|
|
@ -203,35 +228,99 @@ public class QuoteDetailBomTreeServiceImpl extends ServiceImpl<QuoteDetailBomTre |
|
|
|
return purchase; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 获取物料成本(带重试机制) |
|
|
|
* @param component 物料组件 |
|
|
|
* @param ifsServer IFS服务器连接 |
|
|
|
*/ |
|
|
|
private void getFinalPartCost(QuoteDetailBom component, Server ifsServer) { |
|
|
|
// 最大重试次数 |
|
|
|
final int MAX_RETRY_COUNT = 3; |
|
|
|
// 重试间隔(毫秒) |
|
|
|
final long RETRY_INTERVAL_MS = 1000; |
|
|
|
|
|
|
|
String partNo = component.getComponentPart(); |
|
|
|
String site = component.getSite(); |
|
|
|
|
|
|
|
log.info("[COST_QUERY] 开始查询物料成本 - PartNo: {}, Site: {}", partNo, site); |
|
|
|
|
|
|
|
PartInformationEntity part = new PartInformationEntity(); |
|
|
|
part.setSite(component.getSite()); |
|
|
|
part.setPartNo(component.getComponentPart()); |
|
|
|
Map<String, String> map = baseSearchBean.getInventoryValueByPartNo(ifsServer, part); |
|
|
|
part.setSite(site); |
|
|
|
part.setPartNo(partNo); |
|
|
|
|
|
|
|
Map<String, String> map = null; |
|
|
|
boolean success = false; |
|
|
|
String lastErrorMsg = null; |
|
|
|
|
|
|
|
// 重试机制 |
|
|
|
for (int retryCount = 1; retryCount <= MAX_RETRY_COUNT; retryCount++) { |
|
|
|
try { |
|
|
|
log.debug("[COST_QUERY] 第 {} 次尝试查询 - PartNo: {}", retryCount, partNo); |
|
|
|
|
|
|
|
map = baseSearchBean.getInventoryValueByPartNo(ifsServer, part); |
|
|
|
|
|
|
|
if (Objects.equals(map.get("resultCode"), "200")) { |
|
|
|
// 返回成功 将成本 |
|
|
|
// 返回成功 |
|
|
|
InventoryPartUnitCostSumVo unitCostSumVo = JSONObject.parseObject(map.get("obj"), InventoryPartUnitCostSumVo.class); |
|
|
|
try { |
|
|
|
BigDecimal unitCost = new BigDecimal(unitCostSumVo.getInventoryValue()); |
|
|
|
component.setUnitPrice(unitCost); |
|
|
|
component.setActualPrice(unitCost); |
|
|
|
component.setQuotePrice(unitCost); |
|
|
|
|
|
|
|
log.info("[COST_QUERY] 查询成功 - PartNo: {}, Site: {}, UnitCost: {}, 尝试次数: {}", |
|
|
|
partNo, site, unitCost, retryCount); |
|
|
|
success = true; |
|
|
|
break; |
|
|
|
} else { |
|
|
|
// 接口返回错误 |
|
|
|
lastErrorMsg = map.get("resultMsg"); |
|
|
|
log.warn("[COST_QUERY] 第 {} 次查询失败 - PartNo: {}, Site: {}, ResultCode: {}, ErrorMsg: {}", |
|
|
|
retryCount, partNo, site, map.get("resultCode"), lastErrorMsg); |
|
|
|
|
|
|
|
// 账号密码错误不重试,直接抛异常 |
|
|
|
if (lastErrorMsg != null && lastErrorMsg.contains("You have entered an invalid username and/or password")) { |
|
|
|
log.error("[COST_QUERY] IFS账号密码错误,停止重试 - PartNo: {}", partNo); |
|
|
|
throw new RuntimeException(lastErrorMsg); |
|
|
|
} |
|
|
|
|
|
|
|
// 如果不是最后一次重试,等待后继续 |
|
|
|
if (retryCount < MAX_RETRY_COUNT) { |
|
|
|
log.info("[COST_QUERY] 等待 {}ms 后进行第 {} 次重试 - PartNo: {}", |
|
|
|
RETRY_INTERVAL_MS, retryCount + 1, partNo); |
|
|
|
Thread.sleep(RETRY_INTERVAL_MS); |
|
|
|
} |
|
|
|
} |
|
|
|
} catch (InterruptedException e) { |
|
|
|
Thread.currentThread().interrupt(); |
|
|
|
log.error("[COST_QUERY] 重试等待被中断 - PartNo: {}, Site: {}", partNo, site); |
|
|
|
break; |
|
|
|
} catch (RuntimeException e) { |
|
|
|
// 重新抛出运行时异常(如账号密码错误) |
|
|
|
throw e; |
|
|
|
} catch (Exception e) { |
|
|
|
component.setUnitPrice(BigDecimal.ZERO); |
|
|
|
component.setActualPrice(BigDecimal.ZERO); |
|
|
|
component.setQuotePrice(BigDecimal.ZERO); |
|
|
|
log.error("PartNo:{},Site:{},成本信息异常:{}", component.getPartNo(), component.getSite(),e.getMessage()); |
|
|
|
lastErrorMsg = e.getMessage(); |
|
|
|
log.warn("[COST_QUERY] 第 {} 次查询异常 - PartNo: {}, Site: {}, Exception: {}", |
|
|
|
retryCount, partNo, site, e.getMessage()); |
|
|
|
|
|
|
|
// 如果不是最后一次重试,等待后继续 |
|
|
|
if (retryCount < MAX_RETRY_COUNT) { |
|
|
|
try { |
|
|
|
Thread.sleep(RETRY_INTERVAL_MS); |
|
|
|
} catch (InterruptedException ie) { |
|
|
|
Thread.currentThread().interrupt(); |
|
|
|
break; |
|
|
|
} |
|
|
|
}else { |
|
|
|
// 接口返回异常,处理成本数据为0 |
|
|
|
log.error("接口返回400"); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 所有重试都失败,设置成本为0 |
|
|
|
if (!success) { |
|
|
|
log.error("[COST_QUERY] 查询失败(已重试 {} 次)- PartNo: {}, Site: {}, 最后错误: {}", |
|
|
|
MAX_RETRY_COUNT, partNo, site, lastErrorMsg); |
|
|
|
component.setUnitPrice(BigDecimal.ZERO); |
|
|
|
component.setActualPrice(BigDecimal.ZERO); |
|
|
|
component.setQuotePrice(BigDecimal.ZERO); |
|
|
|
String resultMsg = map.get("resultMsg"); |
|
|
|
if (resultMsg.contains("You have entered an invalid username and/or password")){ |
|
|
|
throw new RuntimeException(resultMsg); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|