diff --git a/mhd_wms/src/main/java/com/mhd/wms/application/service/materialInventory/MaterialInventoryApplicationService.java b/mhd_wms/src/main/java/com/mhd/wms/application/service/materialInventory/MaterialInventoryApplicationService.java index 44d7ca251..d99e00be0 100644 --- a/mhd_wms/src/main/java/com/mhd/wms/application/service/materialInventory/MaterialInventoryApplicationService.java +++ b/mhd_wms/src/main/java/com/mhd/wms/application/service/materialInventory/MaterialInventoryApplicationService.java @@ -1,21 +1,32 @@ package com.mhd.wms.application.service.materialInventory; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson2.JSONObject; import com.alibaba.nacos.common.utils.CollectionUtils; +import com.mhd.common.core.utils.StringUtils; +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.cache.AssociationWarehouseCacheDO; import com.mhd.system.api.domain.cache.SystemServiceCacheUtil; import com.mhd.system.api.model.LoginUser; +import com.mhd.wms.domain.materialInventory.repository.mapper.MaterialInventoryMapper; import com.mhd.wms.domain.materialInventory.repository.po.MaterialInventoryPO; import com.mhd.wms.domain.materialInventory.repository.po.MaterialInventoryQueryListPO; import com.mhd.wms.domain.materialInventory.repository.todo.MaterialInventoryDO; import com.mhd.wms.domain.materialInventory.repository.todo.MaterialInventoryQueryListDO; import com.mhd.wms.domain.materialInventory.service.MaterialInventoryDomainService; +import com.mhd.wms.domain.materialMoreDetail.repository.po.MaterialMoreDetailPO; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import java.lang.reflect.Field; import java.math.BigDecimal; -import java.util.List; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.stream.Collectors; /** * 物料库存ApplicationService @@ -28,6 +39,10 @@ import java.util.List; public class MaterialInventoryApplicationService { @Autowired private MaterialInventoryDomainService materialInventoryDomainService; + @Autowired + private SystemServiceFeign systemServiceFeign; + @Autowired + private MaterialInventoryMapper materialInventoryMapper; /** @@ -47,6 +62,274 @@ public class MaterialInventoryApplicationService { return materialInventoryDomainService.queryList(materialInventoryDO); } + /** + * 查询物料库存列表(带批次属性) + * 批次属性值从库存表中获取 + */ + public List queryListWithBatchAttributes(MaterialInventoryDO materialInventoryDO) { + List list = queryList(materialInventoryDO); + + if (CollectionUtils.isEmpty(list)) { + return list; + } + + // 性能优化:批量获取批次属性,避免N+1查询问题 + // 1. 收集所有唯一的materialBaseInfoId + Set materialBaseInfoIdSet = list.stream() + .map(MaterialInventoryPO::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(list, batchDetailMap); + + return list; + } + + /** + * 为物料库存设置更多属性(从库存表中获取值) + * @param materialInventoryPOList 物料库存列表 + * @param batchDetailMap 批次属性Map + */ + private void setMaterialMoreDetailListFromInventory(List materialInventoryPOList, + Map> batchDetailMap) { + if (CollectionUtils.isEmpty(materialInventoryPOList)) { + return; + } + + materialInventoryPOList.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); + }); + } + + /** + * 根据批次标签(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; + } + + /** + * 根据字段名从对象中获取字段值(使用反射) + * @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(); + } + public BigDecimal sumInventoryQuantity(MaterialInventoryDO materialInventoryDO) { LoginUser loginUser = SecurityUtils.getLoginUser(); if (loginUser != null){ diff --git a/mhd_wms/src/main/java/com/mhd/wms/application/service/pickingOrder/PickingOrderApplicationService.java b/mhd_wms/src/main/java/com/mhd/wms/application/service/pickingOrder/PickingOrderApplicationService.java index 46b85242b..22a2338ea 100644 --- a/mhd_wms/src/main/java/com/mhd/wms/application/service/pickingOrder/PickingOrderApplicationService.java +++ b/mhd_wms/src/main/java/com/mhd/wms/application/service/pickingOrder/PickingOrderApplicationService.java @@ -6,15 +6,18 @@ import com.alibaba.fastjson.JSON; import com.alibaba.fastjson2.JSONObject; import com.mhd.common.core.domain.po.UserPo; import com.mhd.common.core.exception.ServiceException; +import com.mhd.common.core.utils.StringUtils; 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.UserServiceFeign; +import com.mhd.system.api.domain.BatchDetailFeignPO; import com.mhd.system.api.domain.StorageLocationFeignPO; import com.mhd.system.api.domain.cache.AssociationWarehouseCacheDO; import com.mhd.system.api.domain.cache.SystemServiceCacheUtil; import com.mhd.system.api.model.LoginUser; +import com.mhd.wms.domain.materialMoreDetail.repository.po.MaterialMoreDetailPO; import com.mhd.wms.domain.pickingMaterialDetail.repository.po.PickingMaterialDetailPO; import com.mhd.wms.domain.pickingMaterialDetail.repository.todo.PickingMaterialDetailDO; import com.mhd.wms.domain.pickingMaterialDetail.service.PickingMaterialDetailDomainService; @@ -26,10 +29,10 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; +import java.lang.reflect.Field; import java.math.BigDecimal; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; +import java.text.SimpleDateFormat; +import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; @@ -96,7 +99,295 @@ public class PickingOrderApplicationService { * 获取拣货单详细信息 */ public PickingOrderPO getInfoByIdAndType(Long pickingOrderId, Integer type) { - return pickingOrderDomainService.getInfoByIdAndType(pickingOrderId, type); + PickingOrderPO pickingOrderPO = pickingOrderDomainService.getInfoByIdAndType(pickingOrderId, type); + List pickingMaterialDetailPOList = pickingOrderPO.getMaterialDetailList(); + + if (CollectionUtils.isEmpty(pickingMaterialDetailPOList)) { + return pickingOrderPO; + } + + // 性能优化:批量获取批次属性,避免N+1查询问题 + // 1. 收集所有唯一的materialBaseInfoId + Set materialBaseInfoIdSet = pickingMaterialDetailPOList.stream() + .map(PickingMaterialDetailPO::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. 为每个物料明细设置更多属性(从物料明细中获取值),递归处理所有层级 + setMaterialMoreDetailListRecursively(pickingMaterialDetailPOList, batchDetailMap); + + pickingOrderPO.setMaterialDetailList(pickingMaterialDetailPOList); + log.info("返回拣货单信息,pickingOrderId={}, materialDetailList大小={}", + pickingOrderPO.getPickingOrderId(), + pickingMaterialDetailPOList != null ? pickingMaterialDetailPOList.size() : 0); + return pickingOrderPO; + } + + /** + * 递归为物料明细设置更多属性 + * @param pickingMaterialDetailPOList 物料明细列表 + * @param batchDetailMap 批次属性Map + */ + private void setMaterialMoreDetailListRecursively(List pickingMaterialDetailPOList, + Map> batchDetailMap) { + if (CollectionUtils.isEmpty(pickingMaterialDetailPOList)) { + return; + } + + pickingMaterialDetailPOList.forEach(pickingMaterialDetailPO -> { + Long materialBaseInfoId = pickingMaterialDetailPO.getMaterialBaseInfoId(); + + // 只保留isDisplay=1的批次属性(从batchDetailMap中获取,已经过滤过了) + // 创建新的materialMoreDetailList,只包含isDisplay=1的属性 + List materialMoreDetailList = new ArrayList<>(); + + // 创建batchDetailId到MaterialMoreDetailPO的映射,用于快速查找已有的属性值 + Map batchDetailIdToMaterialMoreDetailMap = new HashMap<>(); + List existingList = pickingMaterialDetailPO.getMaterialMoreDetailList(); + if (!CollectionUtils.isEmpty(existingList)) { + for (MaterialMoreDetailPO existing : existingList) { + if (existing.getBatchDetailId() != null) { + batchDetailIdToMaterialMoreDetailMap.put(existing.getBatchDetailId(), existing); + } + } + } + + if (materialBaseInfoId != null && batchDetailMap.containsKey(materialBaseInfoId)) { + List batchDetailFeignPOList = batchDetailMap.get(materialBaseInfoId); + // 用于跟踪已添加到列表中的batchDetailId + Set addedBatchDetailIds = new HashSet<>(); + + for (BatchDetailFeignPO batchDetailFeignPO : batchDetailFeignPOList) { + MaterialMoreDetailPO materialMoreDetailPO = batchDetailIdToMaterialMoreDetailMap.get(batchDetailFeignPO.getBatchDetailId()); + + // 如果不存在,创建新的 + if (materialMoreDetailPO == null) { + 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()); + } else { + // 如果已存在,更新属性(保留已有的attributeValue) + materialMoreDetailPO.setBatchId(batchDetailFeignPO.getBatchId()); + 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 (pickingMaterialDetailPO != null) { + String fieldValue = null; + String usedFieldName = null; + // 优先使用fieldName(如果存在) + if (StringUtils.isNotBlank(batchDetailFeignPO.getFieldName())) { + usedFieldName = batchDetailFeignPO.getFieldName(); + fieldValue = getFieldValueByFieldName(pickingMaterialDetailPO, 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(pickingMaterialDetailPO, 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()); + } + } + } + } + pickingMaterialDetailPO.setMaterialMoreDetailList(materialMoreDetailList); + log.info("物料明细 uniqueId={}, materialBaseInfoId={}, materialMoreDetailList大小={}", + pickingMaterialDetailPO.getUniqueId(), pickingMaterialDetailPO.getMaterialBaseInfoId(), + materialMoreDetailList != null ? materialMoreDetailList.size() : 0); + + // 递归处理子级 + if (!CollectionUtils.isEmpty(pickingMaterialDetailPO.getChildren())) { + setMaterialMoreDetailListRecursively(pickingMaterialDetailPO.getChildren(), batchDetailMap); + } + }); + } + + /** + * 根据批次标签(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; + } + + /** + * 根据字段名从对象中获取字段值(使用反射) + * @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(); } /** diff --git a/mhd_wms/src/main/java/com/mhd/wms/domain/materialInventory/repository/po/MaterialInventoryPO.java b/mhd_wms/src/main/java/com/mhd/wms/domain/materialInventory/repository/po/MaterialInventoryPO.java index 190ec65c2..5c186a88a 100644 --- a/mhd_wms/src/main/java/com/mhd/wms/domain/materialInventory/repository/po/MaterialInventoryPO.java +++ b/mhd_wms/src/main/java/com/mhd/wms/domain/materialInventory/repository/po/MaterialInventoryPO.java @@ -8,8 +8,11 @@ import io.swagger.annotations.ApiModelProperty; import lombok.Data; import org.springframework.format.annotation.DateTimeFormat; +import com.mhd.wms.domain.materialMoreDetail.repository.po.MaterialMoreDetailPO; + import java.math.BigDecimal; import java.util.Date; +import java.util.List; /** @@ -202,4 +205,7 @@ public class MaterialInventoryPO extends MaterialBasePO { @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") @Excel(name = "ExtAttr4", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss") private Date extAttr4; + + @ApiModelProperty("物料更多属性列表(批次属性)") + private List materialMoreDetailList; } \ No newline at end of file diff --git a/mhd_wms/src/main/java/com/mhd/wms/domain/pickingMaterialDetail/repository/po/PickingMaterialDetailPO.java b/mhd_wms/src/main/java/com/mhd/wms/domain/pickingMaterialDetail/repository/po/PickingMaterialDetailPO.java index 9ba614111..725bbb275 100644 --- a/mhd_wms/src/main/java/com/mhd/wms/domain/pickingMaterialDetail/repository/po/PickingMaterialDetailPO.java +++ b/mhd_wms/src/main/java/com/mhd/wms/domain/pickingMaterialDetail/repository/po/PickingMaterialDetailPO.java @@ -4,6 +4,7 @@ import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; import com.mhd.common.core.annotation.Excel; import com.mhd.common.core.web.domain.BaseVOEntity; +import com.mhd.wms.domain.materialMoreDetail.repository.po.MaterialMoreDetailPO; import com.mhd.wms.domain.outMaterialDetailSerialNumber.repository.todo.OutMaterialDetailSerialNumberDO; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; @@ -194,5 +195,7 @@ public class PickingMaterialDetailPO extends BaseVOEntity { @ApiModelProperty("序列号") private List materialDetailSerialNumberList; + @ApiModelProperty("物料更多属性列表(批次属性)") + private List materialMoreDetailList; } 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 10e70e248..dd044c40e 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 @@ -132,7 +132,7 @@ public class StockOutOrderDomainService { //统计种类 //stockOutOrderDO.setMaterialQuantity(stockOutOrderDO.getMaterialDetailList().size()); //统计总数量 - BigDecimal quantity = stockOutOrderDO.getMaterialDetailList().stream().map(OutMaterialDetailDO::getOutboundQuantity).reduce(BigDecimal.ZERO, BigDecimal::add); + BigDecimal quantity = stockOutOrderDO.getMaterialDetailList().stream().map(OutMaterialDetailDO::getOutboundQuantity).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add); stockOutOrderDO.setQuantity(quantity); //统计总重量 // BigDecimal weightLimit = stockOutOrderDO.getMaterialDetailList().stream().map(OutMaterialDetailDO::getWeightLimit).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add); @@ -158,7 +158,7 @@ public class StockOutOrderDomainService { //统计种类 stockOutOrderDO.setMaterialQuantity(stockOutOrderDO.getMaterialDetailList().size()); //统计总数量 - BigDecimal quantity = stockOutOrderDO.getMaterialDetailList().stream().map(OutMaterialDetailDO::getQuantity).reduce(BigDecimal.ZERO, BigDecimal::add); + BigDecimal quantity = stockOutOrderDO.getMaterialDetailList().stream().map(OutMaterialDetailDO::getQuantity).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add); stockOutOrderDO.setQuantity(quantity); //统计总重量 BigDecimal weightLimit = stockOutOrderDO.getMaterialDetailList().stream().map(OutMaterialDetailDO::getWeightLimit).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add); @@ -185,7 +185,7 @@ public class StockOutOrderDomainService { //统计种类 stockOutOrderDO.setMaterialQuantity(stockOutOrderDO.getMaterialDetailList().size()); //统计总数量 - BigDecimal quantity = stockOutOrderDO.getMaterialDetailList().stream().map(OutMaterialDetailDO::getQuantity).reduce(BigDecimal.ZERO, BigDecimal::add); + BigDecimal quantity = stockOutOrderDO.getMaterialDetailList().stream().map(OutMaterialDetailDO::getQuantity).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add); stockOutOrderDO.setQuantity(quantity); //统计总重量 BigDecimal weightLimit = stockOutOrderDO.getMaterialDetailList().stream().map(OutMaterialDetailDO::getWeightLimit).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add); diff --git a/mhd_wms/src/main/java/com/mhd/wms/interfaces/facadeApi/materialInventory/MaterialInventoryApi.java b/mhd_wms/src/main/java/com/mhd/wms/interfaces/facadeApi/materialInventory/MaterialInventoryApi.java index 04cc46430..5ed1a840f 100644 --- a/mhd_wms/src/main/java/com/mhd/wms/interfaces/facadeApi/materialInventory/MaterialInventoryApi.java +++ b/mhd_wms/src/main/java/com/mhd/wms/interfaces/facadeApi/materialInventory/MaterialInventoryApi.java @@ -43,7 +43,8 @@ public class MaterialInventoryApi extends BaseController { //转换实体 MaterialInventoryDO materialInventoryDO = materialInventoryAssembler.toDO(materialInventoryDTO); startPage(); - List list = materialInventoryApplicationService.queryList(materialInventoryDO); + // 使用带批次属性的查询方法,批次属性值从库存表中获取 + List list = materialInventoryApplicationService.queryListWithBatchAttributes(materialInventoryDO); return getDataTable(list); }