package com.ruoyi.stock.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.toolkit.ObjectUtils; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.ruoyi.approve.service.impl.ApproveProcessServiceImpl; import com.ruoyi.approve.vo.ApproveProcessVO; import com.ruoyi.basic.mapper.ProductMapper; import com.ruoyi.basic.mapper.ProductModelMapper; import com.ruoyi.basic.pojo.Product; import com.ruoyi.basic.pojo.ProductModel; import com.ruoyi.common.enums.StockInQualifiedRecordTypeEnum; import com.ruoyi.common.enums.StockInUnQualifiedRecordTypeEnum; import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.poi.ExcelUtil; import com.ruoyi.framework.security.LoginUser; import com.ruoyi.framework.web.domain.R; import com.ruoyi.sales.mapper.SalesLedgerProductMapper; import com.ruoyi.sales.pojo.SalesLedgerProduct; import com.ruoyi.stock.dto.*; import com.ruoyi.stock.execl.FinishedProductInventoryExportData; import com.ruoyi.stock.execl.NonFinishedProductInventoryExportData; import com.ruoyi.stock.execl.StockInventoryExportData; import com.ruoyi.stock.mapper.StockInventoryMapper; import com.ruoyi.stock.pojo.StockInRecord; import com.ruoyi.stock.pojo.StockInventory; import com.ruoyi.stock.service.StockInRecordService; import com.ruoyi.stock.service.StockInventoryService; import com.ruoyi.stock.service.StockOutRecordService; import com.ruoyi.stock.service.StockUninventoryService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletResponse; import java.math.BigDecimal; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; @Service public class StockInventoryServiceImpl extends ServiceImpl implements StockInventoryService { @Autowired private StockInventoryMapper stockInventoryMapper; @Autowired private StockInRecordService stockInRecordService; @Autowired private StockOutRecordService stockOutRecordService; @Autowired private SalesLedgerProductMapper salesLedgerProductMapper; @Autowired private ApproveProcessServiceImpl approveProcessService; @Autowired private ProductModelMapper productModelMapper; @Autowired private ProductMapper productMapper; @Autowired private StockUninventoryService stockUninventoryService; @Override public IPage pagestockInventory(Page page, StockInventoryDto stockInventoryDto) { return stockInventoryMapper.pagestockInventory(page, stockInventoryDto); } @Override public List finishedProductList(StockInventoryDto stockInventoryDto) { // 查询库存数据 List dataList = stockInventoryMapper.selectFinishedProductList(stockInventoryDto); if (dataList.isEmpty()) { return new ArrayList<>(); } // 按产品ID分组,构建树形结构 Map productMap = new LinkedHashMap<>(); for (FinishedProductTreeDto data : dataList) { Long productId = data.getProductId(); if (!productMap.containsKey(productId)) { // 创建产品大类节点 FinishedProductTreeDto productNode = new FinishedProductTreeDto(); productNode.setProductId(productId); productNode.setProductName(data.getProductName()); productNode.setLabel(data.getLabel()); productNode.setChildren(new ArrayList<>()); productMap.put(productId, productNode); } // 添加库存叶子节点 FinishedProductTreeDto leafNode = new FinishedProductTreeDto(); leafNode.setStockId(data.getStockId()); leafNode.setProductModelId(data.getProductModelId()); leafNode.setModel(data.getModel()); leafNode.setProcessCategory(data.getProcessCategory()); leafNode.setVoltage(data.getVoltage()); leafNode.setMaterialCode(data.getMaterialCode()); leafNode.setUnit(data.getUnit()); leafNode.setQualitity(data.getQualitity()); productMap.get(productId).getChildren().add(leafNode); } return new ArrayList<>(productMap.values()); } @Override public IPage pageListCombinedStockInventory(Page page, StockInventoryDto stockInventoryDto) { return stockInventoryMapper.pageListCombinedStockInventory(page, stockInventoryDto); } /** * 合格入库:先生成入库记录,再走审批流。 */ @Override @Transactional(rollbackFor = Exception.class) public Boolean addstockInventory(StockInventoryDto stockInventoryDto) { List stockInventoryList = stockInventoryMapper.selectList(null); StockInRecordDto stockInRecordDto = new StockInRecordDto(); stockInRecordDto.setRecordId(stockInventoryDto.getRecordId()); stockInRecordDto.setRecordType(stockInventoryDto.getRecordType()); stockInRecordDto.setStockInNum(stockInventoryDto.getQualitity()); stockInRecordDto.setProductModelId(stockInventoryDto.getProductModelId()); stockInRecordDto.setRemark(stockInventoryDto.getRemark()); stockInRecordDto.setWarnNum(stockInventoryDto.getWarnNum()); stockInRecordDto.setLockedQuantity(stockInventoryDto.getLockedQuantity()); stockInRecordDto.setProcessCategory(normalizeDimension(stockInventoryDto.getProcessCategory())); stockInRecordDto.setVoltage(normalizeDimension(stockInventoryDto.getVoltage())); stockInRecordDto.setApproveStatus(0); stockInRecordDto.setType("0"); if (StringUtils.isBlank(stockInventoryDto.getBatchNo())) { String batchNo; LocalDate now = LocalDate.now(); String monthFlag = now.format(DateTimeFormatter.ofPattern("MM")); int maxSeq = getCurrentMonthMaxSeq(stockInventoryDto, monthFlag, stockInventoryList); int newSeq = maxSeq + 1; String seqStr = String.format("%03d", newSeq); ProductModel productModel = productModelMapper.selectById(stockInventoryDto.getProductModelId()); batchNo = stockInventoryDto.getMaterialCode() + productModel.getModel() + "P" + monthFlag + seqStr; stockInRecordDto.setBatchNo(batchNo); } else { stockInRecordDto.setBatchNo(stockInventoryDto.getBatchNo()); } Long id = stockInRecordService.add(stockInRecordDto); LoginUser loginUser = SecurityUtils.getLoginUser(); if (id != null) { try { addApproveByPurchase(loginUser, stockInRecordDto, id); } catch (Exception e) { throw new RuntimeException(e.getMessage()); } } return true; } private static int getCurrentMonthMaxSeq(StockInventoryDto dto, String monthFlag, List existingList) { int maxSeq = 0; String prefix = dto.getMaterialCode() + dto.getProductModelName() + "P" + monthFlag; Pattern pattern = Pattern.compile(Pattern.quote(prefix) + "(\\d{3})"); for (StockInventory item : existingList) { String batchNo = item.getBatchNo(); if (batchNo == null) { continue; } Matcher matcher = pattern.matcher(batchNo); if (matcher.find()) { int seq = Integer.parseInt(matcher.group(1)); if (seq > maxSeq) { maxSeq = seq; } } } return maxSeq; } @Override public void addApproveByPurchase(LoginUser loginUser, StockInRecordDto stockInRecordDto, Long id) throws Exception { ApproveProcessVO approveProcessVO = new ApproveProcessVO(); approveProcessVO.setApproveType(9); approveProcessVO.setApproveDeptId(loginUser.getCurrentDeptId()); approveProcessVO.setApproveReason(stockInRecordDto.getInboundBatches()); approveProcessVO.setApproveUserIds(String.valueOf(1)); approveProcessVO.setApproveUser(loginUser.getUserId()); approveProcessVO.setApproveTime(LocalDate.now().toString()); approveProcessVO.setInventoryReview(false); approveProcessVO.setStorageType("合格入库"); approveProcessVO.setRecordId(id); approveProcessService.addApprove(approveProcessVO); } /** * 按库存唯一键合并库存。 * 成品使用 product_model_id + processCategory + voltage;其他入库只按 product_model_id。 */ @Override public void updateOrCreateStockInventory(StockInRecord stockInRecord) { String processCategory = normalizeDimension(stockInRecord.getProcessCategory()); String voltage = normalizeDimension(stockInRecord.getVoltage()); StockInventory oldStockInventory = findInventoryForMerge( stockInRecord.getProductModelId(), processCategory, voltage ); if (ObjectUtils.isEmpty(oldStockInventory)) { StockInventory newStockInventory = new StockInventory(); newStockInventory.setProductModelId(stockInRecord.getProductModelId()); newStockInventory.setQualitity(defaultDecimal(stockInRecord.getStockInNum())); newStockInventory.setVersion(1); newStockInventory.setRemark(stockInRecord.getRemark()); newStockInventory.setLockedQuantity(defaultDecimal(stockInRecord.getLockedQuantity())); newStockInventory.setWarnNum(stockInRecord.getWarnNum()); newStockInventory.setProcessCategory(processCategory); newStockInventory.setVoltage(voltage); newStockInventory.setBatchNo(StringUtils.trimToEmpty(stockInRecord.getBatchNo())); stockInventoryMapper.insert(newStockInventory); return; } oldStockInventory.setQualitity(defaultDecimal(oldStockInventory.getQualitity()).add(defaultDecimal(stockInRecord.getStockInNum()))); oldStockInventory.setVersion(oldStockInventory.getVersion() == null ? 1 : oldStockInventory.getVersion() + 1); if (StringUtils.isNotBlank(stockInRecord.getRemark())) { oldStockInventory.setRemark(stockInRecord.getRemark()); } if (stockInRecord.getWarnNum() != null) { oldStockInventory.setWarnNum(stockInRecord.getWarnNum()); } if (stockInRecord.getLockedQuantity() != null) { oldStockInventory.setLockedQuantity(defaultDecimal(oldStockInventory.getLockedQuantity()).add(stockInRecord.getLockedQuantity())); } oldStockInventory.setProcessCategory(processCategory); oldStockInventory.setVoltage(voltage); oldStockInventory.setUpdateTime(LocalDateTime.now()); stockInventoryMapper.updateById(oldStockInventory); } /** * 不审核入库,直接写入入库记录和库存。 */ @Override @Transactional(rollbackFor = Exception.class) public Boolean addstockInventoryNoReview(StockInventoryDto stockInventoryDto) { StockInRecordDto stockInRecordDto = new StockInRecordDto(); stockInRecordDto.setRecordId(stockInventoryDto.getRecordId()); stockInRecordDto.setRecordType(stockInventoryDto.getRecordType()); stockInRecordDto.setStockInNum(stockInventoryDto.getQualitity()); stockInRecordDto.setProductModelId(stockInventoryDto.getProductModelId()); stockInRecordDto.setRemark(stockInventoryDto.getRemark()); stockInRecordDto.setWarnNum(stockInventoryDto.getWarnNum()); stockInRecordDto.setLockedQuantity(stockInventoryDto.getLockedQuantity()); stockInRecordDto.setProcessCategory(normalizeDimension(stockInventoryDto.getProcessCategory())); stockInRecordDto.setVoltage(normalizeDimension(stockInventoryDto.getVoltage())); stockInRecordDto.setBatchNo(stockInventoryDto.getBatchNo()); stockInRecordDto.setType("0"); stockInRecordService.add(stockInRecordDto); updateOrCreateStockInventory(toStockInRecord(stockInRecordDto)); return true; } @Override @Transactional(rollbackFor = Exception.class) public Boolean subtractStockInventory(StockInventoryDto stockInventoryDto) { if (stockInventoryDto.getQualitity() != null && stockInventoryDto.getQualitity().compareTo(BigDecimal.ZERO) == 0) { return true; } StockOutRecordDto stockOutRecordDto = new StockOutRecordDto(); stockOutRecordDto.setRecordId(stockInventoryDto.getRecordId()); stockOutRecordDto.setRecordType(stockInventoryDto.getRecordType()); stockOutRecordDto.setStockOutNum(stockInventoryDto.getQualitity()); stockOutRecordDto.setProductModelId(stockInventoryDto.getProductModelId()); stockOutRecordDto.setType("0"); stockOutRecordService.add(stockOutRecordDto); //销售出库按照保存的库存id if (stockInventoryDto.getStockId() != null) { StockInventory stockInventory = stockInventoryMapper.selectById(stockInventoryDto.getStockId()); if (ObjectUtils.isEmpty(stockInventory)) { throw new RuntimeException("产品库存不存在"); } BigDecimal lockedQty = defaultDecimal(stockInventory.getLockedQuantity()); if (stockInventoryDto.getQualitity().compareTo(defaultDecimal(stockInventory.getQualitity()).subtract(lockedQty)) > 0) { ProductModel productModel = productModelMapper.selectById(stockInventoryDto.getProductModelId()); Product product = productMapper.selectById(productModel.getProductId()); throw new RuntimeException(product.getProductName() + "/" + productModel.getModel() + "库存不足,无法出库"); } stockInventory.setQualitity(defaultDecimal(stockInventory.getQualitity()).subtract(stockInventoryDto.getQualitity())); stockInventory.setVersion(stockInventory.getVersion() == null ? 1 : stockInventory.getVersion() + 1); stockInventory.setUpdateTime(LocalDateTime.now()); stockInventoryMapper.updateById(stockInventory); return true; } if (StringUtils.isBlank(stockInventoryDto.getBatchNo()) && !usesDimensionIdentity(normalizeDimension(stockInventoryDto.getProcessCategory()), normalizeDimension(stockInventoryDto.getVoltage()))) { List stockInventories = stockInventoryMapper.selectList(new QueryWrapper().lambda() .eq(StockInventory::getProductModelId, stockInventoryDto.getProductModelId()) .orderByAsc(StockInventory::getId)); if (ObjectUtils.isEmpty(stockInventories)) { throw new RuntimeException("产品库存不存在"); } BigDecimal remainingQty = stockInventoryDto.getQualitity() == null ? BigDecimal.ZERO : stockInventoryDto.getQualitity(); for (StockInventory stockInventory : stockInventories) { BigDecimal lockedQty = defaultDecimal(stockInventory.getLockedQuantity()); BigDecimal availableQty = defaultDecimal(stockInventory.getQualitity()).subtract(lockedQty); if (availableQty.compareTo(BigDecimal.ZERO) <= 0) { continue; } BigDecimal deductQty = remainingQty.min(availableQty); stockInventory.setQualitity(defaultDecimal(stockInventory.getQualitity()).subtract(deductQty)); stockInventory.setVersion(stockInventory.getVersion() == null ? 1 : stockInventory.getVersion() + 1); stockInventory.setUpdateTime(LocalDateTime.now()); stockInventoryMapper.updateById(stockInventory); remainingQty = remainingQty.subtract(deductQty); if (remainingQty.compareTo(BigDecimal.ZERO) <= 0) { return true; } } ProductModel productModel = productModelMapper.selectById(stockInventoryDto.getProductModelId()); Product product = productMapper.selectById(productModel.getProductId()); throw new RuntimeException(product.getProductName() + "/" + productModel.getModel() + "库存不足,无法出库"); } StockInventory oldStockInventory = findInventoryForMerge( stockInventoryDto.getProductModelId(), stockInventoryDto.getProcessCategory(), stockInventoryDto.getVoltage() ); if (ObjectUtils.isEmpty(oldStockInventory)) { throw new RuntimeException("产品库存不存在"); } BigDecimal lockedQty = defaultDecimal(oldStockInventory.getLockedQuantity()); if (stockInventoryDto.getQualitity().compareTo(defaultDecimal(oldStockInventory.getQualitity()).subtract(lockedQty)) > 0) { ProductModel productModel = productModelMapper.selectById(stockInventoryDto.getProductModelId()); Product product = productMapper.selectById(productModel.getProductId()); throw new RuntimeException(product.getProductName() + "/" + productModel.getModel() + "库存不足,无法出库"); } oldStockInventory.setQualitity(defaultDecimal(oldStockInventory.getQualitity()).subtract(stockInventoryDto.getQualitity())); oldStockInventory.setVersion(oldStockInventory.getVersion() == null ? 1 : oldStockInventory.getVersion() + 1); oldStockInventory.setUpdateTime(LocalDateTime.now()); stockInventoryMapper.updateById(oldStockInventory); return true; } @Override public R importStockInventory(MultipartFile file, Integer productType) { try { List salesLedgerProducts = salesLedgerProductMapper.selectProduct(); Map productMap = new HashMap<>(); for (SalesLedgerProduct product : salesLedgerProducts) { String key = product.getProductCategory() + "|" + product.getSpecificationModel(); productMap.put(key, product); } List unmatchedRecords = new ArrayList<>(); int successCount = 0; // productType: 1=成品, 0=非成品 if (productType == 1) { // 成品导入(包含工序类别和电压) ExcelUtil util = new ExcelUtil<>(FinishedProductInventoryExportData.class); List list = util.importExcel(file.getInputStream()); for (FinishedProductInventoryExportData dto : list) { String key = dto.getProductName() + "|" + dto.getModel(); SalesLedgerProduct matchedProduct = productMap.get(key); if (matchedProduct != null) { if (dto.getQualifiedQuantity() != null && dto.getQualifiedQuantity().compareTo(BigDecimal.ZERO) > 0) { StockInventoryDto stockInventoryDto = new StockInventoryDto(); stockInventoryDto.setRecordId(0L); stockInventoryDto.setRecordType(StockInQualifiedRecordTypeEnum.CUSTOMIZATION_STOCK_IN.getCode()); stockInventoryDto.setQualitity(dto.getQualifiedQuantity()); stockInventoryDto.setRemark(dto.getRemark()); stockInventoryDto.setWarnNum(dto.getWarnNum()); stockInventoryDto.setBatchNo(dto.getQualifiedBatchNo()); stockInventoryDto.setProcessCategory(dto.getProcessCategory()); stockInventoryDto.setVoltage(dto.getVoltage()); if (ObjectUtils.isNotEmpty(dto.getQualifiedLockedQuantity())) { if (dto.getQualifiedLockedQuantity().compareTo(dto.getQualifiedQuantity()) > 0) { throw new RuntimeException("合格冻结数量不能超过本次导入的合格库存数量"); } stockInventoryDto.setLockedQuantity(dto.getQualifiedLockedQuantity()); } else { stockInventoryDto.setLockedQuantity(BigDecimal.ZERO); } stockInventoryDto.setProductModelId(matchedProduct.getProductModelId()); this.addstockInventory(stockInventoryDto); successCount++; } if (dto.getUnQualifiedQuantity() != null && dto.getUnQualifiedQuantity().compareTo(BigDecimal.ZERO) > 0) { StockUninventoryDto stockUninventoryDto = new StockUninventoryDto(); stockUninventoryDto.setRecordId(0L); stockUninventoryDto.setRecordType(StockInUnQualifiedRecordTypeEnum.CUSTOMIZATION_UNSTOCK_IN.getCode()); stockUninventoryDto.setQualitity(dto.getUnQualifiedQuantity()); stockUninventoryDto.setRemark(dto.getRemark()); stockUninventoryDto.setBatchNo(dto.getUnQualifiedBatchNo()); if (ObjectUtils.isNotEmpty(dto.getUnQualifiedLockedQuantity())) { if (dto.getUnQualifiedLockedQuantity().compareTo(dto.getUnQualifiedQuantity()) > 0) { throw new RuntimeException("不合格冻结数量不能超过本次导入的不合格库存数量"); } stockUninventoryDto.setLockedQuantity(dto.getUnQualifiedLockedQuantity()); } else { stockUninventoryDto.setLockedQuantity(BigDecimal.ZERO); } stockUninventoryDto.setProductModelId(matchedProduct.getProductModelId()); stockUninventoryService.addStockUninventory(stockUninventoryDto); successCount++; } } else { String unmatchedRecord = "产品名称:" + dto.getProductName() + ",型号:" + dto.getModel() + " 未匹配到库存产品"; unmatchedRecords.add(unmatchedRecord); } } } else { // 非成品导入(不包含工序类别和电压) ExcelUtil util = new ExcelUtil<>(NonFinishedProductInventoryExportData.class); List list = util.importExcel(file.getInputStream()); for (NonFinishedProductInventoryExportData dto : list) { String key = dto.getProductName() + "|" + dto.getModel(); SalesLedgerProduct matchedProduct = productMap.get(key); if (matchedProduct != null) { if (dto.getQualifiedQuantity() != null && dto.getQualifiedQuantity().compareTo(BigDecimal.ZERO) > 0) { StockInventoryDto stockInventoryDto = new StockInventoryDto(); stockInventoryDto.setRecordId(0L); stockInventoryDto.setRecordType(StockInQualifiedRecordTypeEnum.CUSTOMIZATION_STOCK_IN.getCode()); stockInventoryDto.setQualitity(dto.getQualifiedQuantity()); stockInventoryDto.setRemark(dto.getRemark()); stockInventoryDto.setWarnNum(dto.getWarnNum()); stockInventoryDto.setBatchNo(dto.getQualifiedBatchNo()); if (ObjectUtils.isNotEmpty(dto.getQualifiedLockedQuantity())) { if (dto.getQualifiedLockedQuantity().compareTo(dto.getQualifiedQuantity()) > 0) { throw new RuntimeException("合格冻结数量不能超过本次导入的合格库存数量"); } stockInventoryDto.setLockedQuantity(dto.getQualifiedLockedQuantity()); } else { stockInventoryDto.setLockedQuantity(BigDecimal.ZERO); } stockInventoryDto.setProductModelId(matchedProduct.getProductModelId()); this.addstockInventory(stockInventoryDto); successCount++; } if (dto.getUnQualifiedQuantity() != null && dto.getUnQualifiedQuantity().compareTo(BigDecimal.ZERO) > 0) { StockUninventoryDto stockUninventoryDto = new StockUninventoryDto(); stockUninventoryDto.setRecordId(0L); stockUninventoryDto.setRecordType(StockInUnQualifiedRecordTypeEnum.CUSTOMIZATION_UNSTOCK_IN.getCode()); stockUninventoryDto.setQualitity(dto.getUnQualifiedQuantity()); stockUninventoryDto.setRemark(dto.getRemark()); stockUninventoryDto.setBatchNo(dto.getUnQualifiedBatchNo()); if (ObjectUtils.isNotEmpty(dto.getUnQualifiedLockedQuantity())) { if (dto.getUnQualifiedLockedQuantity().compareTo(dto.getUnQualifiedQuantity()) > 0) { throw new RuntimeException("不合格冻结数量不能超过本次导入的不合格库存数量"); } stockUninventoryDto.setLockedQuantity(dto.getUnQualifiedLockedQuantity()); } else { stockUninventoryDto.setLockedQuantity(BigDecimal.ZERO); } stockUninventoryDto.setProductModelId(matchedProduct.getProductModelId()); stockUninventoryService.addStockUninventory(stockUninventoryDto); successCount++; } } else { String unmatchedRecord = "产品名称:" + dto.getProductName() + ",型号:" + dto.getModel() + " 未匹配到库存产品"; unmatchedRecords.add(unmatchedRecord); } } } StringBuilder message = new StringBuilder(); if (!unmatchedRecords.isEmpty()) { message.append("导入成功 ").append(successCount).append(" 条记录,以下产品未匹配:\n"); for (String record : unmatchedRecords) { message.append(record).append("\n"); } return R.ok(message.toString()); } return R.ok("导入成功,共处理 " + successCount + " 条记录"); } catch (Exception e) { log.error("库存导入失败", e); return R.fail("库存导入失败:" + e.getMessage()); } } @Override public void exportStockInventory(HttpServletResponse response, StockInventoryDto stockInventoryDto, Integer productType) { List list = stockInventoryMapper.listStockInventoryExportData(stockInventoryDto); // productType: 1=成品, 0=非成品 if (productType == 1) { // 成品导出(包含工序类别和电压) List finishedList = new ArrayList<>(); for (StockInventoryExportData data : list) { FinishedProductInventoryExportData finishedData = new FinishedProductInventoryExportData(); finishedData.setProductName(data.getProductName()); finishedData.setModel(data.getModel()); finishedData.setUnit(data.getUnit()); finishedData.setMaterialCode(data.getMaterialCode()); finishedData.setProcessCategory(data.getProcessCategory()); finishedData.setVoltage(data.getVoltage()); finishedData.setQualifiedBatchNo(data.getQualifiedBatchNo()); finishedData.setUnQualifiedBatchNo(data.getUnQualifiedBatchNo()); finishedData.setQualifiedQuantity(data.getQualifiedQuantity()); finishedData.setUnQualifiedQuantity(data.getUnQualifiedQuantity()); finishedData.setWarnNum(data.getWarnNum()); finishedData.setQualifiedLockedQuantity(data.getQualifiedLockedQuantity()); finishedData.setUnQualifiedLockedQuantity(data.getUnQualifiedLockedQuantity()); finishedData.setRemark(data.getRemark()); finishedList.add(finishedData); } ExcelUtil util = new ExcelUtil<>(FinishedProductInventoryExportData.class); util.exportExcel(response, finishedList, "成品库存信息"); } else { // 非成品导出(不包含工序类别和电压) List nonFinishedList = new ArrayList<>(); for (StockInventoryExportData data : list) { NonFinishedProductInventoryExportData nonFinishedData = new NonFinishedProductInventoryExportData(); nonFinishedData.setProductName(data.getProductName()); nonFinishedData.setModel(data.getModel()); nonFinishedData.setUnit(data.getUnit()); nonFinishedData.setMaterialCode(data.getMaterialCode()); nonFinishedData.setQualifiedBatchNo(data.getQualifiedBatchNo()); nonFinishedData.setUnQualifiedBatchNo(data.getUnQualifiedBatchNo()); nonFinishedData.setQualifiedQuantity(data.getQualifiedQuantity()); nonFinishedData.setUnQualifiedQuantity(data.getUnQualifiedQuantity()); nonFinishedData.setWarnNum(data.getWarnNum()); nonFinishedData.setQualifiedLockedQuantity(data.getQualifiedLockedQuantity()); nonFinishedData.setUnQualifiedLockedQuantity(data.getUnQualifiedLockedQuantity()); nonFinishedData.setRemark(data.getRemark()); nonFinishedList.add(nonFinishedData); } ExcelUtil util = new ExcelUtil<>(NonFinishedProductInventoryExportData.class); util.exportExcel(response, nonFinishedList, "非成品库存信息"); } } @Override public IPage stockInventoryPage(StockInventoryDto stockInventoryDto, Page page) { return stockInventoryMapper.stockInventoryPage(stockInventoryDto, page); } @Override public IPage stockInAndOutRecord(StockInventoryDto stockInventoryDto, Page page) { return stockInventoryMapper.stockInAndOutRecord(stockInventoryDto, page); } @Override public Boolean frozenStock(StockInventoryDto stockInventoryDto) { StockInventory stockInventory = stockInventoryMapper.selectById(stockInventoryDto.getId()); if (stockInventory.getQualitity().compareTo(stockInventoryDto.getLockedQuantity()) < 0) { throw new RuntimeException("冻结数量不能超过库存数量"); } if (ObjectUtils.isEmpty(stockInventory.getLockedQuantity())) { stockInventory.setLockedQuantity(stockInventoryDto.getLockedQuantity()); } else { stockInventory.setLockedQuantity(stockInventory.getLockedQuantity().add(stockInventoryDto.getLockedQuantity())); } return this.updateById(stockInventory); } @Override public Boolean thawStock(StockInventoryDto stockInventoryDto) { StockInventory stockInventory = stockInventoryMapper.selectById(stockInventoryDto.getId()); if (stockInventory.getLockedQuantity().compareTo(stockInventoryDto.getLockedQuantity()) < 0) { throw new RuntimeException("解冻数量不能超过冻结数量"); } stockInventory.setLockedQuantity(stockInventory.getLockedQuantity().subtract(stockInventoryDto.getLockedQuantity())); return this.updateById(stockInventory); } // 入库合并唯一键:成品按类别和电压,其余只按产品规格。 private StockInventory findInventoryForMerge(Long productModelId, String processCategory, String voltage) { LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper() .eq(StockInventory::getProductModelId, productModelId) .orderByAsc(StockInventory::getId); if (usesDimensionIdentity(processCategory, voltage)) { queryWrapper.eq(StockInventory::getProcessCategory, normalizeDimension(processCategory)); queryWrapper.eq(StockInventory::getVoltage, normalizeDimension(voltage)); } List inventories = stockInventoryMapper.selectList(queryWrapper); return inventories.isEmpty() ? null : inventories.get(0); } // 将入库记录 DTO 复制为持久化实体。 private StockInRecord toStockInRecord(StockInRecordDto stockInRecordDto) { StockInRecord stockInRecord = new StockInRecord(); stockInRecord.setRecordId(stockInRecordDto.getRecordId()); stockInRecord.setRecordType(stockInRecordDto.getRecordType()); stockInRecord.setStockInNum(stockInRecordDto.getStockInNum()); stockInRecord.setProductModelId(stockInRecordDto.getProductModelId()); stockInRecord.setRemark(stockInRecordDto.getRemark()); stockInRecord.setType(stockInRecordDto.getType()); stockInRecord.setLockedQuantity(stockInRecordDto.getLockedQuantity()); stockInRecord.setWarnNum(stockInRecordDto.getWarnNum()); stockInRecord.setApproveStatus(stockInRecordDto.getApproveStatus()); stockInRecord.setBatchNo(stockInRecordDto.getBatchNo()); stockInRecord.setProcessCategory(stockInRecordDto.getProcessCategory()); stockInRecord.setVoltage(stockInRecordDto.getVoltage()); return stockInRecord; } // 只要带有成品维度,就按维度作为唯一键。 private boolean usesDimensionIdentity(String processCategory, String voltage) { return StringUtils.isNotBlank(processCategory) || StringUtils.isNotBlank(voltage); } private String normalizeDimension(String value) { return StringUtils.trimToEmpty(value); } private BigDecimal defaultDecimal(BigDecimal value) { return value == null ? BigDecimal.ZERO : value; } }