diff --git a/mhd_wms/src/main/java/com/mhd/wms/application/service/statementStatistics/StatementStatisticsService.java b/mhd_wms/src/main/java/com/mhd/wms/application/service/statementStatistics/StatementStatisticsService.java index b3ca3ae4f..154818684 100644 --- a/mhd_wms/src/main/java/com/mhd/wms/application/service/statementStatistics/StatementStatisticsService.java +++ b/mhd_wms/src/main/java/com/mhd/wms/application/service/statementStatistics/StatementStatisticsService.java @@ -85,7 +85,7 @@ public class StatementStatisticsService { } } List immediateInventoryPOS = materialInventoryDomainService.queryImmediateInventoryList(immediateInventoryDO); -// 性能优化:批量获取批次属性,避免N+1查询问题 + // 性能优化:批量获取批次属性,避免N+1查询问题 // 1. 收集所有唯一的materialBaseInfoId Set materialBaseInfoIdSet = immediateInventoryPOS.stream() .map(ImmediateInventoryPO::getMaterialBaseInfoId) diff --git a/mhd_wms/src/main/java/com/mhd/wms/domain/stockOutOrder/service/StockOutOrderDomainService.java b/mhd_wms/src/main/java/com/mhd/wms/domain/stockOutOrder/service/StockOutOrderDomainService.java index b4b3bd9d1..737f48553 100644 --- a/mhd_wms/src/main/java/com/mhd/wms/domain/stockOutOrder/service/StockOutOrderDomainService.java +++ b/mhd_wms/src/main/java/com/mhd/wms/domain/stockOutOrder/service/StockOutOrderDomainService.java @@ -13,6 +13,7 @@ import com.mhd.common.core.utils.bean.BeanUtils; import com.mhd.common.core.web.domain.AjaxResult; import com.mhd.common.security.utils.SecurityUtils; import com.mhd.system.api.SystemServiceFeign; +import com.mhd.system.api.domain.BatchDetailFeignPO; import com.mhd.system.api.domain.PackDetailFeignPO; import com.mhd.system.api.model.LoginUser; import com.mhd.wms.domain.deliveTask.repository.facade.IDeliveTaskService; @@ -27,6 +28,9 @@ import com.mhd.wms.domain.materialBaseInfo.repository.facade.IMaterialBaseInfoSe import com.mhd.wms.domain.materialBaseInfo.repository.mapper.MaterialBaseInfoMapper; import com.mhd.wms.domain.materialBaseInfo.repository.po.MaterialBaseInfoPO; import com.mhd.wms.domain.materialInventory.repository.facade.IMaterialInventoryService; +import com.mhd.wms.domain.materialInventory.repository.mapper.MaterialInventoryMapper; +import com.mhd.wms.domain.materialInventory.repository.po.MaterialInventoryPO; +import com.mhd.wms.domain.materialMoreDetail.repository.po.MaterialMoreDetailPO; import com.mhd.wms.domain.materialWarehouseControl.repository.facade.IMaterialWarehouseControlService; import com.mhd.wms.domain.materialWarehouseControl.repository.po.MaterialWarehouseControlPO; import com.mhd.wms.domain.mobilizeManage.entity.MobilizeManage; @@ -48,6 +52,7 @@ import com.mhd.wms.domain.pickingOrder.service.PickingOrderDomainService; import com.mhd.wms.domain.pushSyncBms.PushSyncBmsDomainService; import com.mhd.wms.domain.replenishmentNotice.repository.todo.ReplenishmentNoticeDO; import com.mhd.wms.domain.replenishmentNotice.service.ReplenishmentNoticeDomainService; +import com.mhd.wms.domain.statementStatistics.po.ImmediateInventoryPO; import com.mhd.wms.domain.stockInOrder.repository.todo.StockInOrderDO; import com.mhd.wms.domain.stockInOrder.service.StockInOrderDomainService; import com.mhd.wms.domain.stockOutOrder.entity.StockOutOrder; @@ -61,7 +66,9 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; +import java.lang.reflect.Field; import java.math.BigDecimal; +import java.text.SimpleDateFormat; import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; @@ -112,7 +119,8 @@ public class StockOutOrderDomainService { private PushSyncBmsDomainService pushSyncBmsDomainService; @Autowired private MaterialBaseInfoMapper materialBaseInfoMapper; - + @Autowired + private MaterialInventoryMapper materialInventoryMapper; /** * 分页查询出库单列表 @@ -703,9 +711,266 @@ public class StockOutOrderDomainService { outMaterialCheckPO.setAlreadyNumber(alreadyNumber); outMaterialCheckPO.setNotHaveNumber(wantNumber.subtract(alreadyNumber)); stockOutOrderPO.setOutMaterialCheckPO(outMaterialCheckPO); + + List materialDetailList = stockOutOrderPO.getMaterialDetailList(); + // 性能优化:批量获取批次属性,避免N+1查询问题 + // 1. 收集所有唯一的materialBaseInfoId + Set materialBaseInfoIdSet = materialDetailList.stream() + .map(OutMaterialDetailPO::getMaterialBaseInfoId) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + + // 2. 批量查询批次属性(使用Map缓存,避免重复查询相同的materialBaseInfoId) + Map> batchDetailMap = new HashMap<>(); + for (Long materialBaseInfoId : materialBaseInfoIdSet) { + try { + AjaxResult ajaxResult = systemServiceFeign.getBatchDetailListByMaterialBaseInfoId(materialBaseInfoId); + if (ajaxResult != null && "200".equals(String.valueOf(ajaxResult.get("code")))) { + List batchDetailFeignPOList = JSON.parseArray( + JSONObject.toJSONString(ajaxResult.get("data")), BatchDetailFeignPO.class); + if (!CollectionUtils.isEmpty(batchDetailFeignPOList)) { + // 过滤isDisplay=1的属性 + List filteredList = batchDetailFeignPOList.stream() + .filter(batchDetail -> batchDetail.getIsDisplay() != null && batchDetail.getIsDisplay() == 1) + .collect(Collectors.toList()); + batchDetailMap.put(materialBaseInfoId, filteredList); + } + } + } catch (Exception e) { + log.warn("获取物料批次属性失败,物料基础信息ID:{},错误:{}", materialBaseInfoId, e.getMessage()); + } + } + + // 3. 为每个物料明细设置更多属性(从库存表中获取值) + setMaterialMoreDetailListFromInventory(materialDetailList, batchDetailMap); + stockOutOrderPO.setMaterialDetailList(materialDetailList); return stockOutOrderPO; } + + /** + * 为物料库存设置更多属性(从库存表中获取值) + * @param materialDetailList 物料库存列表 + * @param batchDetailMap 批次属性Map + */ + private void setMaterialMoreDetailListFromInventory(List materialDetailList, + Map> batchDetailMap) { + if (CollectionUtils.isEmpty(materialDetailList)) { + return; + } + + materialDetailList.forEach(materialInventoryPO -> { + Long materialBaseInfoId = materialInventoryPO.getMaterialBaseInfoId(); + Long materialInventoryId = materialInventoryPO.getMaterialInventoryId(); + + // 创建新的materialMoreDetailList,只包含isDisplay=1的属性 + List materialMoreDetailList = new ArrayList<>(); + + // 从库存表获取数据(包含批次属性字段) + MaterialInventoryPO inventoryPO = null; + if (materialInventoryId != null) { + inventoryPO = materialInventoryMapper.selectByIdWithBatchAttributes(materialInventoryId); + } + + if (materialBaseInfoId != null && batchDetailMap.containsKey(materialBaseInfoId)) { + List batchDetailFeignPOList = batchDetailMap.get(materialBaseInfoId); + // 用于跟踪已添加到列表中的batchDetailId + Set addedBatchDetailIds = new HashSet<>(); + + for (BatchDetailFeignPO batchDetailFeignPO : batchDetailFeignPOList) { + MaterialMoreDetailPO materialMoreDetailPO = new MaterialMoreDetailPO(); + materialMoreDetailPO.setBatchId(batchDetailFeignPO.getBatchId()); + materialMoreDetailPO.setBatchDetailId(batchDetailFeignPO.getBatchDetailId()); + materialMoreDetailPO.setBatchLabels(batchDetailFeignPO.getBatchLabels()); + materialMoreDetailPO.setInputControl(batchDetailFeignPO.getInputControl()); + materialMoreDetailPO.setAttributeFormat(batchDetailFeignPO.getAttributeFormat()); + materialMoreDetailPO.setAttributeOption(batchDetailFeignPO.getAttributeOption()); + materialMoreDetailPO.setRemark(batchDetailFeignPO.getRemark()); + + // 确保添加到列表中(使用batchDetailId来避免重复) + Long batchDetailId = batchDetailFeignPO.getBatchDetailId(); + if (batchDetailId != null && !addedBatchDetailIds.contains(batchDetailId)) { + materialMoreDetailList.add(materialMoreDetailPO); + addedBatchDetailIds.add(batchDetailId); + } + + // 根据fieldName或batchLabels从库存表中获取对应的字段值 + if (inventoryPO != null) { + String fieldValue = null; + String usedFieldName = null; + // 优先使用fieldName(如果存在) + if (StringUtils.isNotBlank(batchDetailFeignPO.getFieldName())) { + usedFieldName = batchDetailFeignPO.getFieldName(); + fieldValue = getFieldValueByFieldName(inventoryPO, usedFieldName); + log.info("批次属性 batchDetailId={}, fieldName={}, 从库存表获取到的值={}", + batchDetailFeignPO.getBatchDetailId(), usedFieldName, fieldValue); + } + // 如果fieldName为空,尝试根据batchLabels映射到字段名 + else if (StringUtils.isNotBlank(batchDetailFeignPO.getBatchLabels())) { + String fieldNameByLabel = getFieldNameByBatchLabel(batchDetailFeignPO.getBatchLabels()); + if (StringUtils.isNotBlank(fieldNameByLabel)) { + usedFieldName = fieldNameByLabel; + fieldValue = getFieldValueByFieldName(inventoryPO, usedFieldName); + log.info("批次属性 batchDetailId={}, batchLabels={}, 映射字段名={}, 从库存表获取到的值={}", + batchDetailFeignPO.getBatchDetailId(), batchDetailFeignPO.getBatchLabels(), + usedFieldName, fieldValue); + } + } + // 只有当获取到值时才更新attributeValue + if (fieldValue != null) { + materialMoreDetailPO.setAttributeValue(fieldValue); + log.info("设置更多属性:batchDetailId={}, batchLabels={}, attributeValue={}", + materialMoreDetailPO.getBatchDetailId(), materialMoreDetailPO.getBatchLabels(), + materialMoreDetailPO.getAttributeValue()); + } + } + } + } + materialInventoryPO.setMaterialMoreDetailList(materialMoreDetailList); + log.info("物料库存 materialInventoryId={}, materialBaseInfoId={}, materialMoreDetailList大小={}", + materialInventoryPO.getMaterialInventoryId(), materialInventoryPO.getMaterialBaseInfoId(), + materialMoreDetailList != null ? materialMoreDetailList.size() : 0); + }); + } + + /** + * 根据字段名从对象中获取字段值(使用反射) + * @param obj 对象 + * @param fieldName 字段名 + * @return 字段值(转换为字符串) + */ + private String getFieldValueByFieldName(Object obj, String fieldName) { + if (obj == null || StringUtils.isBlank(fieldName)) { + return null; + } + + try { + // 兼容:如果传的是下划线命名(数据库列名风格),先转成驼峰字段名再取 + String normalizedFieldName = fieldName; + if (normalizedFieldName.contains("_")) { + normalizedFieldName = snakeToCamel(normalizedFieldName); + } + + // 获取对象的Class + Class clazz = obj.getClass(); + + // 尝试获取字段(包括父类字段) + Field field = null; + Class currentClass = clazz; + while (currentClass != null && field == null) { + try { + field = currentClass.getDeclaredField(normalizedFieldName); + } catch (NoSuchFieldException e) { + currentClass = currentClass.getSuperclass(); + } + } + + if (field == null) { + log.warn("字段不存在:{}(normalized:{}),对象类型:{}", fieldName, normalizedFieldName, clazz.getName()); + return null; + } + + // 设置可访问 + field.setAccessible(true); + + // 获取字段值 + Object value = field.get(obj); + + // 转换为字符串 + if (value == null) { + log.info("字段 {} 的值为 null,对象类型:{}", normalizedFieldName, clazz.getName()); + return null; + } + + String result; + // 处理日期类型字段 + if (value instanceof Date) { + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); + result = sdf.format((Date) value); + } else { + result = value.toString(); + } + + log.info("通过反射获取字段值成功,字段名:{}(原始:{}),值:{},对象类型:{}", + normalizedFieldName, fieldName, result, clazz.getName()); + return result; + } catch (Exception e) { + log.warn("通过反射获取字段值失败,字段名:{},对象类型:{},错误:{}", fieldName, obj.getClass().getName(), e.getMessage()); + return null; + } + } + + private String snakeToCamel(String snake) { + if (StringUtils.isBlank(snake)) { + return null; + } + String s = snake.trim().toLowerCase(); + StringBuilder sb = new StringBuilder(); + boolean upperNext = false; + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (c == '_') { + upperNext = true; + continue; + } + if (upperNext) { + sb.append(Character.toUpperCase(c)); + upperNext = false; + } else { + sb.append(c); + } + } + return sb.toString(); + } + + /** + * 根据批次标签(batchLabels)映射到字段名 + * @param batchLabel 批次标签 + * @return 字段名,如果找不到映射则返回null + */ + private String getFieldNameByBatchLabel(String batchLabel) { + if (StringUtils.isBlank(batchLabel)) { + return null; + } + + // 批次标签到字段名的映射关系 + Map labelToFieldNameMap = new HashMap<>(); + labelToFieldNameMap.put("Invoice No.", "invoiceNo"); + labelToFieldNameMap.put("Invoice No", "invoiceNo"); + labelToFieldNameMap.put("发票号", "invoiceNo"); + labelToFieldNameMap.put("Batch Ref NO's", "batchRefNo"); + labelToFieldNameMap.put("Batch Ref NO", "batchRefNo"); + labelToFieldNameMap.put("批次参考号", "batchRefNo"); + labelToFieldNameMap.put("Sheet Ref NO's", "sheetRefNo"); + labelToFieldNameMap.put("Sheet Ref NO", "sheetRefNo"); + labelToFieldNameMap.put("单据参考号", "sheetRefNo"); + labelToFieldNameMap.put("箱号/卡板号", "boxPalletNo"); + labelToFieldNameMap.put("箱号", "boxPalletNo"); + labelToFieldNameMap.put("卡板号", "boxPalletNo"); + labelToFieldNameMap.put("生产日期", "productionDate"); + labelToFieldNameMap.put("过期日期", "expiryDate"); + labelToFieldNameMap.put("存货日期", "inventoryDate"); + labelToFieldNameMap.put("扩展字段1", "extAttr1"); + labelToFieldNameMap.put("扩展字段2", "extAttr2"); + labelToFieldNameMap.put("扩展字段3", "extAttr3"); + labelToFieldNameMap.put("扩展字段4", "extAttr4"); + + // 精确匹配 + String fieldName = labelToFieldNameMap.get(batchLabel.trim()); + if (StringUtils.isNotBlank(fieldName)) { + return fieldName; + } + + // 模糊匹配(包含关系) + for (Map.Entry entry : labelToFieldNameMap.entrySet()) { + if (batchLabel.contains(entry.getKey()) || entry.getKey().contains(batchLabel)) { + return entry.getValue(); + } + } + + return null; + } + /** * @description 单件复核 * @author ZhouGY