Merge branch 'dev_qinhongzhanWMS' into wms_dev
This commit is contained in:
+284
-1
@@ -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<MaterialInventoryPO> queryListWithBatchAttributes(MaterialInventoryDO materialInventoryDO) {
|
||||
List<MaterialInventoryPO> list = queryList(materialInventoryDO);
|
||||
|
||||
if (CollectionUtils.isEmpty(list)) {
|
||||
return list;
|
||||
}
|
||||
|
||||
// 性能优化:批量获取批次属性,避免N+1查询问题
|
||||
// 1. 收集所有唯一的materialBaseInfoId
|
||||
Set<Long> materialBaseInfoIdSet = list.stream()
|
||||
.map(MaterialInventoryPO::getMaterialBaseInfoId)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
// 2. 批量查询批次属性(使用Map缓存,避免重复查询相同的materialBaseInfoId)
|
||||
Map<Long, List<BatchDetailFeignPO>> batchDetailMap = new HashMap<>();
|
||||
for (Long materialBaseInfoId : materialBaseInfoIdSet) {
|
||||
try {
|
||||
AjaxResult ajaxResult = systemServiceFeign.getBatchDetailListByMaterialBaseInfoId(materialBaseInfoId);
|
||||
if (ajaxResult != null && "200".equals(String.valueOf(ajaxResult.get("code")))) {
|
||||
List<BatchDetailFeignPO> batchDetailFeignPOList = JSON.parseArray(
|
||||
JSONObject.toJSONString(ajaxResult.get("data")), BatchDetailFeignPO.class);
|
||||
if (!CollectionUtils.isEmpty(batchDetailFeignPOList)) {
|
||||
// 过滤isDisplay=1的属性
|
||||
List<BatchDetailFeignPO> 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<MaterialInventoryPO> materialInventoryPOList,
|
||||
Map<Long, List<BatchDetailFeignPO>> batchDetailMap) {
|
||||
if (CollectionUtils.isEmpty(materialInventoryPOList)) {
|
||||
return;
|
||||
}
|
||||
|
||||
materialInventoryPOList.forEach(materialInventoryPO -> {
|
||||
Long materialBaseInfoId = materialInventoryPO.getMaterialBaseInfoId();
|
||||
Long materialInventoryId = materialInventoryPO.getMaterialInventoryId();
|
||||
|
||||
// 创建新的materialMoreDetailList,只包含isDisplay=1的属性
|
||||
List<MaterialMoreDetailPO> materialMoreDetailList = new ArrayList<>();
|
||||
|
||||
// 从库存表获取数据(包含批次属性字段)
|
||||
MaterialInventoryPO inventoryPO = null;
|
||||
if (materialInventoryId != null) {
|
||||
inventoryPO = materialInventoryMapper.selectByIdWithBatchAttributes(materialInventoryId);
|
||||
}
|
||||
|
||||
if (materialBaseInfoId != null && batchDetailMap.containsKey(materialBaseInfoId)) {
|
||||
List<BatchDetailFeignPO> batchDetailFeignPOList = batchDetailMap.get(materialBaseInfoId);
|
||||
// 用于跟踪已添加到列表中的batchDetailId
|
||||
Set<Long> 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<String, String> 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<String, String> 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){
|
||||
|
||||
+295
-4
@@ -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<PickingMaterialDetailPO> pickingMaterialDetailPOList = pickingOrderPO.getMaterialDetailList();
|
||||
|
||||
if (CollectionUtils.isEmpty(pickingMaterialDetailPOList)) {
|
||||
return pickingOrderPO;
|
||||
}
|
||||
|
||||
// 性能优化:批量获取批次属性,避免N+1查询问题
|
||||
// 1. 收集所有唯一的materialBaseInfoId
|
||||
Set<Long> materialBaseInfoIdSet = pickingMaterialDetailPOList.stream()
|
||||
.map(PickingMaterialDetailPO::getMaterialBaseInfoId)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
// 2. 批量查询批次属性(使用Map缓存,避免重复查询相同的materialBaseInfoId)
|
||||
Map<Long, List<BatchDetailFeignPO>> batchDetailMap = new HashMap<>();
|
||||
for (Long materialBaseInfoId : materialBaseInfoIdSet) {
|
||||
try {
|
||||
AjaxResult ajaxResult = systemServiceFeign.getBatchDetailListByMaterialBaseInfoId(materialBaseInfoId);
|
||||
if (ajaxResult != null && "200".equals(String.valueOf(ajaxResult.get("code")))) {
|
||||
List<BatchDetailFeignPO> batchDetailFeignPOList = JSON.parseArray(
|
||||
JSONObject.toJSONString(ajaxResult.get("data")), BatchDetailFeignPO.class);
|
||||
if (!CollectionUtils.isEmpty(batchDetailFeignPOList)) {
|
||||
// 过滤isDisplay=1的属性
|
||||
List<BatchDetailFeignPO> 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<PickingMaterialDetailPO> pickingMaterialDetailPOList,
|
||||
Map<Long, List<BatchDetailFeignPO>> batchDetailMap) {
|
||||
if (CollectionUtils.isEmpty(pickingMaterialDetailPOList)) {
|
||||
return;
|
||||
}
|
||||
|
||||
pickingMaterialDetailPOList.forEach(pickingMaterialDetailPO -> {
|
||||
Long materialBaseInfoId = pickingMaterialDetailPO.getMaterialBaseInfoId();
|
||||
|
||||
// 只保留isDisplay=1的批次属性(从batchDetailMap中获取,已经过滤过了)
|
||||
// 创建新的materialMoreDetailList,只包含isDisplay=1的属性
|
||||
List<MaterialMoreDetailPO> materialMoreDetailList = new ArrayList<>();
|
||||
|
||||
// 创建batchDetailId到MaterialMoreDetailPO的映射,用于快速查找已有的属性值
|
||||
Map<Long, MaterialMoreDetailPO> batchDetailIdToMaterialMoreDetailMap = new HashMap<>();
|
||||
List<MaterialMoreDetailPO> 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<BatchDetailFeignPO> batchDetailFeignPOList = batchDetailMap.get(materialBaseInfoId);
|
||||
// 用于跟踪已添加到列表中的batchDetailId
|
||||
Set<Long> 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<String, String> 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<String, String> 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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+6
@@ -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<MaterialMoreDetailPO> materialMoreDetailList;
|
||||
}
|
||||
+3
@@ -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<OutMaterialDetailSerialNumberDO> materialDetailSerialNumberList;
|
||||
|
||||
@ApiModelProperty("物料更多属性列表(批次属性)")
|
||||
private List<MaterialMoreDetailPO> materialMoreDetailList;
|
||||
|
||||
}
|
||||
|
||||
+3
-3
@@ -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);
|
||||
|
||||
+2
-1
@@ -43,7 +43,8 @@ public class MaterialInventoryApi extends BaseController {
|
||||
//转换实体
|
||||
MaterialInventoryDO materialInventoryDO = materialInventoryAssembler.toDO(materialInventoryDTO);
|
||||
startPage();
|
||||
List<MaterialInventoryPO> list = materialInventoryApplicationService.queryList(materialInventoryDO);
|
||||
// 使用带批次属性的查询方法,批次属性值从库存表中获取
|
||||
List<MaterialInventoryPO> list = materialInventoryApplicationService.queryListWithBatchAttributes(materialInventoryDO);
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user