feat(wms):上架管理PC上传页默认填充指定上架库位(维他奶仓→2层冷凍A區A01),仅回显不落库

This commit is contained in:
rcx
2026-08-22 14:06:56 +08:00
parent 5fc491f612
commit ab7219e600
4 changed files with 154 additions and 0 deletions
@@ -237,6 +237,12 @@ public interface SystemServiceFeign {
@GetMapping("/warehouseApi/getInfo/{warehouseId}") @GetMapping("/warehouseApi/getInfo/{warehouseId}")
public AjaxResult getWarehouseInfoByWarehouseId(@PathVariable("warehouseId") Long warehouseId); public AjaxResult getWarehouseInfoByWarehouseId(@PathVariable("warehouseId") Long warehouseId);
/**
* 根据warehouseId获取仓库及下库区及库位树(与前端 warehouseApi/getWarehouseChildren 一致,用于PC上架页默认库位回显)
*/
@GetMapping("/warehouseApi/getWarehouseChildren")
public AjaxResult getWarehouseChildren(@RequestParam("warehouseId") Long warehouseId);
/** /**
* 根据storageSectionId获取对应库区信息 * 根据storageSectionId获取对应库区信息
*/ */
@@ -0,0 +1,30 @@
package com.mhd.system.api.domain;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
/**
* 仓库联动树节点(Feign侧)
* 镜像system端WarehouseLinkagePO/StorageSectionLinkagePO/StorageLocationLinkagePO三级结构:仓库→库区→库位
*
* @author rcx
* @date 2026-08-22
*/
@Data
public class WarehouseLinkageFeignPO implements Serializable {
private static final long serialVersionUID = 1L;
/** 仓库id / 库区id(storageSectionId) / 库位id(storageLocationId) */
private Long id;
/** warehouseCode / storageCode / storageLocationCode */
private String code;
/** warehouseName / storageName / storageLocationName */
private String name;
/** 下级节点:仓库下为库区列表,库区下为库位列表,库位无children */
private List<WarehouseLinkageFeignPO> children;
}
@@ -20,6 +20,7 @@ import org.springframework.cloud.openfeign.FallbackFactory;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.List; import java.util.List;
@@ -184,6 +185,11 @@ public class RemoteSystemFeignFallbackFactory implements FallbackFactory<SystemS
return AjaxResult.error("根据仓库ID获取仓库信息失败"); return AjaxResult.error("根据仓库ID获取仓库信息失败");
} }
@Override
public AjaxResult getWarehouseChildren(@RequestParam("warehouseId") Long warehouseId) {
return AjaxResult.error("根据仓库ID获取仓库及下库区及库位树失败");
}
@Override @Override
public AjaxResult getStorageSectionInfoByStorageSectionId(@PathVariable("storageSectionId") Long storageSectionId) { public AjaxResult getStorageSectionInfoByStorageSectionId(@PathVariable("storageSectionId") Long storageSectionId) {
return AjaxResult.error("根据库区ID获取库区信息失败"); return AjaxResult.error("根据库区ID获取库区信息失败");
@@ -133,6 +133,14 @@ public class StockShelfOrderApplicationService {
private static final String API_NAME = "save_qnBill_i"; private static final String API_NAME = "save_qnBill_i";
private static final String VERIFY = "ESLuxAmB6VXCgbqP1eFzxTPVRagkQ62BUkfhElrgQ6XmE/g2reHVflY/SiCSU7JD"; private static final String VERIFY = "ESLuxAmB6VXCgbqP1eFzxTPVRagkQ62BUkfhElrgQ6XmE/g2reHVflY/SiCSU7JD";
/** 仅这些仓库的上架单在PC上传页默认填充上架库位(按仓库名匹配;需放开其他仓库时在此追加)。
* 生产环境:维他奶仓(warehouseId=14);测试环境为 青洲仓(warehouseId=13),需测试时切换 */
private static final Set<String> DEFAULT_STORAGE_LOCATION_WAREHOUSE_NAMES = new HashSet<>(Collections.singletonList("维他奶仓"));
/** 默认上架库位:指定库区名→库位名(精确匹配,须与基础资料名称完全一致,注意含繁体字)。
* 生产环境:維他奶2层冷凍A區 → 2层冷凍A區A01;测试环境为 4楼A区 → 4A-01-01,需测试时切换 */
private static final String DEFAULT_SHELF_STORAGE_NAME = "維他奶2层冷凍A區";
private static final String DEFAULT_SHELF_STORAGE_LOCATION_NAME = "2层冷凍A區A01";
/** /**
* 分页查询上架单列表 * 分页查询上架单列表
@@ -357,6 +365,8 @@ public class StockShelfOrderApplicationService {
// PC 端(addParentCopyWhenNoChildren==false):单头 total* 仍按明细树递归汇总;明细行净重/毛重/体积/面积<strong>不</strong>做「计划−已上架」覆盖,保持 shelf_material_detail 库表值 // PC 端(addParentCopyWhenNoChildren==false):单头 total* 仍按明细树递归汇总;明细行净重/毛重/体积/面积<strong>不</strong>做「计划−已上架」覆盖,保持 shelf_material_detail 库表值
if (!addParentCopyWhenNoChildren) { if (!addParentCopyWhenNoChildren) {
// PC上传页:指定仓库(维他奶仓)的待上架明细默认填充指定库位,供前端回显(PDA不填充,实际落库仍以上架提交时选择的库位为准)
fillDefaultStorageLocation(stockShelfOrderPO, shelfMaterialDetailPOList);
BigDecimal totalNetWeight = BigDecimal.ZERO; BigDecimal totalNetWeight = BigDecimal.ZERO;
BigDecimal totalGrossWeight = BigDecimal.ZERO; BigDecimal totalGrossWeight = BigDecimal.ZERO;
BigDecimal totalVolume = BigDecimal.ZERO; BigDecimal totalVolume = BigDecimal.ZERO;
@@ -2447,6 +2457,108 @@ public class StockShelfOrderApplicationService {
} }
} }
/**
* PC上传页:为尚未指定上架库位的明细行默认填充指定仓库的指定库位
* (按名称精确匹配 DEFAULT_SHELF_STORAGE_NAME 库区下的 DEFAULT_SHELF_STORAGE_LOCATION_NAME 库位,非取排序第一个)。
* 仅回显默认值不落库:实际落库仍以上架提交时选择的库位为准(setStorageLocationInfo 会重新校验补全);
* 已上架行的库表库位不覆盖,兼容二次上架。Feign失败或未匹配到目标库位时仅告警降级,不影响页面打开。
*/
private void fillDefaultStorageLocation(StockShelfOrderPO stockShelfOrderPO, List<ShelfMaterialDetailPO> shelfMaterialDetailPOList) {
if (stockShelfOrderPO == null || CollectionUtils.isEmpty(shelfMaterialDetailPOList)) {
return;
}
// 已完成上架的单据不再填充,避免展示从未实际使用的库位
if (stockShelfOrderPO.getStatus() != null && stockShelfOrderPO.getStatus() > 2) {
return;
}
// 仅指定仓库生效(按名称匹配,warehouseId跨环境不稳定)
if (stockShelfOrderPO.getWarehouseId() == null
|| !DEFAULT_STORAGE_LOCATION_WAREHOUSE_NAMES.contains(stockShelfOrderPO.getWarehouseName())) {
return;
}
// 明细树中存在未填库位的行才发起查询
if (!hasEmptyStorageLocationRecursively(shelfMaterialDetailPOList)) {
return;
}
try {
AjaxResult warehouseChildrenResult = systemServiceFeign.getWarehouseChildren(stockShelfOrderPO.getWarehouseId());
if (warehouseChildrenResult == null || !"200".equals(String.valueOf(warehouseChildrenResult.get("code")))
|| warehouseChildrenResult.get("data") == null) {
log.warn("获取仓库库位树失败,跳过默认上架库位填充,warehouseId={}", stockShelfOrderPO.getWarehouseId());
return;
}
List<WarehouseLinkageFeignPO> warehouseLinkageList = JSON.parseArray(
JSONObject.toJSONString(warehouseChildrenResult.get("data")), WarehouseLinkageFeignPO.class);
if (CollectionUtils.isEmpty(warehouseLinkageList)) {
return;
}
WarehouseLinkageFeignPO warehouse = warehouseLinkageList.get(0);
if (CollectionUtils.isEmpty(warehouse.getChildren())) {
return;
}
// 精确匹配指定的默认库区/库位(非取排序第一个)
WarehouseLinkageFeignPO section = warehouse.getChildren().stream()
.filter(sectionNode -> DEFAULT_SHELF_STORAGE_NAME.equals(sectionNode.getName()))
.findFirst().orElse(null);
if (section == null || CollectionUtils.isEmpty(section.getChildren())) {
log.warn("未找到默认上架库区:{},warehouseId={},跳过默认库位填充",
DEFAULT_SHELF_STORAGE_NAME, stockShelfOrderPO.getWarehouseId());
return;
}
WarehouseLinkageFeignPO location = section.getChildren().stream()
.filter(locationNode -> DEFAULT_SHELF_STORAGE_LOCATION_NAME.equals(locationNode.getName()))
.findFirst().orElse(null);
if (location == null) {
log.warn("未找到默认上架库位:{}(库区:{}),warehouseId={},跳过默认库位填充",
DEFAULT_SHELF_STORAGE_LOCATION_NAME, DEFAULT_SHELF_STORAGE_NAME, stockShelfOrderPO.getWarehouseId());
return;
}
fillStorageLocationRecursively(shelfMaterialDetailPOList, warehouse, section, location);
log.info("已为上架单填充默认上架库位,shelfOrderId={}shelfOrderNumber={},库位:{}-{}-{}(库位ID{}",
stockShelfOrderPO.getShelfOrderId(), stockShelfOrderPO.getShelfOrderNumber(),
warehouse.getName(), section.getName(), location.getName(), location.getId());
} catch (Exception e) {
log.warn("填充默认上架库位失败,warehouseId={},错误:{}", stockShelfOrderPO.getWarehouseId(), e.getMessage());
}
}
/** 明细树(含子行)中是否存在未指定库位(storageLocationId为空或0)的行 */
private boolean hasEmptyStorageLocationRecursively(List<ShelfMaterialDetailPO> shelfMaterialDetailPOList) {
for (ShelfMaterialDetailPO detail : shelfMaterialDetailPOList) {
if (detail.getStorageLocationId() == null || detail.getStorageLocationId() == 0) {
return true;
}
if (!CollectionUtils.isEmpty(detail.getChildren())
&& hasEmptyStorageLocationRecursively(detail.getChildren())) {
return true;
}
}
return false;
}
/** 递归为未指定库位(storageLocationId为空或0)的明细行填充仓库/库区/库位信息,已有值不覆盖 */
private void fillStorageLocationRecursively(List<ShelfMaterialDetailPO> shelfMaterialDetailPOList,
WarehouseLinkageFeignPO warehouse,
WarehouseLinkageFeignPO section,
WarehouseLinkageFeignPO location) {
for (ShelfMaterialDetailPO detail : shelfMaterialDetailPOList) {
if (detail.getStorageLocationId() == null || detail.getStorageLocationId() == 0) {
detail.setWarehouseId(warehouse.getId());
detail.setWarehouseCode(warehouse.getCode());
detail.setWarehouseName(warehouse.getName());
detail.setStorageSectionId(section.getId());
detail.setStorageCode(section.getCode());
detail.setStorageName(section.getName());
detail.setStorageLocationId(location.getId());
detail.setStorageLocationCode(location.getCode());
detail.setStorageLocationName(location.getName());
}
if (!CollectionUtils.isEmpty(detail.getChildren())) {
fillStorageLocationRecursively(detail.getChildren(), warehouse, section, location);
}
}
}
/** /**
* 根据批次标签(batchLabels)映射到字段名 * 根据批次标签(batchLabels)映射到字段名
* @param batchLabel 批次标签 * @param batchLabel 批次标签