Merge branch 'wms_dev' into dev

This commit is contained in:
高正杨
2026-02-12 10:57:47 +08:00
2 changed files with 180 additions and 65 deletions
@@ -24,6 +24,8 @@ import com.mhd.wms.domain.deliveTask.repository.facade.IDeliveTaskService;
import com.mhd.wms.domain.deliveTask.repository.todo.DeliveTaskDO;
import com.mhd.wms.domain.inMaterialDetail.entity.InMaterialDetail;
import com.mhd.wms.domain.inMaterialDetail.repository.facade.IInMaterialDetailService;
import com.mhd.wms.domain.inMaterialDetail.repository.po.InMaterialDetailPO;
import com.mhd.wms.domain.inMaterialDetail.repository.todo.InMaterialDetailDO;
import com.mhd.wms.domain.inMaterialDetailSerialNumber.repository.facade.IInMaterialDetailSerialNumberService;
import com.mhd.wms.domain.inMaterialDetailSerialNumber.repository.po.InMaterialDetailSerialNumberPO;
import com.mhd.wms.domain.inMaterialDetailSerialNumber.repository.todo.InMaterialDetailSerialNumberDO;
@@ -316,88 +318,199 @@ public class StockShelfOrderDomainService {
}
/**
* @description 上架数量同步更新入库单明细中(累计值)
* @author Auto
* @date 2026/2/11
* 同步更新入库单明细的上架数量、上架状态
* 一条上架单明细的上架数量直接更新到对应的入库单明细
* @param stockShelfOrderDO 上架单DO
*/
private void syncShelvesQuantityToInMaterialDetail(StockShelfOrderDO stockShelfOrderDO) {
List<ShelfMaterialDetailDO> detailDOList = stockShelfOrderDO.getMaterialDetailList();
if (CollectionUtils.isEmpty(detailDOList)) {
if (stockShelfOrderDO == null || StringUtils.isBlank(stockShelfOrderDO.getShelfOrderNumber())) {
return;
}
LoginUser loginUser = SecurityUtils.getLoginUser();
// 收集所有需要同步的入库单明细 uniqueIdinUniqueId
Set<Long> inUniqueIdSet = new HashSet<>();
for (ShelfMaterialDetailDO d : detailDOList) {
// 只处理一级明细(子级是拆分库位的记录,不作为入库单明细的累计口径)
if (d.getLevel() != null && d.getLevel() == 2) {
// 获取入库单号
String inOrderNumber = stockShelfOrderDO.getInOrderNumber();
if (StringUtils.isBlank(inOrderNumber) && stockShelfOrderDO.getShelfOrderId() != null) {
// 如果上架单DO中没有入库单号,从上架单PO中获取
StockShelfOrderPO shelfOrderPO = stockShelfOrderService.getInfo(stockShelfOrderDO.getShelfOrderId());
if (shelfOrderPO != null) {
inOrderNumber = shelfOrderPO.getInOrderNumber();
}
}
if (StringUtils.isBlank(inOrderNumber)) {
log.warn("上架单【{}】没有关联的入库单号,无法同步更新入库单明细", stockShelfOrderDO.getShelfOrderNumber());
return;
}
log.info("开始同步更新入库单【{}】的明细,上架单号:{}", inOrderNumber, stockShelfOrderDO.getShelfOrderNumber());
// 从数据库查询当前上架单的明细(包括已更新的数据,不限制level,查询所有层级)
ShelfMaterialDetailDO shelfMaterialDetailDO = new ShelfMaterialDetailDO();
shelfMaterialDetailDO.setShelfOrderNumber(stockShelfOrderDO.getShelfOrderNumber());
List<ShelfMaterialDetailPO> shelfMaterialDetailPOList = shelfMaterialDetailService.queryList(shelfMaterialDetailDO);
if (CollectionUtils.isEmpty(shelfMaterialDetailPOList)) {
return;
}
LoginUser loginUser = SecurityUtils.getLoginUser();
Date updateTime = new Date();
// 先查询该入库单的所有明细,用于调试和验证
InMaterialDetailDO inMaterialDetailDO = new InMaterialDetailDO();
inMaterialDetailDO.setInOrderNumber(inOrderNumber);
inMaterialDetailDO.setDelFlag(1);
List<InMaterialDetailPO> allInMaterialDetailList = inMaterialDetailService.queryList(inMaterialDetailDO);
// 构建unique_id到入库单明细的映射,用于调试
Map<Long, InMaterialDetailPO> inMaterialDetailMap = new HashMap<>();
if (!CollectionUtils.isEmpty(allInMaterialDetailList)) {
for (InMaterialDetailPO inDetail : allInMaterialDetailList) {
if (inDetail.getUniqueId() != null) {
inMaterialDetailMap.put(inDetail.getUniqueId(), inDetail);
}
}
}
// 遍历所有层级的上架单明细(queryList已返回所有层级,扁平列表)
// 一条上架单明细对应一条入库单明细,直接更新
for (ShelfMaterialDetailPO shelfDetail : shelfMaterialDetailPOList) {
if (shelfDetail == null || shelfDetail.getInUniqueId() == null) {
continue;
}
if (d.getInUniqueId() != null) {
inUniqueIdSet.add(d.getInUniqueId());
Long uniqueId = shelfDetail.getInUniqueId(); // 入库单明细的unique_id
BigDecimal shelvesQuantity = shelfDetail.getShelvesQuantity(); // 上架数量
Integer shelvesStatus = shelfDetail.getShelvesStatus(); // 上架状态
// 先尝试精确匹配
InMaterialDetailPO targetInDetail = inMaterialDetailMap.get(uniqueId);
// 如果精确匹配失败,尝试模糊匹配(因为可能存在精度丢失问题)
if (targetInDetail == null && !inMaterialDetailMap.isEmpty()) {
// 将uniqueId转换为字符串,取前15位进行模糊匹配
String uniqueIdStr = String.valueOf(uniqueId);
if (uniqueIdStr.length() > 15) {
String prefix = uniqueIdStr.substring(0, 15);
// 收集所有可能匹配的入库单明细
List<InMaterialDetailPO> candidateList = new ArrayList<>();
for (Map.Entry<Long, InMaterialDetailPO> entry : inMaterialDetailMap.entrySet()) {
String actualUniqueIdStr = String.valueOf(entry.getKey());
if (actualUniqueIdStr.startsWith(prefix)) {
candidateList.add(entry.getValue());
}
}
if (inUniqueIdSet.isEmpty()) {
return;
// 如果找到多个候选,使用额外字段进行精确匹配
if (candidateList.size() == 1) {
targetInDetail = candidateList.get(0);
uniqueId = targetInDetail.getUniqueId();
log.info("通过模糊匹配找到入库单明细,上架单明细inUniqueId: {}, 实际uniqueId: {}",
shelfDetail.getInUniqueId(), uniqueId);
} else if (candidateList.size() > 1) {
// 使用批次号、批次参考号、单据参考号、箱号/卡板号进行精确匹配
for (InMaterialDetailPO candidate : candidateList) {
boolean matched = true;
// 批次号匹配
if (StringUtils.isNotBlank(shelfDetail.getBatchNumber()) &&
StringUtils.isNotBlank(candidate.getBatchNumber())) {
if (!shelfDetail.getBatchNumber().equals(candidate.getBatchNumber())) {
matched = false;
}
}
// 从数据库查询所有相关的上架明细,按 inUniqueId 汇总本次上架数量(shelvesQuantity
// 这样可以确保即使一个入库单明细对应多个上架明细(多次上架),也能正确同步
Map<Long, BigDecimal> shelvesQuantityMap = new HashMap<>();
Map<Long, Integer> shelvesStatusMap = new HashMap<>();
for (Long inUniqueId : inUniqueIdSet) {
ShelfMaterialDetailDO queryDO = new ShelfMaterialDetailDO();
queryDO.setInUniqueId(inUniqueId);
queryDO.setLevel(1); // 只查询一级明细
List<ShelfMaterialDetailPO> shelfDetailPOList = shelfMaterialDetailService.queryList(queryDO);
if (!CollectionUtils.isEmpty(shelfDetailPOList)) {
BigDecimal totalShelvesQty = BigDecimal.ZERO;
Integer maxShelvesStatus = null;
for (ShelfMaterialDetailPO po : shelfDetailPOList) {
// 汇总本次上架数量(shelvesQuantity
BigDecimal shelvesQty = po.getShelvesQuantity();
if (shelvesQty != null && shelvesQty.compareTo(BigDecimal.ZERO) > 0) {
totalShelvesQty = totalShelvesQty.add(shelvesQty);
// 批次参考号匹配
if (matched && StringUtils.isNotBlank(shelfDetail.getBatchRefNo()) &&
StringUtils.isNotBlank(candidate.getBatchRefNo())) {
if (!shelfDetail.getBatchRefNo().equals(candidate.getBatchRefNo())) {
matched = false;
}
}
// 取最大的上架状态(3-已上架 > 2-上架中 > 1-待上架)
Integer status = po.getShelvesStatus();
if (status != null) {
if (maxShelvesStatus == null || status > maxShelvesStatus) {
maxShelvesStatus = status;
// 单据参考号匹配
if (matched && StringUtils.isNotBlank(shelfDetail.getSheetRefNo()) &&
StringUtils.isNotBlank(candidate.getSheetRefNo())) {
if (!shelfDetail.getSheetRefNo().equals(candidate.getSheetRefNo())) {
matched = false;
}
}
// 箱号/卡板号匹配
if (matched && StringUtils.isNotBlank(shelfDetail.getBoxPalletNo()) &&
StringUtils.isNotBlank(candidate.getBoxPalletNo())) {
if (!shelfDetail.getBoxPalletNo().equals(candidate.getBoxPalletNo())) {
matched = false;
}
}
if (matched) {
targetInDetail = candidate;
uniqueId = candidate.getUniqueId();
log.info("通过模糊匹配+字段匹配找到入库单明细,上架单明细inUniqueId: {}, 实际uniqueId: {}, 批次号: {}, 批次参考号: {}, 单据参考号: {}, 箱号/卡板号: {}",
shelfDetail.getInUniqueId(), uniqueId,
shelfDetail.getBatchNumber(), shelfDetail.getBatchRefNo(),
shelfDetail.getSheetRefNo(), shelfDetail.getBoxPalletNo());
break;
}
}
// 如果仍然没有匹配到,使用第一个候选(兼容旧逻辑)
if (targetInDetail == null && !candidateList.isEmpty()) {
targetInDetail = candidateList.get(0);
uniqueId = targetInDetail.getUniqueId();
log.warn("通过模糊匹配找到多个候选,使用第一个,上架单明细inUniqueId: {}, 实际uniqueId: {}",
shelfDetail.getInUniqueId(), uniqueId);
}
}
}
}
shelvesQuantityMap.put(inUniqueId, totalShelvesQty);
if (maxShelvesStatus != null) {
shelvesStatusMap.put(inUniqueId, maxShelvesStatus);
}
}
if (targetInDetail == null) {
log.warn("入库单明细不存在,uniqueId: {}, inOrderNumber: {}, 上架单明细uniqueId: {}, 入库单明细实际uniqueId列表: {}",
shelfDetail.getInUniqueId(), inOrderNumber, shelfDetail.getUniqueId(),
inMaterialDetailMap.keySet());
continue;
}
// 更新入库单明细的上架数量和上架状态
Date now = new Date();
for (Long inUniqueId : inUniqueIdSet) {
BigDecimal shelvesQty = shelvesQuantityMap.getOrDefault(inUniqueId, BigDecimal.ZERO);
Integer shelvesStatus = shelvesStatusMap.get(inUniqueId);
UpdateWrapper<InMaterialDetail> updateWrapper = new UpdateWrapper<>();
updateWrapper.eq("unique_id", uniqueId);
updateWrapper.eq("in_order_number", inOrderNumber);
updateWrapper.eq("del_flag", 1);
// 仅更新与上架相关字段
inMaterialDetailService.update(null, new UpdateWrapper<InMaterialDetail>().lambda()
.set(InMaterialDetail::getShelvesQuantity, shelvesQty)
.set(shelvesStatus != null, InMaterialDetail::getShelvesStatus, shelvesStatus)
.set(InMaterialDetail::getUpdateBy, loginUser.getUserid())
.set(InMaterialDetail::getUpdateByName, loginUser.getUsername())
.set(InMaterialDetail::getUpdateTime, now)
.eq(InMaterialDetail::getUniqueId, inUniqueId)
.eq(StringUtils.isNotBlank(stockShelfOrderDO.getInOrderNumber()), InMaterialDetail::getInOrderNumber, stockShelfOrderDO.getInOrderNumber())
);
boolean hasUpdate = false;
// 更新上架数量
if (shelvesQuantity != null) {
updateWrapper.set("shelves_quantity", shelvesQuantity);
hasUpdate = true;
}
// 更新上架状态
if (shelvesStatus != null) {
updateWrapper.set("shelves_status", shelvesStatus);
hasUpdate = true;
}
if (hasUpdate) {
updateWrapper.set("update_by", loginUser.getUserid());
updateWrapper.set("update_by_name", loginUser.getUsername());
updateWrapper.set("update_time", updateTime);
boolean updated = inMaterialDetailService.update(null, updateWrapper);
if (updated) {
log.info("成功更新入库单明细,uniqueId: {}, 上架数量: {}, 上架状态: {}",
uniqueId, shelvesQuantity, shelvesStatus);
} else {
log.warn("更新入库单明细失败,未匹配到记录,uniqueId: {}, inOrderNumber: {}, 上架单明细uniqueId: {}",
uniqueId, inOrderNumber, shelfDetail.getUniqueId());
}
}
}
log.info("完成同步更新入库单【{}】的明细", inOrderNumber);
}
/**
* @description 设置上架单上架需要更新的数据数据
@@ -41,6 +41,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="storageLocationName" column="storage_location_name" />
<result property="level" column="level" />
<result property="parentUniqueId" column="parent_unique_id" />
<result property="inUniqueId" column="in_unique_id" />
<result property="receiptUniqueId" column="receipt_unique_id" />
<result property="remark" column="remark" />
<result property="createTime" column="create_time" />
<result property="createBy" column="create_by" />