批次规则bug修改

This commit is contained in:
秦鸿展
2026-01-29 17:26:05 +08:00
parent e0d3e40847
commit 43aec4ac0d
11 changed files with 967 additions and 54 deletions
@@ -120,7 +120,11 @@ public class MaterialBaseInfoApplicationService {
throw new ServiceException("获取货主信息失败"); throw new ServiceException("获取货主信息失败");
} }
UserPo userPo = JSON.parseObject(JSONObject.toJSONString(ajaxResult.get("data")), UserPo.class); UserPo userPo = JSON.parseObject(JSONObject.toJSONString(ajaxResult.get("data")), UserPo.class);
materialBaseInfoDO.setShipperName(userPo.getUserName()); // 如果原始shipperName不为空,保留原始值(导入或手动输入时应该保留完整名称)
// 只有在原始shipperName为空时,才使用查询到的userName
if (materialBaseInfoDO.getShipperName() == null || materialBaseInfoDO.getShipperName().trim().isEmpty()) {
materialBaseInfoDO.setShipperName(userPo.getUserName());
}
} }
public List<MaterialStockInfoVo> getMaterialStockList(String shipperName, String materialName, public List<MaterialStockInfoVo> getMaterialStockList(String shipperName, String materialName,
@@ -1616,23 +1616,20 @@ public class StockInOrderApplicationService {
// 如果编号不匹配,再根据名称匹配(支持去除空格后匹配) // 如果编号不匹配,再根据名称匹配(支持去除空格后匹配)
if (!matched && StringUtils.isNotEmpty(normalizedShipperName) && userName != null) { if (!matched && StringUtils.isNotEmpty(normalizedShipperName) && userName != null) {
String normalizedUserName = String.valueOf(userName).trim(); String normalizedUserName = String.valueOf(userName).trim();
// 精确匹配 // 精确匹配(优先)
if (normalizedShipperName.equals(normalizedUserName)) { if (normalizedShipperName.equals(normalizedUserName)) {
matched = true; matched = true;
log.debug("通过名称精确匹配成功,货主名称:{},货主ID:{}", normalizedShipperName, userId); log.debug("通过名称精确匹配成功,货主名称:{},货主ID:{}", normalizedShipperName, userId);
} }
// 如果精确匹配失败,尝试包含匹配(支持部分匹配 // 如果精确匹配失败,尝试包含匹配(列表中的名称包含导入的名称,例如列表中是"珠海百思科鞋材有限公司",导入的是"百思科"
// 注意:这里不使用反向包含匹配(导入的名称包含列表中的名称),因为这样会误匹配(例如导入"珠海百思科鞋材有限公司"会匹配到"百思科"
if (!matched && normalizedUserName.contains(normalizedShipperName)) { if (!matched && normalizedUserName.contains(normalizedShipperName)) {
matched = true; matched = true;
log.debug("通过名称包含匹配成功,货主名称:{},列表中的名称:{},货主ID:{}", log.debug("通过名称包含匹配成功,货主名称:{},列表中的名称:{},货主ID:{}",
normalizedShipperName, normalizedUserName, userId); normalizedShipperName, normalizedUserName, userId);
} }
// 如果包含匹配也失败,尝试反向包含匹配 // 不再使用反向包含匹配,避免误匹配
if (!matched && normalizedShipperName.contains(normalizedUserName)) { // 例如:导入"珠海百思科鞋材有限公司"不应该匹配到列表中的"百思科"
matched = true;
log.debug("通过名称反向包含匹配成功,货主名称:{},列表中的名称:{},货主ID:{}",
normalizedShipperName, normalizedUserName, userId);
}
} }
if (matched && userId != null) { if (matched && userId != null) {
@@ -1698,7 +1695,11 @@ public class StockInOrderApplicationService {
throw new ServiceException("获取货主信息失败:返回数据为空(货主ID: " + shipperId + ""); throw new ServiceException("获取货主信息失败:返回数据为空(货主ID: " + shipperId + "");
} }
stockInOrderDO.setShipperCode(userPo.getUserMemberCode()); stockInOrderDO.setShipperCode(userPo.getUserMemberCode());
stockInOrderDO.setShipperName(userPo.getUserName()); // 如果原始shipperName不为空,保留原始值(导入时读取的客户名称应该保留)
// 只有在原始shipperName为空时,才使用查询到的userName
if (StringUtils.isBlank(stockInOrderDO.getShipperName())) {
stockInOrderDO.setShipperName(userPo.getUserName());
}
} }
/** /**
@@ -43,12 +43,17 @@ import org.springframework.stereotype.Service;
import com.mhd.common.core.utils.StringUtils; import com.mhd.common.core.utils.StringUtils;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import java.lang.reflect.Field;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.text.ParseException; import java.text.ParseException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@@ -136,23 +141,52 @@ public class StockReceiptOrderApplicationService {
receiptMaterialDetailDO.setReceiptOrderNumber(stockReceiptOrderPO.getReceiptOrderNumber()); receiptMaterialDetailDO.setReceiptOrderNumber(stockReceiptOrderPO.getReceiptOrderNumber());
List<ReceiptMaterialDetailPO> receiptMaterialDetailPOList = receiptMaterialDetailDomainService.queryListChildren(receiptMaterialDetailDO); List<ReceiptMaterialDetailPO> receiptMaterialDetailPOList = receiptMaterialDetailDomainService.queryListChildren(receiptMaterialDetailDO);
// 为每个物料明细设置更多属性(根据物料绑定的批次属性,过滤isDisplay=1的属性) // 性能优化:批量获取批次属性,避免N+1查询问题
receiptMaterialDetailPOList.forEach(receiptMaterialDetailPO -> { // 1. 收集所有唯一的materialBaseInfoId
List<MaterialMoreDetailPO> materialMoreDetailList = Set<Long> materialBaseInfoIdSet = receiptMaterialDetailPOList.stream()
getMaterialMoreDetailByBatch(receiptMaterialDetailPO.getMaterialBaseInfoId()); .map(ReceiptMaterialDetailPO::getMaterialBaseInfoId)
receiptMaterialDetailPO.setMaterialMoreDetailList(materialMoreDetailList); .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(receiptMaterialDetailPOList, batchDetailMap);
stockReceiptOrderPO.setMaterialDetailList(receiptMaterialDetailPOList); stockReceiptOrderPO.setMaterialDetailList(receiptMaterialDetailPOList);
log.info("返回收货单信息,receiptOrderId={}, materialDetailList大小={}",
stockReceiptOrderPO.getReceiptOrderId(),
receiptMaterialDetailPOList != null ? receiptMaterialDetailPOList.size() : 0);
return stockReceiptOrderPO; return stockReceiptOrderPO;
} }
/** /**
* 根据物料基础信息ID获取批次属性(更多属性),只返回isDisplay=1的属性 * 根据物料基础信息ID获取批次属性(更多属性),只返回isDisplay=1的属性
* 并根据fieldName从物料明细中获取对应的字段值
* @param materialBaseInfoId 物料基础信息ID * @param materialBaseInfoId 物料基础信息ID
* @param receiptMaterialDetailPO 收货物料明细对象,用于根据fieldName获取字段值
* @return 更多属性列表 * @return 更多属性列表
*/ */
private List<MaterialMoreDetailPO> getMaterialMoreDetailByBatch(Long materialBaseInfoId) { private List<MaterialMoreDetailPO> getMaterialMoreDetailByBatch(Long materialBaseInfoId, ReceiptMaterialDetailPO receiptMaterialDetailPO) {
List<MaterialMoreDetailPO> materialMoreDetailPOList = new ArrayList<>(); List<MaterialMoreDetailPO> materialMoreDetailPOList = new ArrayList<>();
if (materialBaseInfoId == null) { if (materialBaseInfoId == null) {
return materialMoreDetailPOList; return materialMoreDetailPOList;
@@ -178,6 +212,32 @@ public class StockReceiptOrderApplicationService {
materialMoreDetailPO.setAttributeFormat(batchDetailFeignPO.getAttributeFormat()); materialMoreDetailPO.setAttributeFormat(batchDetailFeignPO.getAttributeFormat());
materialMoreDetailPO.setAttributeOption(batchDetailFeignPO.getAttributeOption()); materialMoreDetailPO.setAttributeOption(batchDetailFeignPO.getAttributeOption());
materialMoreDetailPO.setRemark(batchDetailFeignPO.getRemark()); materialMoreDetailPO.setRemark(batchDetailFeignPO.getRemark());
// 根据fieldName或batchLabels从物料明细中获取对应的字段值
if (receiptMaterialDetailPO != null) {
String fieldValue = null;
String usedFieldName = null;
// 优先使用fieldName(如果存在)
if (StringUtils.isNotBlank(batchDetailFeignPO.getFieldName())) {
usedFieldName = batchDetailFeignPO.getFieldName();
fieldValue = getFieldValueByFieldName(receiptMaterialDetailPO, usedFieldName);
log.debug("批次属性 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(receiptMaterialDetailPO, usedFieldName);
log.debug("批次属性 batchDetailId={}, batchLabels={}, 映射字段名={}, 获取到的值={}",
batchDetailFeignPO.getBatchDetailId(), batchDetailFeignPO.getBatchLabels(),
usedFieldName, fieldValue);
}
}
materialMoreDetailPO.setAttributeValue(fieldValue);
}
materialMoreDetailPOList.add(materialMoreDetailPO); materialMoreDetailPOList.add(materialMoreDetailPO);
}); });
} }
@@ -188,6 +248,300 @@ public class StockReceiptOrderApplicationService {
return materialMoreDetailPOList; return materialMoreDetailPOList;
} }
/**
* 递归为物料明细设置更多属性
* @param receiptMaterialDetailPOList 物料明细列表
* @param batchDetailMap 批次属性Map
*/
private void setMaterialMoreDetailListRecursively(List<ReceiptMaterialDetailPO> receiptMaterialDetailPOList,
Map<Long, List<BatchDetailFeignPO>> batchDetailMap) {
if (CollectionUtils.isEmpty(receiptMaterialDetailPOList)) {
return;
}
receiptMaterialDetailPOList.forEach(receiptMaterialDetailPO -> {
Long materialBaseInfoId = receiptMaterialDetailPO.getMaterialBaseInfoId();
// 只保留isDisplay=1的批次属性(从batchDetailMap中获取,已经过滤过了)
// 创建新的materialMoreDetailList,只包含isDisplay=1的属性
List<MaterialMoreDetailPO> materialMoreDetailList = new ArrayList<>();
// 创建batchDetailId到MaterialMoreDetailPO的映射,用于快速查找已有的属性值
Map<Long, MaterialMoreDetailPO> batchDetailIdToMaterialMoreDetailMap = new HashMap<>();
List<MaterialMoreDetailPO> existingList = receiptMaterialDetailPO.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 (receiptMaterialDetailPO != null) {
String fieldValue = null;
String usedFieldName = null;
// 优先使用fieldName(如果存在)
if (StringUtils.isNotBlank(batchDetailFeignPO.getFieldName())) {
usedFieldName = batchDetailFeignPO.getFieldName();
fieldValue = getFieldValueByFieldName(receiptMaterialDetailPO, 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(receiptMaterialDetailPO, 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());
}
}
}
}
receiptMaterialDetailPO.setMaterialMoreDetailList(materialMoreDetailList);
log.info("物料明细 uniqueId={}, materialBaseInfoId={}, materialMoreDetailList大小={}",
receiptMaterialDetailPO.getUniqueId(), receiptMaterialDetailPO.getMaterialBaseInfoId(),
materialMoreDetailList != null ? materialMoreDetailList.size() : 0);
// 递归处理子集
if (!CollectionUtils.isEmpty(receiptMaterialDetailPO.getChildren())) {
setMaterialMoreDetailListRecursively(receiptMaterialDetailPO.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("申报数量", "declaredQuantity");
labelToFieldNameMap.put("申报单位", "declarationUnit");
labelToFieldNameMap.put("原产国", "originCountry");
labelToFieldNameMap.put("币制", "currency");
labelToFieldNameMap.put("单位", "unit");
labelToFieldNameMap.put("", "liter");
labelToFieldNameMap.put("尺寸", "dimensions");
labelToFieldNameMap.put("箱数", "boxCount");
labelToFieldNameMap.put("件数", "pieceCount");
labelToFieldNameMap.put("入库数量", "inboundQuantity");
labelToFieldNameMap.put("总净重", "totalNetWeight");
labelToFieldNameMap.put("总毛重", "totalGrossWeight");
labelToFieldNameMap.put("总体积", "totalVolume");
labelToFieldNameMap.put("总面积", "totalArea");
labelToFieldNameMap.put("总价", "totalPrice");
// 精确匹配
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();
}
}
// 如果找不到映射,尝试将批次标签转换为驼峰命名(简单处理)
// 例如:"Invoice No." -> "invoiceNo"
return convertLabelToFieldName(batchLabel);
}
/**
* 将批次标签转换为字段名(驼峰命名)
* @param label 批次标签
* @return 字段名
*/
private String convertLabelToFieldName(String label) {
if (StringUtils.isBlank(label)) {
return null;
}
// 移除特殊字符,转换为小写,用空格或特殊字符分割
String cleaned = label.replaceAll("[^a-zA-Z0-9\\u4e00-\\u9fa5\\s]", " ")
.trim()
.toLowerCase();
// 如果是中文,无法自动转换,返回null
if (cleaned.matches(".*[\\u4e00-\\u9fa5].*")) {
return null;
}
// 将空格分割的单词转换为驼峰命名
String[] words = cleaned.split("\\s+");
if (words.length == 0) {
return null;
}
StringBuilder fieldName = new StringBuilder(words[0]);
for (int i = 1; i < words.length; i++) {
if (words[i].length() > 0) {
fieldName.append(words[i].substring(0, 1).toUpperCase())
.append(words[i].substring(1));
}
}
return fieldName.toString();
}
/**
* 根据字段名从对象中获取字段值(使用反射)
* @param obj 对象
* @param fieldName 字段名
* @return 字段值(转换为字符串)
*/
private String getFieldValueByFieldName(Object obj, String fieldName) {
if (obj == null || StringUtils.isBlank(fieldName)) {
return null;
}
try {
// 兼容:如果传的是下划线命名(数据库列名风格),先转成驼峰字段名再取
// 例如:box_pallet_no -> boxPalletNo
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) {
result = DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD, (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();
}
/** /**
* 获取收货单详细信息 * 获取收货单详细信息
@@ -836,3 +1190,4 @@ public class StockReceiptOrderApplicationService {
receiptMaterialDetailDO.setDistributeRuleName(materialGoodsRulePO.getRuleName()); receiptMaterialDetailDO.setDistributeRuleName(materialGoodsRulePO.getRuleName());
} }
} }
@@ -10,6 +10,8 @@ import lombok.Data;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.List; import java.util.List;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
/** /**
@@ -348,4 +350,35 @@ public class InMaterialDetailPO extends StockInOrderBasePO {
@Excel(name = "总价") @Excel(name = "总价")
private BigDecimal totalPrice; private BigDecimal totalPrice;
@ApiModelProperty("生产日期")
@Excel(name = "生产日期", width = 30, dateFormat = "yyyy-MM-dd")
@JsonFormat(pattern = "yyyy-MM-dd")
private Date productionDate;
@ApiModelProperty("过期日期")
@Excel(name = "过期日期", width = 30, dateFormat = "yyyy-MM-dd")
@JsonFormat(pattern = "yyyy-MM-dd")
private Date expiryDate;
@ApiModelProperty("存货日期")
@Excel(name = "存货日期", width = 30, dateFormat = "yyyy-MM-dd")
@JsonFormat(pattern = "yyyy-MM-dd")
private Date inventoryDate;
@ApiModelProperty("扩展属性1")
@Excel(name = "扩展属性1")
private String extAttr1;
@ApiModelProperty("扩展属性2")
@Excel(name = "扩展属性2")
private String extAttr2;
@ApiModelProperty("扩展属性3")
@Excel(name = "扩展属性3")
private String extAttr3;
@ApiModelProperty("扩展属性4")
@Excel(name = "扩展属性4")
private String extAttr4;
} }
@@ -214,5 +214,73 @@ public class ReceiptMaterialDetail extends BaseVOEntity {
@Excel(name = "备注") @Excel(name = "备注")
private String remark; private String remark;
@ApiModelProperty("总净重(KG)")
@Excel(name = "总净重(KG)")
private BigDecimal totalNetWeight;
@ApiModelProperty("总毛重(KG)")
@Excel(name = "总毛重(KG)")
private BigDecimal totalGrossWeight;
@ApiModelProperty("总体积(CBM)")
@Excel(name = "总体积(CBM)")
private BigDecimal totalVolume;
@ApiModelProperty("总面积(SQM)")
@Excel(name = "总面积(SQM)")
private BigDecimal totalArea;
@ApiModelProperty("Invoice No.")
@Excel(name = "Invoice No.")
private String invoiceNo;
@ApiModelProperty("Batch Ref NO's")
@Excel(name = "Batch Ref NO's")
private String batchRefNo;
@ApiModelProperty("Sheet Ref NO's")
@Excel(name = "Sheet Ref NO's")
private String sheetRefNo;
@ApiModelProperty("箱号/卡板号")
@Excel(name = "箱号/卡板号")
private String boxPalletNo;
@ApiModelProperty("申报数量")
@Excel(name = "申报数量")
private BigDecimal declaredQuantity;
@ApiModelProperty("申报单位")
@Excel(name = "申报单位")
private String declarationUnit;
@ApiModelProperty("原产国")
@Excel(name = "原产国")
private String originCountry;
@ApiModelProperty("币制")
@Excel(name = "币制")
private String currency;
@ApiModelProperty("单位")
@Excel(name = "单位")
private String unit;
@ApiModelProperty("")
@Excel(name = "")
private BigDecimal liter;
@ApiModelProperty("尺寸")
@Excel(name = "尺寸")
private String dimensions;
@ApiModelProperty("箱数")
@Excel(name = "箱数")
private BigDecimal boxCount;
@ApiModelProperty("件数")
@Excel(name = "件数")
private BigDecimal pieceCount;
} }
@@ -13,6 +13,7 @@ import com.mhd.common.core.utils.uuid.IdGenerator;
import com.mhd.common.core.web.domain.AjaxResult; import com.mhd.common.core.web.domain.AjaxResult;
import com.mhd.common.security.utils.SecurityUtils; import com.mhd.common.security.utils.SecurityUtils;
import com.mhd.system.api.SystemServiceFeign; import com.mhd.system.api.SystemServiceFeign;
import com.mhd.system.api.domain.BatchDetailFeignPO;
import com.mhd.system.api.model.LoginUser; import com.mhd.system.api.model.LoginUser;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.mhd.wms.domain.inMaterialDetail.entity.InMaterialDetail; import com.mhd.wms.domain.inMaterialDetail.entity.InMaterialDetail;
@@ -43,11 +44,15 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import com.mhd.common.core.utils.StringUtils; import com.mhd.common.core.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
@@ -56,6 +61,7 @@ import java.util.stream.Collectors;
* @author gen * @author gen
* @date 2024-06-17 * @date 2024-06-17
*/ */
@Slf4j
@Service @Service
public class ReceiptMaterialDetailImpl extends ServiceImpl<ReceiptMaterialDetailMapper, ReceiptMaterialDetail> implements IReceiptMaterialDetailService{ public class ReceiptMaterialDetailImpl extends ServiceImpl<ReceiptMaterialDetailMapper, ReceiptMaterialDetail> implements IReceiptMaterialDetailService{
@Autowired @Autowired
@@ -291,7 +297,7 @@ public class ReceiptMaterialDetailImpl extends ServiceImpl<ReceiptMaterialDetail
if (!CollectionUtils.isEmpty(materialDetailSerialNumberList)){ if (!CollectionUtils.isEmpty(materialDetailSerialNumberList)){
inMaterialDetailSerialNumberService.saveBatch(materialDetailSerialNumberList); inMaterialDetailSerialNumberService.saveBatch(materialDetailSerialNumberList);
} }
//从materialMoreDetailList中根据attributeOption提取字段值并更新入库单明细 //从materialMoreDetailList中提取字段值并更新入库单明细(优先使用batchDetailId->fieldName映射,兼容attributeOption
updateInMaterialDetailFromAttributeOption(receiptMaterialDetailDOList); updateInMaterialDetailFromAttributeOption(receiptMaterialDetailDOList);
} }
@@ -306,64 +312,185 @@ public class ReceiptMaterialDetailImpl extends ServiceImpl<ReceiptMaterialDetail
if (CollectionUtils.isEmpty(receiptMaterialDetailDOList)) { if (CollectionUtils.isEmpty(receiptMaterialDetailDOList)) {
return; return;
} }
// 缓存:materialBaseInfoId -> (batchDetailId -> fieldName)
Map<Long, Map<Long, String>> batchFieldNameCache = new HashMap<>();
updateInMaterialDetailFromAttributeOption(receiptMaterialDetailDOList, batchFieldNameCache);
}
private void updateInMaterialDetailFromAttributeOption(List<ReceiptMaterialDetailDO> receiptMaterialDetailDOList,
Map<Long, Map<Long, String>> batchFieldNameCache) {
if (CollectionUtils.isEmpty(receiptMaterialDetailDOList)) {
return;
}
LoginUser loginUser = SecurityUtils.getLoginUser(); LoginUser loginUser = SecurityUtils.getLoginUser();
for (ReceiptMaterialDetailDO receiptMaterialDetailDO : receiptMaterialDetailDOList) { for (ReceiptMaterialDetailDO receiptMaterialDetailDO : receiptMaterialDetailDOList) {
Long inUniqueId = receiptMaterialDetailDO.getInUniqueId(); Long inUniqueId = receiptMaterialDetailDO.getInUniqueId();
if (inUniqueId == null) { if (inUniqueId == null) {
// 处理子项
List<ReceiptMaterialDetailDO> children = receiptMaterialDetailDO.getChildren();
if (!CollectionUtils.isEmpty(children)) {
updateInMaterialDetailFromAttributeOption(children, batchFieldNameCache);
}
continue; continue;
} }
List<MaterialMoreDetailDO> materialMoreDetailList = receiptMaterialDetailDO.getMaterialMoreDetailList(); List<MaterialMoreDetailDO> materialMoreDetailList = receiptMaterialDetailDO.getMaterialMoreDetailList();
if (CollectionUtils.isEmpty(materialMoreDetailList)) { if (CollectionUtils.isEmpty(materialMoreDetailList)) {
// 处理子项
List<ReceiptMaterialDetailDO> children = receiptMaterialDetailDO.getChildren();
if (!CollectionUtils.isEmpty(children)) {
updateInMaterialDetailFromAttributeOption(children, batchFieldNameCache);
}
continue; continue;
} }
// 构建UpdateWrapper
// 构建UpdateWrapper(用列名动态set
UpdateWrapper<InMaterialDetail> updateWrapper = new UpdateWrapper<>(); UpdateWrapper<InMaterialDetail> updateWrapper = new UpdateWrapper<>();
updateWrapper.lambda().eq(InMaterialDetail::getUniqueId, inUniqueId); updateWrapper.eq("unique_id", inUniqueId);
boolean hasUpdate = false; boolean hasUpdate = false;
// 遍历materialMoreDetailList,根据attributeOption映射到对应字段 // 构建 batchDetailId -> fieldName 映射(按物料维度缓存)
Map<Long, String> batchDetailIdToFieldName = null;
Long materialBaseInfoId = receiptMaterialDetailDO.getMaterialBaseInfoId();
if (materialBaseInfoId != null) {
batchDetailIdToFieldName = batchFieldNameCache.get(materialBaseInfoId);
if (batchDetailIdToFieldName == null) {
batchDetailIdToFieldName = loadBatchDetailIdToFieldName(materialBaseInfoId);
batchFieldNameCache.put(materialBaseInfoId, batchDetailIdToFieldName);
}
}
for (MaterialMoreDetailDO materialMoreDetail : materialMoreDetailList) { for (MaterialMoreDetailDO materialMoreDetail : materialMoreDetailList) {
String attributeOption = materialMoreDetail.getAttributeOption();
String attributeValue = materialMoreDetail.getAttributeValue(); String attributeValue = materialMoreDetail.getAttributeValue();
if (StringUtils.isBlank(attributeValue)) {
// 如果attributeValue为空,跳过
if (attributeValue == null || attributeValue.trim().isEmpty()) {
continue; continue;
} }
// 根据attributeOption映射到入库单明细表的字段 String fieldName = null;
if ("InvoiceNo".equalsIgnoreCase(attributeOption)) { Long batchDetailId = materialMoreDetail.getBatchDetailId();
updateWrapper.lambda().set(InMaterialDetail::getInvoiceNo, attributeValue); if (batchDetailId != null && batchDetailIdToFieldName != null) {
hasUpdate = true; fieldName = batchDetailIdToFieldName.get(batchDetailId);
} else if ("BatchRefNo".equalsIgnoreCase(attributeOption)) {
updateWrapper.lambda().set(InMaterialDetail::getBatchRefNo, attributeValue);
hasUpdate = true;
} else if ("SheetRefNo".equalsIgnoreCase(attributeOption)) {
updateWrapper.lambda().set(InMaterialDetail::getSheetRefNo, attributeValue);
hasUpdate = true;
} else if ("BoxPalletNo".equalsIgnoreCase(attributeOption)) {
updateWrapper.lambda().set(InMaterialDetail::getBoxPalletNo, attributeValue);
hasUpdate = true;
} }
// 兼容旧逻辑:fieldName取不到时用attributeOption(例如 InvoiceNo -> invoiceNo
if (StringUtils.isBlank(fieldName)) {
String attributeOption = materialMoreDetail.getAttributeOption();
fieldName = lowerFirst(attributeOption);
}
if (StringUtils.isBlank(fieldName)) {
continue;
}
// camelCase -> snake_case 列名
String columnName = camelToSnake(fieldName);
if (StringUtils.isBlank(columnName)) {
continue;
}
// 数字字段尝试转BigDecimal,其它按String
Object val = coerceInMaterialDetailValue(fieldName, attributeValue);
updateWrapper.set(columnName, val);
hasUpdate = true;
} }
// 如果有字段需要更新,则执行更新
if (hasUpdate) { if (hasUpdate) {
updateWrapper.lambda() updateWrapper
.set(InMaterialDetail::getUpdateBy, loginUser.getUserid()) .set("update_by", loginUser.getUserid())
.set(InMaterialDetail::getUpdateByName, loginUser.getUsername()) .set("update_by_name", loginUser.getUsername())
.set(InMaterialDetail::getUpdateTime, new Date()); .set("update_time", new Date());
iInMaterialDetailService.update(updateWrapper); iInMaterialDetailService.update(updateWrapper);
} }
// 处理子项 // 处理子项
List<ReceiptMaterialDetailDO> children = receiptMaterialDetailDO.getChildren(); List<ReceiptMaterialDetailDO> children = receiptMaterialDetailDO.getChildren();
if (!CollectionUtils.isEmpty(children)) { if (!CollectionUtils.isEmpty(children)) {
updateInMaterialDetailFromAttributeOption(children); updateInMaterialDetailFromAttributeOption(children, batchFieldNameCache);
} }
} }
} }
private Map<Long, String> loadBatchDetailIdToFieldName(Long materialBaseInfoId) {
Map<Long, String> result = new HashMap<>();
try {
AjaxResult ajaxResult = systemServiceFeign.getBatchDetailListByMaterialBaseInfoId(materialBaseInfoId);
if (ajaxResult == null || !"200".equals(String.valueOf(ajaxResult.get("code")))) {
return result;
}
List<BatchDetailFeignPO> list = JSON.parseArray(JSON.toJSONString(ajaxResult.get("data")), BatchDetailFeignPO.class);
if (CollectionUtils.isEmpty(list)) {
return result;
}
for (BatchDetailFeignPO po : list) {
if (po.getBatchDetailId() == null) {
continue;
}
String fieldName = po.getFieldName();
if (StringUtils.isBlank(fieldName)) {
// 兼容:fieldName为空时尝试用attributeOption
fieldName = lowerFirst(po.getAttributeOption());
}
if (StringUtils.isNotBlank(fieldName)) {
result.put(po.getBatchDetailId(), fieldName);
}
}
} catch (Exception e) {
log.warn("加载批次属性fieldName失败,materialBaseInfoId{},错误:{}", materialBaseInfoId, e.getMessage());
}
return result;
}
private String lowerFirst(String s) {
if (StringUtils.isBlank(s)) {
return null;
}
if (s.length() == 1) {
return s.toLowerCase();
}
return s.substring(0, 1).toLowerCase() + s.substring(1);
}
private String camelToSnake(String camel) {
if (StringUtils.isBlank(camel)) {
return null;
}
StringBuilder sb = new StringBuilder();
for (int i = 0; i < camel.length(); i++) {
char c = camel.charAt(i);
if (Character.isUpperCase(c)) {
if (i > 0) {
sb.append('_');
}
sb.append(Character.toLowerCase(c));
} else {
sb.append(c);
}
}
return sb.toString();
}
private Object coerceInMaterialDetailValue(String fieldName, String raw) {
if (raw == null) {
return null;
}
// 这些字段在库里通常是NUMBER
if ("liter".equals(fieldName)
|| "declaredQuantity".equals(fieldName)
|| "boxCount".equals(fieldName)
|| "pieceCount".equals(fieldName)
|| "totalNetWeight".equals(fieldName)
|| "totalGrossWeight".equals(fieldName)
|| "totalVolume".equals(fieldName)
|| "totalArea".equals(fieldName)) {
try {
return new BigDecimal(raw.trim());
} catch (Exception ignored) {
return raw;
}
}
return raw;
}
/** /**
* @description 从入库单明细表中获取InvoiceNo、BatchRefNo、SheetRefNo、BoxPalletNo字段的值并填充到materialMoreDetailList的attributeValue中 * @description 从入库单明细表中获取InvoiceNo、BatchRefNo、SheetRefNo、BoxPalletNo字段的值并填充到materialMoreDetailList的attributeValue中
* @author Auto * @author Auto
@@ -12,6 +12,8 @@ import lombok.Data;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.List; import java.util.List;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
/** /**
@@ -252,6 +254,37 @@ public class ReceiptMaterialDetailPO extends BaseVOEntity {
@Excel(name = "箱号/卡板号") @Excel(name = "箱号/卡板号")
private String boxPalletNo; private String boxPalletNo;
@ApiModelProperty("生产日期")
@Excel(name = "生产日期", width = 30, dateFormat = "yyyy-MM-dd")
@JsonFormat(pattern = "yyyy-MM-dd")
private Date productionDate;
@ApiModelProperty("过期日期")
@Excel(name = "过期日期", width = 30, dateFormat = "yyyy-MM-dd")
@JsonFormat(pattern = "yyyy-MM-dd")
private Date expiryDate;
@ApiModelProperty("存货日期")
@Excel(name = "存货日期", width = 30, dateFormat = "yyyy-MM-dd")
@JsonFormat(pattern = "yyyy-MM-dd")
private Date inventoryDate;
@ApiModelProperty("扩展属性1")
@Excel(name = "扩展属性1")
private String extAttr1;
@ApiModelProperty("扩展属性2")
@Excel(name = "扩展属性2")
private String extAttr2;
@ApiModelProperty("扩展属性3")
@Excel(name = "扩展属性3")
private String extAttr3;
@ApiModelProperty("扩展属性4")
@Excel(name = "扩展属性4")
private String extAttr4;
@ApiModelProperty("物料明细子集") @ApiModelProperty("物料明细子集")
private List<ReceiptMaterialDetailPO> children; private List<ReceiptMaterialDetailPO> children;
@@ -11,6 +11,8 @@ import lombok.Data;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.List; import java.util.List;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
/** /**
@@ -218,6 +220,69 @@ public class ReceiptMaterialDetailDO extends BaseVOEntity {
@Excel(name = "是否生成上架单: 1-是 2-否") @Excel(name = "是否生成上架单: 1-是 2-否")
private Integer genStockShelf; private Integer genStockShelf;
@ApiModelProperty("总净重(KG)")
@Excel(name = "总净重(KG)")
private BigDecimal totalNetWeight;
@ApiModelProperty("总毛重(KG)")
@Excel(name = "总毛重(KG)")
private BigDecimal totalGrossWeight;
@ApiModelProperty("总体积(CBM)")
@Excel(name = "总体积(CBM)")
private BigDecimal totalVolume;
@ApiModelProperty("总面积(SQM)")
@Excel(name = "总面积(SQM)")
private BigDecimal totalArea;
@ApiModelProperty("Invoice No.")
@Excel(name = "Invoice No.")
private String invoiceNo;
@ApiModelProperty("Batch Ref NO's")
@Excel(name = "Batch Ref NO's")
private String batchRefNo;
@ApiModelProperty("Sheet Ref NO's")
@Excel(name = "Sheet Ref NO's")
private String sheetRefNo;
@ApiModelProperty("箱号/卡板号")
@Excel(name = "箱号/卡板号")
private String boxPalletNo;
@ApiModelProperty("生产日期")
@Excel(name = "生产日期", width = 30, dateFormat = "yyyy-MM-dd")
@JsonFormat(pattern = "yyyy-MM-dd")
private Date productionDate;
@ApiModelProperty("过期日期")
@Excel(name = "过期日期", width = 30, dateFormat = "yyyy-MM-dd")
@JsonFormat(pattern = "yyyy-MM-dd")
private Date expiryDate;
@ApiModelProperty("存货日期")
@Excel(name = "存货日期", width = 30, dateFormat = "yyyy-MM-dd")
@JsonFormat(pattern = "yyyy-MM-dd")
private Date inventoryDate;
@ApiModelProperty("扩展属性1")
@Excel(name = "扩展属性1")
private String extAttr1;
@ApiModelProperty("扩展属性2")
@Excel(name = "扩展属性2")
private String extAttr2;
@ApiModelProperty("扩展属性3")
@Excel(name = "扩展属性3")
private String extAttr3;
@ApiModelProperty("扩展属性4")
@Excel(name = "扩展属性4")
private String extAttr4;
@ApiModelProperty(name = "组织ID集合") @ApiModelProperty(name = "组织ID集合")
private List<Long> organizationIdList; private List<Long> organizationIdList;
@@ -40,6 +40,7 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import java.lang.reflect.Field;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@@ -297,6 +298,11 @@ public class StockInOrderDomainService {
ReceiptMaterialDetailDO receiptMaterialDetailDO = new ReceiptMaterialDetailDO(); ReceiptMaterialDetailDO receiptMaterialDetailDO = new ReceiptMaterialDetailDO();
BeanUtils.copyProperties(inMaterialDetailPO, receiptMaterialDetailDO); BeanUtils.copyProperties(inMaterialDetailPO, receiptMaterialDetailDO);
receiptMaterialDetailDO.setInUniqueId(inMaterialDetailPO.getUniqueId()); receiptMaterialDetailDO.setInUniqueId(inMaterialDetailPO.getUniqueId());
// 复制特定字段:生产日期、过期日期、存货日期、批次参考号、单据参考号、箱号/卡板号、扩展属性1-4
// 这些字段可能使用驼峰命名或下划线命名,使用反射获取和设置
copySpecialFields(inMaterialDetailPO, receiptMaterialDetailDO);
receiptMaterialDetailDOList.add(receiptMaterialDetailDO); receiptMaterialDetailDOList.add(receiptMaterialDetailDO);
}); });
stockReceiptOrderDO.setMaterialDetailList(receiptMaterialDetailDOList); stockReceiptOrderDO.setMaterialDetailList(receiptMaterialDetailDOList);
@@ -452,4 +458,196 @@ public class StockInOrderDomainService {
} }
} }
/**
* @description 复制特定字段从入库单明细到收货明细
* 包括:生产日期、过期日期、存货日期、批次参考号、单据参考号、箱号/卡板号、扩展属性1-4
* @author gen
* @date 2026/1/29
* @param source 源对象(InMaterialDetailPO
* @param target 目标对象(ReceiptMaterialDetailDO
*/
private void copySpecialFields(InMaterialDetailPO source, ReceiptMaterialDetailDO target) {
if (source == null || target == null) {
return;
}
try {
// 字段映射:源字段名 -> 目标字段名(支持驼峰和下划线两种命名方式)
Map<String, String> fieldMappings = new HashMap<>();
fieldMappings.put("productionDate", "productionDate");
fieldMappings.put("production_date", "productionDate");
fieldMappings.put("expiryDate", "expiryDate");
fieldMappings.put("expiry_date", "expiryDate");
fieldMappings.put("inventoryDate", "inventoryDate");
fieldMappings.put("inventory_date", "inventoryDate");
fieldMappings.put("batchRefNo", "batchRefNo");
fieldMappings.put("batch_ref_no", "batchRefNo");
fieldMappings.put("sheetRefNo", "sheetRefNo");
fieldMappings.put("sheet_ref_no", "sheetRefNo");
fieldMappings.put("boxPalletNo", "boxPalletNo");
fieldMappings.put("box_pallet_no", "boxPalletNo");
fieldMappings.put("extAttr1", "extAttr1");
fieldMappings.put("ext_attr_1", "extAttr1");
fieldMappings.put("extAttr2", "extAttr2");
fieldMappings.put("ext_attr_2", "extAttr2");
fieldMappings.put("extAttr3", "extAttr3");
fieldMappings.put("ext_attr_3", "extAttr3");
fieldMappings.put("extAttr4", "extAttr4");
fieldMappings.put("ext_attr_4", "extAttr4");
// 使用反射复制字段值
for (Map.Entry<String, String> entry : fieldMappings.entrySet()) {
String sourceFieldName = entry.getKey();
String targetFieldName = entry.getValue();
// 尝试从源对象获取字段值(支持驼峰和下划线命名)
Object value = getFieldValue(source, sourceFieldName);
if (value != null) {
// 设置到目标对象(使用驼峰命名)
setFieldValue(target, targetFieldName, value);
log.info("复制字段成功:{} -> {},值:{}", sourceFieldName, targetFieldName, value);
} else {
log.debug("源对象字段 {} 的值为 null,跳过复制", sourceFieldName);
}
}
} catch (Exception e) {
log.warn("复制特定字段时发生异常,源对象:{},目标对象:{},错误:{}",
source.getClass().getSimpleName(), target.getClass().getSimpleName(), e.getMessage());
}
}
/**
* @description 使用反射获取字段值(支持驼峰和下划线命名)
*/
private Object getFieldValue(Object obj, String fieldName) {
if (obj == null || StringUtils.isBlank(fieldName)) {
return null;
}
try {
Class<?> clazz = obj.getClass();
Field field = null;
// 先尝试直接获取字段(驼峰命名)
try {
field = clazz.getDeclaredField(fieldName);
} catch (NoSuchFieldException e) {
// 如果失败,尝试转换为驼峰命名(如果是下划线命名)
if (fieldName.contains("_")) {
String camelCaseName = snakeToCamel(fieldName);
try {
field = clazz.getDeclaredField(camelCaseName);
} catch (NoSuchFieldException ex) {
// 继续查找父类
}
}
}
// 如果当前类找不到,查找父类
if (field == null) {
Class<?> currentClass = clazz.getSuperclass();
while (currentClass != null && field == null) {
try {
field = currentClass.getDeclaredField(fieldName);
} catch (NoSuchFieldException e) {
if (fieldName.contains("_")) {
String camelCaseName = snakeToCamel(fieldName);
try {
field = currentClass.getDeclaredField(camelCaseName);
} catch (NoSuchFieldException ex) {
// 继续查找父类
}
}
}
currentClass = currentClass.getSuperclass();
}
}
if (field != null) {
field.setAccessible(true);
return field.get(obj);
}
} catch (Exception e) {
log.debug("获取字段值失败,对象:{},字段名:{},错误:{}",
obj.getClass().getSimpleName(), fieldName, e.getMessage());
}
return null;
}
/**
* @description 使用反射设置字段值
*/
private void setFieldValue(Object obj, String fieldName, Object value) {
if (obj == null || StringUtils.isBlank(fieldName)) {
return;
}
try {
Class<?> clazz = obj.getClass();
Field field = null;
// 先尝试直接获取字段(驼峰命名)
try {
field = clazz.getDeclaredField(fieldName);
} catch (NoSuchFieldException e) {
// 如果失败,查找父类
Class<?> currentClass = clazz.getSuperclass();
while (currentClass != null && field == null) {
try {
field = currentClass.getDeclaredField(fieldName);
} catch (NoSuchFieldException ex) {
// 继续查找父类
}
currentClass = currentClass.getSuperclass();
}
}
if (field != null) {
field.setAccessible(true);
// 类型转换:如果值类型不匹配,尝试转换
Class<?> fieldType = field.getType();
if (value != null && !fieldType.isAssignableFrom(value.getClass())) {
// 尝试类型转换
if (fieldType == Date.class && value instanceof String) {
// 字符串转日期(简单处理,实际可能需要更复杂的解析)
value = null; // 暂时不转换,避免解析错误
} else if (fieldType == String.class) {
value = String.valueOf(value);
}
}
if (value != null && fieldType.isAssignableFrom(value.getClass())) {
field.set(obj, value);
}
}
} catch (Exception e) {
log.debug("设置字段值失败,对象:{},字段名:{},值:{},错误:{}",
obj.getClass().getSimpleName(), fieldName, value, e.getMessage());
}
}
/**
* @description 下划线命名转驼峰命名
*/
private String snakeToCamel(String snakeCase) {
if (StringUtils.isBlank(snakeCase)) {
return snakeCase;
}
String[] parts = snakeCase.split("_");
if (parts.length == 0) {
return snakeCase;
}
StringBuilder camelCase = new StringBuilder(parts[0].toLowerCase());
for (int i = 1; i < parts.length; i++) {
if (parts[i].length() > 0) {
camelCase.append(parts[i].substring(0, 1).toUpperCase())
.append(parts[i].substring(1).toLowerCase());
}
}
return camelCase.toString();
}
} }
@@ -73,6 +73,13 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="totalVolume" column="TOTAL_VOLUME" /> <result property="totalVolume" column="TOTAL_VOLUME" />
<result property="totalArea" column="TOTAL_AREA" /> <result property="totalArea" column="TOTAL_AREA" />
<result property="totalPrice" column="TOTAL_PRICE" /> <result property="totalPrice" column="TOTAL_PRICE" />
<result property="productionDate" column="PRODUCTION_DATE" />
<result property="expiryDate" column="EXPIRY_DATE" />
<result property="inventoryDate" column="INVENTORY_DATE" />
<result property="extAttr1" column="EXT_ATTR_1" />
<result property="extAttr2" column="EXT_ATTR_2" />
<result property="extAttr3" column="EXT_ATTR_3" />
<result property="extAttr4" column="EXT_ATTR_4" />
</resultMap> </resultMap>
@@ -91,7 +98,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
a.level, a.parent_unique_id, a.remark, a.create_time, a.create_by,a.create_by_name, a.update_time, a.update_by, a.update_by_name, a.del_flag, a.over_stock_id, a.over_stock_status, a.over_stock_type, a.level, a.parent_unique_id, a.remark, a.create_time, a.create_by,a.create_by_name, a.update_time, a.update_by, a.update_by_name, a.del_flag, a.over_stock_id, a.over_stock_status, a.over_stock_type,
a.SPECIFICATION_MODEL, a.SKU_CODE, a.INVOICE_NO, a.LITER, a.DIMENSIONS, a.DECLARATION_UNIT, a.BATCH_REF_NO, a.SHEET_REF_NO, a.SPECIFICATION_MODEL, a.SKU_CODE, a.INVOICE_NO, a.LITER, a.DIMENSIONS, a.DECLARATION_UNIT, a.BATCH_REF_NO, a.SHEET_REF_NO,
a.ORIGIN_COUNTRY, a.BOX_PALLET_NO, a.CURRENCY, a.UNIT, a.DECLARED_QUANTITY, a.BOX_COUNT, a.PIECE_COUNT, a.INBOUND_QUANTITY, a.ORIGIN_COUNTRY, a.BOX_PALLET_NO, a.CURRENCY, a.UNIT, a.DECLARED_QUANTITY, a.BOX_COUNT, a.PIECE_COUNT, a.INBOUND_QUANTITY,
a.TOTAL_NET_WEIGHT, a.TOTAL_GROSS_WEIGHT, a.TOTAL_VOLUME, a.TOTAL_AREA, a.TOTAL_PRICE a.TOTAL_NET_WEIGHT, a.TOTAL_GROSS_WEIGHT, a.TOTAL_VOLUME, a.TOTAL_AREA, a.TOTAL_PRICE,
a.PRODUCTION_DATE, a.EXPIRY_DATE, a.INVENTORY_DATE, a.EXT_ATTR_1, a.EXT_ATTR_2, a.EXT_ATTR_3, a.EXT_ATTR_4
,CASE ,CASE
WHEN (a.quantity - a.receipt_quantity) &lt; 0 THEN 0 WHEN (a.quantity - a.receipt_quantity) &lt; 0 THEN 0
ELSE (a.quantity - a.receipt_quantity) ELSE (a.quantity - a.receipt_quantity)
@@ -62,10 +62,17 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="totalGrossWeight" column="total_gross_weight" /> <result property="totalGrossWeight" column="total_gross_weight" />
<result property="totalVolume" column="total_volume" /> <result property="totalVolume" column="total_volume" />
<result property="totalArea" column="total_area" /> <result property="totalArea" column="total_area" />
<result property="invoiceNo" column="invoice_no" /> <result property="invoiceNo" column="INVOICE_NO" />
<result property="batchRefNo" column="batch_ref_no" /> <result property="batchRefNo" column="BATCH_REF_NO" />
<result property="sheetRefNo" column="sheet_ref_no" /> <result property="sheetRefNo" column="SHEET_REF_NO" />
<result property="boxPalletNo" column="box_pallet_no" /> <result property="boxPalletNo" column="BOX_PALLET_NO" />
<result property="productionDate" column="PRODUCTION_DATE" />
<result property="expiryDate" column="EXPIRY_DATE" />
<result property="inventoryDate" column="INVENTORY_DATE" />
<result property="extAttr1" column="EXT_ATTR_1" />
<result property="extAttr2" column="EXT_ATTR_2" />
<result property="extAttr3" column="EXT_ATTR_3" />
<result property="extAttr4" column="EXT_ATTR_4" />
</resultMap> </resultMap>
@@ -76,7 +83,21 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
a.BAR_CODE, a.BAR_CODE,
c.SPECIFICATION_MODEL, c.SPECIFICATION_MODEL,
a.QUANTITY, a.ALREADY_RECEIPT_QUANTITY, a.RECEIPT_QUANTITY, a.RECEIPT_STATUS, a.PACK_ID, a.PACK_CODE, a.PACK_NAME, a.PACK_DETAIL_ID, a.UNIT_CODE, a.UNIT_NAME, a.UNIT_NUMBER, a.MATERIAL_WAREHOUSE_CONTROL_ID, a.SERIAL_NUMBER_MANAGE, a.QUALITY_INSPECTION_MANAGE, a.QUALITY_INSPECTION_STAGE, a.QUALITY_INSPECTION_RULE, a.QUALITY_INSPECTION_RATIO, a.QUALITY_INSPECTION_TERM, a.QUALITY_INSPECTION_RESULTS, a.ALLOW_OVERCHARGE, a.OVERCHARGE_RATIO, a.BATCH_NUMBER, a.MATERIAL_STATUS_CODE, a.MATERIAL_STATUS_NAME, a.DISTRIBUTE_RULE_ID, a.DISTRIBUTE_RULE_CODE, a.DISTRIBUTE_RULE_NAME, a.CONTAINER_ID, a.CONTAINER_CODE, a.CONTAINER_TYPE, a.CONTAINER_TYPE_NAME, a.LEVEL, a.PARENT_UNIQUE_ID, a.IN_UNIQUE_ID, a.ALLOW_MODIFY, a.REMARK, a.CREATE_TIME, a.CREATE_BY, a.CREATE_BY_NAME, a.UPDATE_TIME, a.UPDATE_BY, a.UPDATE_BY_NAME, a.GEN_STOCK_SHELF, a.DEL_FLAG, a.QUANTITY, a.ALREADY_RECEIPT_QUANTITY, a.RECEIPT_QUANTITY, a.RECEIPT_STATUS, a.PACK_ID, a.PACK_CODE, a.PACK_NAME, a.PACK_DETAIL_ID, a.UNIT_CODE, a.UNIT_NAME, a.UNIT_NUMBER, a.MATERIAL_WAREHOUSE_CONTROL_ID, a.SERIAL_NUMBER_MANAGE, a.QUALITY_INSPECTION_MANAGE, a.QUALITY_INSPECTION_STAGE, a.QUALITY_INSPECTION_RULE, a.QUALITY_INSPECTION_RATIO, a.QUALITY_INSPECTION_TERM, a.QUALITY_INSPECTION_RESULTS, a.ALLOW_OVERCHARGE, a.OVERCHARGE_RATIO, a.BATCH_NUMBER, a.MATERIAL_STATUS_CODE, a.MATERIAL_STATUS_NAME, a.DISTRIBUTE_RULE_ID, a.DISTRIBUTE_RULE_CODE, a.DISTRIBUTE_RULE_NAME, a.CONTAINER_ID, a.CONTAINER_CODE, a.CONTAINER_TYPE, a.CONTAINER_TYPE_NAME, a.LEVEL, a.PARENT_UNIQUE_ID, a.IN_UNIQUE_ID, a.ALLOW_MODIFY, a.REMARK, a.CREATE_TIME, a.CREATE_BY, a.CREATE_BY_NAME, a.UPDATE_TIME, a.UPDATE_BY, a.UPDATE_BY_NAME, a.GEN_STOCK_SHELF, a.DEL_FLAG,
b.TOTAL_NET_WEIGHT, b.TOTAL_GROSS_WEIGHT, b.TOTAL_VOLUME, b.TOTAL_AREA, b.INVOICE_NO, b.BATCH_REF_NO, b.SHEET_REF_NO, b.BOX_PALLET_NO ifnull(a.TOTAL_NET_WEIGHT, b.TOTAL_NET_WEIGHT) TOTAL_NET_WEIGHT,
ifnull(a.TOTAL_GROSS_WEIGHT, b.TOTAL_GROSS_WEIGHT) TOTAL_GROSS_WEIGHT,
ifnull(a.TOTAL_VOLUME, b.TOTAL_VOLUME) TOTAL_VOLUME,
ifnull(a.TOTAL_AREA, b.TOTAL_AREA) TOTAL_AREA,
ifnull(a.INVOICE_NO, b.INVOICE_NO) INVOICE_NO,
ifnull(a.BATCH_REF_NO, b.BATCH_REF_NO) BATCH_REF_NO,
ifnull(a.SHEET_REF_NO, b.SHEET_REF_NO) SHEET_REF_NO,
ifnull(a.BOX_PALLET_NO, b.BOX_PALLET_NO) BOX_PALLET_NO,
ifnull(a.PRODUCTION_DATE, b.PRODUCTION_DATE) PRODUCTION_DATE,
ifnull(a.EXPIRY_DATE, b.EXPIRY_DATE) EXPIRY_DATE,
ifnull(a.INVENTORY_DATE, b.INVENTORY_DATE) INVENTORY_DATE,
ifnull(a.EXT_ATTR_1, b.EXT_ATTR_1) EXT_ATTR_1,
ifnull(a.EXT_ATTR_2, b.EXT_ATTR_2) EXT_ATTR_2,
ifnull(a.EXT_ATTR_3, b.EXT_ATTR_3) EXT_ATTR_3,
ifnull(a.EXT_ATTR_4, b.EXT_ATTR_4) EXT_ATTR_4
from RECEIPT_MATERIAL_DETAIL a from RECEIPT_MATERIAL_DETAIL a
LEFT JOIN IN_MATERIAL_DETAIL b ON a.IN_UNIQUE_ID = b.UNIQUE_ID AND b.DEL_FLAG = 1 LEFT JOIN IN_MATERIAL_DETAIL b ON a.IN_UNIQUE_ID = b.UNIQUE_ID AND b.DEL_FLAG = 1
LEFT JOIN MATERIAL_BASE_INFO c ON a.MATERIAL_BASE_INFO_ID = c.MATERIAL_BASE_INFO_ID AND c.DEL_FLAG = 1 LEFT JOIN MATERIAL_BASE_INFO c ON a.MATERIAL_BASE_INFO_ID = c.MATERIAL_BASE_INFO_ID AND c.DEL_FLAG = 1