库存调整查询修改;

This commit is contained in:
王奎兴
2026-02-04 09:40:54 +08:00
parent 4cbb338425
commit eb46fb1cc3
4 changed files with 356 additions and 7 deletions
@@ -1,17 +1,31 @@
package com.mhd.wms.application.service.inventoryAdjustmentRecord;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson2.JSONObject;
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.inventoryAdjustmentRecord.repository.po.InventoryAdjustmentRecordPO;
import com.mhd.wms.domain.inventoryAdjustmentRecord.repository.todo.InventoryAdjustmentRecordDO;
import com.mhd.wms.domain.inventoryAdjustmentRecord.service.InventoryAdjustmentRecordDomainService;
import com.mhd.wms.domain.materialInventory.repository.mapper.MaterialInventoryMapper;
import com.mhd.wms.domain.materialInventory.repository.po.MaterialInventoryPO;
import com.mhd.wms.domain.materialMoreDetail.repository.po.MaterialMoreDetailPO;
import com.mhd.wms.domain.statementStatistics.po.ImmediateInventoryPO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.util.List;
import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;
/**
* 库存调整记录ApplicationService
@@ -24,6 +38,10 @@ import java.util.List;
public class InventoryAdjustmentRecordApplicationService {
@Autowired
private InventoryAdjustmentRecordDomainService inventoryAdjustmentRecordDomainService;
@Autowired
private MaterialInventoryMapper materialInventoryMapper;
@Autowired
private SystemServiceFeign systemServiceFeign;
/**
@@ -40,7 +58,262 @@ public class InventoryAdjustmentRecordApplicationService {
AssociationWarehouseCacheDO associationWarehouseCacheDO = SystemServiceCacheUtil.getAssociationWarehouseCache(loginUser.getUserid());
inventoryAdjustmentRecordDO.setWarehouseId(associationWarehouseCacheDO.getWarehouseId());
}
return inventoryAdjustmentRecordDomainService.queryList(inventoryAdjustmentRecordDO);
List<InventoryAdjustmentRecordPO> inventoryAdjustmentRecordPOS = inventoryAdjustmentRecordDomainService.queryList(inventoryAdjustmentRecordDO);
// 性能优化:批量获取批次属性,避免N+1查询问题
// 1. 收集所有唯一的materialBaseInfoId
Set<Long> materialBaseInfoIdSet = inventoryAdjustmentRecordPOS.stream()
.map(InventoryAdjustmentRecordPO::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(inventoryAdjustmentRecordPOS, batchDetailMap);
return inventoryAdjustmentRecordPOS;
}
/**
* 为物料库存设置更多属性(从库存表中获取值)
* @param materialInventoryPOList 物料库存列表
* @param batchDetailMap 批次属性Map
*/
private void setMaterialMoreDetailListFromInventory(List<InventoryAdjustmentRecordPO> 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);
});
}
/**
* 根据字段名从对象中获取字段值(使用反射)
* @param obj 对象
* @param fieldName 字段名
* @return 字段值(转换为字符串)
*/
private String getFieldValueByFieldName(Object obj, String fieldName) {
if (obj == null || StringUtils.isBlank(fieldName)) {
return null;
}
try {
// 兼容:如果传的是下划线命名(数据库列名风格),先转成驼峰字段名再取
String normalizedFieldName = fieldName;
if (normalizedFieldName.contains("_")) {
normalizedFieldName = snakeToCamel(normalizedFieldName);
}
// 获取对象的Class
Class<?> clazz = obj.getClass();
// 尝试获取字段(包括父类字段)
Field field = null;
Class<?> currentClass = clazz;
while (currentClass != null && field == null) {
try {
field = currentClass.getDeclaredField(normalizedFieldName);
} catch (NoSuchFieldException e) {
currentClass = currentClass.getSuperclass();
}
}
if (field == null) {
log.warn("字段不存在:{}(normalized{}),对象类型:{}", fieldName, normalizedFieldName, clazz.getName());
return null;
}
// 设置可访问
field.setAccessible(true);
// 获取字段值
Object value = field.get(obj);
// 转换为字符串
if (value == null) {
log.info("字段 {} 的值为 null,对象类型:{}", normalizedFieldName, clazz.getName());
return null;
}
String result;
// 处理日期类型字段
if (value instanceof Date) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
result = sdf.format((Date) value);
} else {
result = value.toString();
}
log.info("通过反射获取字段值成功,字段名:{}(原始:{}),值:{},对象类型:{}",
normalizedFieldName, fieldName, result, clazz.getName());
return result;
} catch (Exception e) {
log.warn("通过反射获取字段值失败,字段名:{},对象类型:{},错误:{}", fieldName, obj.getClass().getName(), e.getMessage());
return null;
}
}
private String snakeToCamel(String snake) {
if (StringUtils.isBlank(snake)) {
return null;
}
String s = snake.trim().toLowerCase();
StringBuilder sb = new StringBuilder();
boolean upperNext = false;
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (c == '_') {
upperNext = true;
continue;
}
if (upperNext) {
sb.append(Character.toUpperCase(c));
upperNext = false;
} else {
sb.append(c);
}
}
return sb.toString();
}
/**
* 根据批次标签(batchLabels)映射到字段名
* @param batchLabel 批次标签
* @return 字段名,如果找不到映射则返回null
*/
private String getFieldNameByBatchLabel(String batchLabel) {
if (StringUtils.isBlank(batchLabel)) {
return null;
}
// 批次标签到字段名的映射关系
Map<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;
}
@@ -99,4 +372,4 @@ public class InventoryAdjustmentRecordApplicationService {
public Boolean inventoryFrozenForAll(InventoryAdjustmentRecordDO inventoryAdjustmentRecordDO) {
return inventoryAdjustmentRecordDomainService.inventoryFrozenForAll(inventoryAdjustmentRecordDO);
}
}
}
@@ -11,7 +11,6 @@ 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.inventoryStandingDetail.service.InventoryStandingDetailDomainService;
import com.mhd.wms.domain.investigetionManageDetail.repository.po.InvestigetionManageDetailPO;
import com.mhd.wms.domain.materialInventory.repository.mapper.MaterialInventoryMapper;
import com.mhd.wms.domain.materialInventory.repository.po.MaterialInventoryPO;
import com.mhd.wms.domain.materialInventory.service.MaterialInventoryDomainService;
@@ -1,12 +1,17 @@
package com.mhd.wms.domain.inventoryAdjustmentRecord.repository.po;
import com.fasterxml.jackson.annotation.JsonFormat;
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 io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
/**
@@ -147,6 +152,58 @@ public class InventoryAdjustmentRecordPO extends BaseVOEntity {
@Excel(name = "调整后冻结数量")
private BigDecimal freezeAfterQuantity;
@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("扩展字段1")
@Excel(name = "扩展字段1")
private String extAttr1;
@ApiModelProperty("扩展字段2")
@Excel(name = "扩展字段2")
private String extAttr2;
@ApiModelProperty("生产日期")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "生产日期", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private Date productionDate;
@ApiModelProperty("过期日期")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "过期日期", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private Date expiryDate;
@ApiModelProperty("存货日期")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "存货日期", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private Date inventoryDate;
@ApiModelProperty("ExtAttr3")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "ExtAttr3", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private Date extAttr3;
@ApiModelProperty("ExtAttr4")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@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;
}
}
@@ -44,12 +44,32 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="updateBy" column="update_by" />
<result property="updateByName" column="update_by_name" />
<result property="delFlag" column="del_flag" />
<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" />
<result property="productionDate" column="production_date" />
<result property="expiryDate" column="expiry_date" />
<result property="inventoryDate" column="inventory_date" />
<result property="batchRefNo" column="batch_ref_no" />
<result property="sheetRefNo" column="sheet_ref_no" />
<result property="boxPalletNo" column="box_pallet_no" />
</resultMap>
<sql id="selectInventoryAdjustmentRecordPo">
select inventory_adjustment_id, organization_id, organization_name, top_organization_id, material_inventory_id, material_base_info_id, material_code, material_name, bar_code, shipper_id, shipper_name, warehouse_id, warehouse_code, warehouse_name, storage_section_id, storage_code, storage_name, storage_location_id, storage_location_code, storage_location_name, adjust_type, adjust_direction, adjust_before_status_code, adjust_before_status_name, adjust_after_status_code, adjust_after_status_name, inventory_before_quantity, allocation_before_quantity, freeze_before_quantity, inventory_after_quantity, allocation_after_quantity, freeze_after_quantity, create_time, create_by, create_by_name, update_time, update_by, update_by_name, del_flag from inventory_adjustment_record
where del_flag = 1
select a.inventory_adjustment_id, a.organization_id, a.organization_name, a.top_organization_id, a.material_inventory_id,
a.material_base_info_id, a.material_code, a.material_name, a.bar_code, a.shipper_id, a.shipper_name, a.warehouse_id,
a.warehouse_code, a.warehouse_name, a.storage_section_id, a.storage_code, a.storage_name, a.storage_location_id,
a.storage_location_code, a.storage_location_name, a.adjust_type, a.adjust_direction, a.adjust_before_status_code,
a.adjust_before_status_name, a.adjust_after_status_code, a.adjust_after_status_name, a.inventory_before_quantity,
a.allocation_before_quantity, a.freeze_before_quantity, a.inventory_after_quantity, a.allocation_after_quantity,
a.freeze_after_quantity, a.create_time, a.create_by, a.create_by_name, a.update_time, a.update_by, a.update_by_name,
a.del_flag,b.ext_attr_1, b.ext_attr_2, b.ext_attr_3, b.ext_attr_4,
b.production_date, b.expiry_date, b.inventory_date,
b.batch_ref_no, b.sheet_ref_no, b.box_pallet_no
from inventory_adjustment_record a left join material_inventory b on a.material_inventory_id = b.material_inventory_id
where a.del_flag = 1
</sql>
<sql id="selectInventoryAdjustmentRecordPo1">