package com.ruoyi.production.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.ruoyi.common.exception.ServiceException; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.production.bean.dto.ProductionOrderPickDto; import com.ruoyi.production.bean.vo.ProductionOrderPickVo; import com.ruoyi.production.mapper.ProductionOperationTaskMapper; import com.ruoyi.production.mapper.ProductionOrderMapper; import com.ruoyi.production.mapper.ProductionOrderPickMapper; import com.ruoyi.production.mapper.ProductionOrderPickRecordMapper; import com.ruoyi.production.pojo.ProductionOrder; import com.ruoyi.production.pojo.ProductionOrderPick; import com.ruoyi.production.pojo.ProductionOrderPickRecord; import com.ruoyi.production.service.ProductionOrderPickService; import com.ruoyi.stock.dto.StockInventoryDto; import com.ruoyi.stock.mapper.StockInventoryMapper; import com.ruoyi.stock.pojo.StockInventory; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.math.BigDecimal; import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; /** *

* 璁㈠崟棰嗘枡绾胯竟浠?鏈嶅姟瀹炵幇绫? *

* * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃 * @since 2026-04-21 03:55:52 */ @Service @RequiredArgsConstructor public class ProductionOrderPickServiceImpl extends ServiceImpl implements ProductionOrderPickService { private final ProductionOrderMapper productionOrderMapper; private final ProductionOperationTaskMapper productionOperationTaskMapper; private final ProductionOrderPickRecordMapper productionOrderPickRecordMapper; private final StockInventoryMapper stockInventoryMapper; @Override @Transactional(rollbackFor = Exception.class) public Boolean savePick(ProductionOrderPickDto dto) { List pickItems = resolvePickItems(dto); for (int i = 0; i < pickItems.size(); i++) { int rowNo = i + 1; ProductionOrderPickDto resolvedDto = mergeDto(dto, pickItems.get(i)); validatePickParam(resolvedDto, rowNo); List batchNoList = resolveBatchNoList(resolvedDto); String inventoryBatchNo = pickInventoryBatchNo(batchNoList); String storedBatchNo = formatBatchNoStorage(batchNoList); subtractInventory(resolvedDto.getProductModelId(), inventoryBatchNo, resolvedDto.getPickQuantity(), rowNo); ProductionOrderPick orderPick = new ProductionOrderPick(); orderPick.setProductionOrderId(resolvedDto.getProductionOrderId()); orderPick.setProductModelId(resolvedDto.getProductModelId()); orderPick.setBatchNo(storedBatchNo); orderPick.setQuantity(resolvedDto.getPickQuantity()); orderPick.setRemark(resolvedDto.getRemark()); orderPick.setOperationName(resolvedDto.getOperationName()); orderPick.setTechnologyOperationId(resolvedDto.getTechnologyOperationId()); orderPick.setDemandedQuantity(resolvedDto.getDemandedQuantity()); orderPick.setBom(resolvedDto.getBom()); baseMapper.insert(orderPick); insertPickRecord(orderPick.getId(), resolvedDto.getProductionOrderId(), resolvedDto.getProductionOperationTaskId(), resolvedDto.getProductModelId(), inventoryBatchNo, resolvedDto.getPickQuantity(), BigDecimal.ZERO, resolvedDto.getPickQuantity(), resolvedDto.getPickType(), resolvedDto.getRemark()); } return true; } @Override @Transactional(rollbackFor = Exception.class) public Boolean updatePick(ProductionOrderPickDto dto) { if (dto == null) { throw new ServiceException("变更参数不能为空"); } Long productionOrderId = resolveProductionOrderId(dto); if (productionOrderId == null) { throw new ServiceException("生产订单ID不能为空"); } ProductionOrder productionOrder = productionOrderMapper.selectById(productionOrderId); if (productionOrder == null) { throw new ServiceException("生产订单不存在"); } List existingPickList = baseMapper.selectList( Wrappers.lambdaQuery() .eq(ProductionOrderPick::getProductionOrderId, productionOrderId)); Map existingPickMap = existingPickList.stream() .filter(item -> item.getId() != null) .collect(Collectors.toMap(ProductionOrderPick::getId, Function.identity(), (a, b) -> a)); processDeletePickIds(dto, existingPickMap, productionOrderId); List pickItems = resolveUpdateItems(dto); Set keepPickIdSet = new HashSet<>(); for (int i = 0; i < pickItems.size(); i++) { int rowNo = i + 1; ProductionOrderPickDto resolvedDto = mergeDto(dto, pickItems.get(i)); if (isEmptyUpdateItem(resolvedDto)) { continue; } if (resolvedDto.getProductionOrderId() == null) { resolvedDto.setProductionOrderId(productionOrderId); } validatePickParam(resolvedDto, rowNo); if (resolvedDto.getId() == null) { addNewPickInUpdate(resolvedDto, rowNo); continue; } keepPickIdSet.add(resolvedDto.getId()); updateExistingPick(resolvedDto, rowNo, existingPickMap); } processMissingPickItems(dto, existingPickMap, productionOrderId, keepPickIdSet); return true; } @Override public List listPickedDetail(Long productionOrderId) { if (productionOrderId == null) { return Collections.emptyList(); } List detailList = baseMapper.listPickedDetailByOrderId(productionOrderId); fillBatchNoList(detailList); fillSelectableBatchNoList(detailList); return detailList; } private void processDeletePickIds(ProductionOrderPickDto rootDto, Map existingPickMap, Long productionOrderId) { if (rootDto.getDeletePickIds() == null || rootDto.getDeletePickIds().isEmpty()) { return; } Set deleteIdSet = new LinkedHashSet<>(rootDto.getDeletePickIds()); for (Long deleteId : deleteIdSet) { if (deleteId == null) { continue; } ProductionOrderPick existingPick = existingPickMap.get(deleteId); if (existingPick == null || !Objects.equals(existingPick.getProductionOrderId(), productionOrderId)) { throw new ServiceException("要删除的领料记录不存在或不属于当前订单,ID=" + deleteId); } String oldBatchNo = resolveInventoryBatchNoFromStored(existingPick.getBatchNo()); BigDecimal oldQuantity = defaultDecimal(existingPick.getQuantity()); addInventory(existingPick.getProductModelId(), oldBatchNo, oldQuantity); int affected = baseMapper.deleteById(deleteId); if (affected <= 0) { throw new ServiceException("删除领料失败,ID=" + deleteId); } insertPickRecord(existingPick.getId(), existingPick.getProductionOrderId(), rootDto.getProductionOperationTaskId(), existingPick.getProductModelId(), oldBatchNo, oldQuantity, oldQuantity, BigDecimal.ZERO, rootDto.getPickType(), rootDto.getRemark()); existingPickMap.remove(deleteId); } } private void processMissingPickItems(ProductionOrderPickDto rootDto, Map existingPickMap, Long productionOrderId, Set keepPickIdSet) { if (rootDto.getPickList() == null) { return; } List missingPickList = existingPickMap.values().stream() .filter(Objects::nonNull) .filter(item -> item.getId() != null) .filter(item -> Objects.equals(item.getProductionOrderId(), productionOrderId)) .filter(item -> !keepPickIdSet.contains(item.getId())) .collect(Collectors.toList()); for (ProductionOrderPick missingPick : missingPickList) { String oldBatchNo = resolveInventoryBatchNoFromStored(missingPick.getBatchNo()); BigDecimal oldQuantity = defaultDecimal(missingPick.getQuantity()); addInventory(missingPick.getProductModelId(), oldBatchNo, oldQuantity); int affected = baseMapper.deleteById(missingPick.getId()); if (affected <= 0) { throw new ServiceException("删除领料失败,ID=" + missingPick.getId()); } insertPickRecord(missingPick.getId(), missingPick.getProductionOrderId(), rootDto.getProductionOperationTaskId(), missingPick.getProductModelId(), oldBatchNo, oldQuantity, oldQuantity, BigDecimal.ZERO, rootDto.getPickType(), rootDto.getRemark()); existingPickMap.remove(missingPick.getId()); } } private void addNewPickInUpdate(ProductionOrderPickDto dto, int rowNo) { List batchNoList = resolveBatchNoList(dto); String inventoryBatchNo = pickInventoryBatchNo(batchNoList); String storedBatchNo = formatBatchNoStorage(batchNoList); subtractInventory(dto.getProductModelId(), inventoryBatchNo, dto.getPickQuantity(), rowNo); ProductionOrderPick orderPick = new ProductionOrderPick(); orderPick.setProductionOrderId(dto.getProductionOrderId()); orderPick.setProductModelId(dto.getProductModelId()); orderPick.setBatchNo(storedBatchNo); orderPick.setQuantity(dto.getPickQuantity()); orderPick.setRemark(dto.getRemark()); orderPick.setOperationName(dto.getOperationName()); orderPick.setTechnologyOperationId(dto.getTechnologyOperationId()); orderPick.setDemandedQuantity(dto.getDemandedQuantity()); orderPick.setBom(dto.getBom()); baseMapper.insert(orderPick); insertPickRecord(orderPick.getId(), dto.getProductionOrderId(), dto.getProductionOperationTaskId(), dto.getProductModelId(), inventoryBatchNo, dto.getPickQuantity(), BigDecimal.ZERO, dto.getPickQuantity(), dto.getPickType(), dto.getRemark()); } private void updateExistingPick(ProductionOrderPickDto dto, int rowNo, Map existingPickMap) { ProductionOrderPick oldPick = existingPickMap.get(dto.getId()); if (oldPick == null || !Objects.equals(oldPick.getProductionOrderId(), dto.getProductionOrderId())) { throw new ServiceException("第" + rowNo + "条领料记录不存在或不属于当前订单"); } Long oldProductModelId = oldPick.getProductModelId(); String oldBatchNo = resolveInventoryBatchNoFromStored(oldPick.getBatchNo()); BigDecimal oldQuantity = defaultDecimal(oldPick.getQuantity()); Long newProductModelId = dto.getProductModelId(); List newBatchNoList = resolveBatchNoList(dto); String newBatchNo = pickInventoryBatchNo(newBatchNoList); String newStoredBatchNo = formatBatchNoStorage(newBatchNoList); BigDecimal newQuantity = dto.getPickQuantity(); boolean sameStockKey = Objects.equals(oldProductModelId, newProductModelId) && Objects.equals(oldBatchNo, newBatchNo); if (sameStockKey) { BigDecimal delta = newQuantity.subtract(oldQuantity); if (delta.compareTo(BigDecimal.ZERO) > 0) { subtractInventory(newProductModelId, newBatchNo, delta, rowNo); } else if (delta.compareTo(BigDecimal.ZERO) < 0) { addInventory(oldProductModelId, oldBatchNo, delta.abs()); } } else { addInventory(oldProductModelId, oldBatchNo, oldQuantity); subtractInventory(newProductModelId, newBatchNo, newQuantity, rowNo); } oldPick.setProductModelId(newProductModelId); oldPick.setBatchNo(newStoredBatchNo); oldPick.setQuantity(newQuantity); oldPick.setRemark(dto.getRemark()); oldPick.setOperationName(dto.getOperationName()); oldPick.setTechnologyOperationId(dto.getTechnologyOperationId()); if (dto.getDemandedQuantity() != null) { oldPick.setDemandedQuantity(dto.getDemandedQuantity()); } if (dto.getBom() != null) { oldPick.setBom(dto.getBom()); } int affected = baseMapper.updateById(oldPick); if (affected <= 0) { throw new ServiceException("第" + rowNo + "条领料更新失败"); } BigDecimal recordQuantity = sameStockKey ? oldQuantity.subtract(newQuantity).abs() : newQuantity; if (recordQuantity.compareTo(BigDecimal.ZERO) > 0 || oldQuantity.compareTo(newQuantity) != 0 || !sameStockKey) { insertPickRecord(oldPick.getId(), dto.getProductionOrderId(), dto.getProductionOperationTaskId(), newProductModelId, newBatchNo, recordQuantity, oldQuantity, newQuantity, dto.getPickType(), dto.getRemark()); } } private void insertPickRecord(Long pickId, Long productionOrderId, Long productionOperationTaskId, Long productModelId, String batchNo, BigDecimal pickQuantity, BigDecimal beforeQuantity, BigDecimal afterQuantity, Byte pickType, String remark) { ProductionOrderPickRecord pickRecord = new ProductionOrderPickRecord(); pickRecord.setPickId(pickId); pickRecord.setProductionOrderId(productionOrderId); pickRecord.setProductionOperationTaskId(productionOperationTaskId); pickRecord.setProductModelId(productModelId); pickRecord.setBatchNo(batchNo); pickRecord.setPickQuantity(defaultDecimal(pickQuantity)); pickRecord.setBeforeQuantity(defaultDecimal(beforeQuantity)); pickRecord.setAfterQuantity(defaultDecimal(afterQuantity)); pickRecord.setPickType(pickType == null ? (byte) 1 : pickType); pickRecord.setRemark(remark); productionOrderPickRecordMapper.insert(pickRecord); } private void subtractInventory(Long productModelId, String batchNo, BigDecimal quantity, int rowNo) { StockInventory stockInventory = stockInventoryMapper.selectOne(buildStockWrapper(productModelId, batchNo)); if (stockInventory == null) { throw new ServiceException("第" + rowNo + "条领料对应库存不存在"); } BigDecimal availableQuantity = defaultDecimal(stockInventory.getQualitity()) .subtract(defaultDecimal(stockInventory.getLockedQuantity())); if (quantity.compareTo(availableQuantity) > 0) { throw new ServiceException("第" + rowNo + "条领料可用库存不足"); } StockInventoryDto stockInventoryDto = new StockInventoryDto(); stockInventoryDto.setProductModelId(productModelId); stockInventoryDto.setBatchNo(batchNo); stockInventoryDto.setQualitity(quantity); int affected = stockInventoryMapper.updateSubtractStockInventory(stockInventoryDto); if (affected <= 0) { throw new ServiceException("第" + rowNo + "条领料扣减库存失败"); } } private void addInventory(Long productModelId, String batchNo, BigDecimal quantity) { BigDecimal addQuantity = defaultDecimal(quantity); if (addQuantity.compareTo(BigDecimal.ZERO) <= 0) { return; } StockInventory stockInventory = stockInventoryMapper.selectOne(buildStockWrapper(productModelId, batchNo)); if (stockInventory == null) { StockInventory newStockInventory = new StockInventory(); newStockInventory.setProductModelId(productModelId); newStockInventory.setBatchNo(batchNo); newStockInventory.setQualitity(addQuantity); newStockInventory.setLockedQuantity(BigDecimal.ZERO); newStockInventory.setVersion(1); stockInventoryMapper.insert(newStockInventory); return; } StockInventoryDto stockInventoryDto = new StockInventoryDto(); stockInventoryDto.setProductModelId(productModelId); stockInventoryDto.setBatchNo(batchNo); stockInventoryDto.setQualitity(addQuantity); int affected = stockInventoryMapper.updateAddStockInventory(stockInventoryDto); if (affected <= 0) { throw new ServiceException("库存回退失败,产品规格ID=" + productModelId); } } private List resolvePickItems(ProductionOrderPickDto dto) { if (dto == null) { throw new ServiceException("领料参数不能为空"); } if (dto.getPickList() != null && !dto.getPickList().isEmpty()) { return dto.getPickList(); } return Collections.singletonList(dto); } private List resolveUpdateItems(ProductionOrderPickDto dto) { if (dto.getPickList() != null) { return dto.getPickList(); } if (isEmptyUpdateItem(dto)) { return Collections.emptyList(); } return Collections.singletonList(dto); } private boolean isEmptyUpdateItem(ProductionOrderPickDto dto) { return dto.getId() == null && dto.getProductModelId() == null && dto.getPickQuantity() == null && StringUtils.isEmpty(dto.getBatchNo()) && (dto.getBatchNoList() == null || dto.getBatchNoList().isEmpty()) && dto.getPickType() == null && dto.getProductionOperationTaskId() == null && dto.getTechnologyOperationId() == null && StringUtils.isEmpty(dto.getOperationName()) && dto.getDemandedQuantity() == null && dto.getBom() == null && StringUtils.isEmpty(dto.getRemark()); } private Long resolveProductionOrderId(ProductionOrderPickDto dto) { if (dto.getProductionOrderId() != null) { return dto.getProductionOrderId(); } if (dto.getPickList() == null || dto.getPickList().isEmpty()) { return null; } return dto.getPickList().stream() .filter(Objects::nonNull) .map(ProductionOrderPickDto::getProductionOrderId) .filter(Objects::nonNull) .findFirst() .orElse(null); } private ProductionOrderPickDto mergeDto(ProductionOrderPickDto rootDto, ProductionOrderPickDto itemDto) { ProductionOrderPickDto merged = new ProductionOrderPickDto(); if (itemDto != null) { merged.setId(itemDto.getId()); merged.setProductionOrderId(itemDto.getProductionOrderId()); merged.setProductionOperationTaskId(itemDto.getProductionOperationTaskId()); merged.setProductModelId(itemDto.getProductModelId()); merged.setBatchNo(itemDto.getBatchNo()); merged.setBatchNoList(itemDto.getBatchNoList()); merged.setPickQuantity(itemDto.getPickQuantity()); merged.setPickType(itemDto.getPickType()); merged.setRemark(itemDto.getRemark()); merged.setTechnologyOperationId(itemDto.getTechnologyOperationId()); merged.setOperationName(itemDto.getOperationName()); merged.setDemandedQuantity(itemDto.getDemandedQuantity()); merged.setBom(itemDto.getBom()); } if (merged.getId() == null) { merged.setId(rootDto.getId()); } if (merged.getProductionOrderId() == null) { merged.setProductionOrderId(rootDto.getProductionOrderId()); } if (merged.getProductionOperationTaskId() == null) { merged.setProductionOperationTaskId(rootDto.getProductionOperationTaskId()); } if (merged.getProductModelId() == null) { merged.setProductModelId(rootDto.getProductModelId()); } if (merged.getBatchNo() == null) { merged.setBatchNo(rootDto.getBatchNo()); } if (merged.getBatchNoList() == null || merged.getBatchNoList().isEmpty()) { merged.setBatchNoList(rootDto.getBatchNoList()); } if (merged.getPickQuantity() == null) { merged.setPickQuantity(rootDto.getPickQuantity()); } if (merged.getPickType() == null) { merged.setPickType(rootDto.getPickType()); } if (merged.getRemark() == null) { merged.setRemark(rootDto.getRemark()); } if (merged.getTechnologyOperationId() == null) { merged.setTechnologyOperationId(rootDto.getTechnologyOperationId()); } if (merged.getOperationName() == null) { merged.setOperationName(rootDto.getOperationName()); } if (merged.getDemandedQuantity() == null) { merged.setDemandedQuantity(rootDto.getDemandedQuantity()); } if (merged.getBom() == null) { merged.setBom(rootDto.getBom()); } return merged; } private void validatePickParam(ProductionOrderPickDto dto, int rowNo) { if (dto.getProductionOrderId() == null) { throw new ServiceException("第" + rowNo + "条生产订单ID不能为空"); } if (dto.getProductModelId() == null) { throw new ServiceException("第" + rowNo + "条产品规格ID不能为空"); } if (dto.getPickQuantity() == null || dto.getPickQuantity().compareTo(BigDecimal.ZERO) <= 0) { throw new ServiceException("第" + rowNo + "条领料数量必须大于0"); } if (dto.getPickType() != null && dto.getPickType() != 1 && dto.getPickType() != 2) { throw new ServiceException("第" + rowNo + "条领料类型只能是1或2"); } } private String normalizeBatchNo(String batchNo) { if (StringUtils.isEmpty(batchNo)) { return null; } String trimBatchNo = batchNo.trim(); return trimBatchNo.isEmpty() ? null : trimBatchNo; } private List resolveBatchNoList(ProductionOrderPickDto dto) { List normalizedBatchNoList = normalizeBatchNoList(dto.getBatchNoList()); if (!normalizedBatchNoList.isEmpty()) { return normalizedBatchNoList; } return parseBatchNoValue(dto.getBatchNo()); } private String pickInventoryBatchNo(List batchNoList) { if (batchNoList == null || batchNoList.isEmpty()) { return null; } return batchNoList.get(0); } private String resolveInventoryBatchNoFromStored(String storedBatchNo) { return pickInventoryBatchNo(parseBatchNoValue(storedBatchNo)); } private String formatBatchNoStorage(List batchNoList) { if (batchNoList == null || batchNoList.isEmpty()) { return null; } if (batchNoList.size() == 1) { return batchNoList.get(0); } return String.join(",", batchNoList); } private List normalizeBatchNoList(List batchNoList) { if (batchNoList == null || batchNoList.isEmpty()) { return Collections.emptyList(); } LinkedHashSet normalizedSet = new LinkedHashSet<>(); for (String batchNo : batchNoList) { String normalizedBatchNo = normalizeBatchNo(batchNo); if (!StringUtils.isEmpty(normalizedBatchNo)) { normalizedSet.add(normalizedBatchNo); } } return new ArrayList<>(normalizedSet); } private void fillBatchNoList(List detailList) { if (detailList == null || detailList.isEmpty()) { return; } Map> batchNoGroupMap = new HashMap<>(); for (ProductionOrderPickVo detail : detailList) { String key = buildBatchNoGroupKey(detail); LinkedHashSet batchSet = batchNoGroupMap.computeIfAbsent(key, k -> new LinkedHashSet<>()); batchSet.addAll(parseBatchNoValue(detail.getBatchNo())); if (detail.getBatchNoList() != null && !detail.getBatchNoList().isEmpty()) { batchSet.addAll(normalizeBatchNoList(detail.getBatchNoList())); } } for (ProductionOrderPickVo detail : detailList) { String key = buildBatchNoGroupKey(detail); LinkedHashSet batchSet = batchNoGroupMap.get(key); detail.setBatchNoList(batchSet == null ? Collections.emptyList() : new ArrayList<>(batchSet)); } } private void fillSelectableBatchNoList(List detailList) { if (detailList == null || detailList.isEmpty()) { return; } Set productModelIdSet = detailList.stream() .map(ProductionOrderPickVo::getProductModelId) .filter(Objects::nonNull) .collect(Collectors.toSet()); if (productModelIdSet.isEmpty()) { return; } List stockBatchList = stockInventoryMapper.listSelectableBatchNoByProductModelIds( new ArrayList<>(productModelIdSet)); Map> stockBatchMap = new HashMap<>(); for (StockInventory stockInventory : stockBatchList) { if (stockInventory == null || stockInventory.getProductModelId() == null) { continue; } String normalizedBatchNo = normalizeBatchNo(stockInventory.getBatchNo()); if (StringUtils.isEmpty(normalizedBatchNo)) { continue; } stockBatchMap.computeIfAbsent(stockInventory.getProductModelId(), k -> new LinkedHashSet<>()) .add(normalizedBatchNo); } for (ProductionOrderPickVo detail : detailList) { LinkedHashSet mergedBatchSet = new LinkedHashSet<>(); mergedBatchSet.addAll(normalizeBatchNoList(detail.getBatchNoList())); LinkedHashSet selectableBatchSet = stockBatchMap.get(detail.getProductModelId()); if (selectableBatchSet != null) { mergedBatchSet.addAll(selectableBatchSet); } detail.setBatchNoList(new ArrayList<>(mergedBatchSet)); } } private String buildBatchNoGroupKey(ProductionOrderPickVo detail) { return String.valueOf(detail.getProductionOrderId()) + "|" + String.valueOf(detail.getProductModelId()) + "|" + String.valueOf(detail.getTechnologyOperationId()) + "|" + String.valueOf(detail.getOperationName()); } private List parseBatchNoValue(String rawBatchNoValue) { String normalizedValue = normalizeBatchNo(rawBatchNoValue); if (StringUtils.isEmpty(normalizedValue)) { return Collections.emptyList(); } if (normalizedValue.startsWith("[") && normalizedValue.endsWith("]")) { String value = normalizedValue.substring(1, normalizedValue.length() - 1); if (StringUtils.isEmpty(value)) { return Collections.emptyList(); } List parsed = Arrays.stream(value.split(",")) .map(item -> item == null ? null : item.trim().replace("\"", "").replace("'", "")) .collect(Collectors.toList()); return normalizeBatchNoList(parsed); } if (normalizedValue.contains(",")) { List parsed = Arrays.stream(normalizedValue.split(",")) .map(item -> item == null ? null : item.trim()) .collect(Collectors.toList()); return normalizeBatchNoList(parsed); } return Collections.singletonList(normalizedValue); } private LambdaQueryWrapper buildStockWrapper(Long productModelId, String batchNo) { LambdaQueryWrapper wrapper = Wrappers.lambdaQuery() .eq(StockInventory::getProductModelId, productModelId); if (StringUtils.isEmpty(batchNo)) { wrapper.isNull(StockInventory::getBatchNo); } else { wrapper.eq(StockInventory::getBatchNo, batchNo); } return wrapper; } private BigDecimal defaultDecimal(BigDecimal value) { return value == null ? BigDecimal.ZERO : value; } }