tuqin
2026-09-24 0891b6708cc09c2672a19661a8555aae0e742df1
src/main/java/com/ruoyi/stock/service/impl/StockInRecordServiceImpl.java
@@ -4,13 +4,25 @@
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.enums.ReviewStatusEnum;
import com.ruoyi.common.enums.StockInQualifiedRecordTypeEnum;
import com.ruoyi.common.enums.StockOutQualifiedRecordTypeEnum;
import com.ruoyi.common.exception.base.BaseException;
import com.ruoyi.common.utils.EnumUtil;
import com.ruoyi.common.utils.OrderUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.bean.BeanUtils;
import com.ruoyi.staff.pojo.StaffOnJob;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.stock.dto.StockInRecordDto;
import com.ruoyi.stock.dto.StockInRecordImportDto;
import com.ruoyi.basic.mapper.ProductModelMapper;
import com.ruoyi.basic.pojo.ProductModel;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.stock.dto.StockInventoryDto;
import com.ruoyi.stock.dto.StockUninventoryDto;
import com.ruoyi.stock.execl.StockInRecordExportData;
import com.ruoyi.production.mapper.ProductionOrderPickMapper;
import com.ruoyi.production.pojo.ProductionOrderPick;
import com.ruoyi.stock.mapper.StockInRecordMapper;
import com.ruoyi.stock.mapper.StockInventoryMapper;
import com.ruoyi.stock.mapper.StockUninventoryMapper;
@@ -18,20 +30,26 @@
import com.ruoyi.stock.pojo.StockInventory;
import com.ruoyi.stock.pojo.StockUninventory;
import com.ruoyi.stock.service.StockInRecordService;
import org.springframework.beans.factory.annotation.Autowired;
import jakarta.servlet.http.HttpServletResponse;
import lombok.AllArgsConstructor;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
@Service
@AllArgsConstructor
public class StockInRecordServiceImpl extends ServiceImpl<StockInRecordMapper, StockInRecord> implements StockInRecordService {
    @Autowired
    private StockInRecordMapper stockInRecordMapper;
    @Autowired
    private StockInventoryMapper stockInventoryMapper;
    @Autowired
    private StockUninventoryMapper stockUninventoryMapper;
    private ProductionOrderPickMapper productionOrderPickMapper;
    private ProductModelMapper productModelMapper;
    @Override
    public IPage<StockInRecordDto> listPage(Page page, StockInRecordDto stockInRecordDto) {
@@ -41,12 +59,13 @@
    // 新增入库
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int add(StockInRecordDto stockInRecordDto) {
        String no = OrderUtils.countTodayByCreateTime(stockInRecordMapper, "RK");
    public StockInRecord add(StockInRecordDto stockInRecordDto) {
        String no = OrderUtils.countTodayByCreateTime(stockInRecordMapper, "RK","inbound_batches");
        stockInRecordDto.setInboundBatches(no);
        StockInRecord stockInRecord = new StockInRecord();
        BeanUtils.copyProperties(stockInRecordDto, stockInRecord);
        return stockInRecordMapper.insert(stockInRecord);
        stockInRecordMapper.insert(stockInRecord);
        return stockInRecord;
    }
    @Override
@@ -58,9 +77,56 @@
            throw new BaseException("该入库记录不存在,无法更新!!!");
        }
        // 记录修改前的 batch_no
        String oldBatchNo = stockInRecord.getBatchNo();
        String newBatchNo = stockInRecordDto.getBatchNo();
        String[] ignoreProperties = {"id", "inbound_batches"};//排除id属性
        BeanUtils.copyProperties(stockInRecordDto, stockInRecord, ignoreProperties);
        return stockInRecordMapper.updateById(stockInRecord);
        int result = stockInRecordMapper.updateById(stockInRecord);
        // 如果 batch_no 发生变化,需要同步更新关联表
        if (newBatchNo != null && !newBatchNo.equals(oldBatchNo)) {
            updateRelatedBatchNo(stockInRecord, oldBatchNo, newBatchNo);
        }
        return result;
    }
    /**
     * 同步更新关联表的 batch_no
     * @param stockInRecord 入库记录
     * @param oldBatchNo 修改前的批号
     * @param newBatchNo 修改后的批号
     */
    private void updateRelatedBatchNo(StockInRecord stockInRecord, String oldBatchNo, String newBatchNo) {
        // 1. 更新 stock_inventory 表(合格库存)
        LambdaQueryWrapper<StockInventory> inventoryEq = new LambdaQueryWrapper<StockInventory>()
                .eq(StockInventory::getProductModelId, stockInRecord.getProductModelId());
        if (StringUtils.isEmpty(oldBatchNo)) {
            inventoryEq.isNull(StockInventory::getBatchNo);
        } else {
            inventoryEq.eq(StockInventory::getBatchNo, oldBatchNo);
        }
        StockInventory stockInventory = stockInventoryMapper.selectOne(inventoryEq);
        if (stockInventory != null) {
            stockInventory.setBatchNo(newBatchNo);
            stockInventoryMapper.updateById(stockInventory);
        }
        // 2. 更新 stock_uninventory 表(不合格库存)
        LambdaQueryWrapper<StockUninventory> uninventoryEq = new LambdaQueryWrapper<StockUninventory>()
                .eq(StockUninventory::getProductModelId, stockInRecord.getProductModelId());
        if (StringUtils.isEmpty(oldBatchNo)) {
            uninventoryEq.isNull(StockUninventory::getBatchNo);
        } else {
            uninventoryEq.eq(StockUninventory::getBatchNo, oldBatchNo);
        }
        StockUninventory stockUninventory = stockUninventoryMapper.selectOne(uninventoryEq);
        if (stockUninventory != null) {
            stockUninventory.setBatchNo(newBatchNo);
            stockUninventoryMapper.updateById(stockUninventory);
        }
    }
    @Override
@@ -69,27 +135,315 @@
        for (Long id : ids) {
            StockInRecord stockInRecord = stockInRecordMapper.selectById(id);
            if (stockInRecord.getType().equals("0")) {
                StockInventory stockInventory = stockInventoryMapper.selectOne(new LambdaQueryWrapper<StockInventory>().eq(StockInventory::getProductModelId, stockInRecord.getProductModelId()));
                LambdaQueryWrapper<StockInventory> eq = new LambdaQueryWrapper<StockInventory>()
                        .eq(StockInventory::getProductModelId, stockInRecord.getProductModelId());
                if (StringUtils.isEmpty(stockInRecord.getBatchNo())) {
                    eq.isNull(StockInventory::getBatchNo);
                } else {
                    eq.eq(StockInventory::getBatchNo, stockInRecord.getBatchNo());
                }
                StockInventory stockInventory = stockInventoryMapper.selectOne(eq);
                if (stockInventory == null) {
                    throw new BaseException("库存记录中没有对应的产品,无法删除!!!");
                }else {
                    StockInventoryDto stockInRecordDto = new StockInventoryDto();
                    stockInRecordDto.setProductModelId(stockInventory.getProductModelId());
                    stockInRecordDto.setBatchNo(stockInventory.getBatchNo());
                    stockInRecordDto.setQualitity(stockInRecord.getStockInNum());
                    stockInventoryMapper.updateAddStockInventory(stockInRecordDto);
                    stockInventoryMapper.updateSubtractStockInventory(stockInRecordDto);
                }
            }else if (stockInRecord.getType().equals("1")) {
                StockUninventory stockUninventory = stockUninventoryMapper.selectOne(new LambdaQueryWrapper<StockUninventory>().eq(StockUninventory::getProductModelId, stockInRecord.getProductModelId()));
                LambdaQueryWrapper<StockUninventory> eq = new LambdaQueryWrapper<StockUninventory>()
                        .eq(StockUninventory::getProductModelId, stockInRecord.getProductModelId());
                if (StringUtils.isEmpty(stockInRecord.getBatchNo())) {
                    eq.isNull(StockUninventory::getBatchNo);
                } else {
                    eq.eq(StockUninventory::getBatchNo, stockInRecord.getBatchNo());
                }
                StockUninventory stockUninventory = stockUninventoryMapper.selectOne(eq);
                if (stockUninventory == null) {
                    throw new BaseException("库存记录中没有对应的产品,无法删除!!!");
                }else {
                    StockUninventoryDto stockUninventoryDto = new StockUninventoryDto();
                    stockUninventoryDto.setProductModelId(stockUninventory.getProductModelId());
                    stockUninventoryDto.setBatchNo(stockUninventory.getBatchNo());
                    stockUninventoryDto.setQualitity(stockInRecord.getStockInNum());
                    stockUninventoryMapper.updateAddStockUnInventory(stockUninventoryDto);
                    stockUninventoryMapper.updateSubtractStockUnInventory(stockUninventoryDto);
                }
            }
        }
        return stockInRecordMapper.deleteBatchIds(ids);
    }
    @Override
    public void exportStockInRecord(HttpServletResponse response, StockInRecordDto stockInRecordDto) {
        List<StockInRecordExportData> list = stockInRecordMapper.listStockInRecordExportData(stockInRecordDto);
        for (StockInRecordExportData stockInRecordExportData : list) {
            if (!stockInRecordExportData.getType().equals("0")) {
                stockInRecordExportData.setRecordType(EnumUtil.fromCode(StockOutQualifiedRecordTypeEnum.class, Integer.parseInt(stockInRecordExportData.getRecordType())).getValue());
            }else {
                stockInRecordExportData.setRecordType(EnumUtil.fromCode(StockInQualifiedRecordTypeEnum.class, Integer.parseInt(stockInRecordExportData.getRecordType())).getValue());
            }
        }
        ExcelUtil<StockInRecordExportData> util = new ExcelUtil<>(StockInRecordExportData.class);
        util.exportExcel(response,list, "入库记录信息");
    }
    private StockInventory getStockInventory(Long productModelId, String batchNo) {
        LambdaQueryWrapper<StockInventory> eq = new LambdaQueryWrapper<>();
        eq.eq(StockInventory::getProductModelId, productModelId);
        if (StringUtils.isEmpty(batchNo)) {
            eq.isNull(StockInventory::getBatchNo);
        } else {
            eq.eq(StockInventory::getBatchNo, batchNo);
        }
        return stockInventoryMapper.selectOne(eq);
    }
    private StockUninventory getStockUninventory(Long productModelId, String batchNo) {
        LambdaQueryWrapper<StockUninventory> eq = new LambdaQueryWrapper<>();
        eq.eq(StockUninventory::getProductModelId, productModelId);
        if (StringUtils.isEmpty(batchNo)) {
            eq.isNull(StockUninventory::getBatchNo);
        } else {
            eq.eq(StockUninventory::getBatchNo, batchNo);
        }
        return stockUninventoryMapper.selectOne(eq);
    }
    /**
     * 回滚生产退料入库的领料记录退料数量
     * @param stockInRecord 入库记录
     */
    private void rollbackFeedReturnQty(StockInRecord stockInRecord) {
        ProductionOrderPick productionOrderPick = productionOrderPickMapper.selectById(stockInRecord.getRecordId());
        if (productionOrderPick != null) {
            BigDecimal returnQty = productionOrderPick.getReturnQty();
            if (returnQty == null) {
                returnQty = BigDecimal.ZERO;
            }
            BigDecimal newReturnQty = returnQty.subtract(stockInRecord.getStockInNum());
            if (newReturnQty.compareTo(BigDecimal.ZERO) < 0) {
                newReturnQty = BigDecimal.ZERO;
            }
            productionOrderPick.setReturnQty(newReturnQty);
            // 重新计算实际用量
            BigDecimal actualQty = productionOrderPick.getQuantity().add(
                productionOrderPick.getFeedingQty() != null ? productionOrderPick.getFeedingQty() : BigDecimal.ZERO)
                .subtract(newReturnQty);
            productionOrderPick.setActualQty(actualQty);
            productionOrderPick.setReturned(newReturnQty.compareTo(BigDecimal.ZERO) > 0);
            productionOrderPickMapper.updateById(productionOrderPick);
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int batchDeletePending(List<Long> ids) {
        for (Long id : ids) {
            StockInRecord stockInRecord = stockInRecordMapper.selectById(id);
            if (stockInRecord == null) {
                throw new BaseException("入库记录不存在,无法删除!!!");
            }
            if (stockInRecord.getApprovalStatus() != null && !ReviewStatusEnum.PENDING_REVIEW.getCode().equals(stockInRecord.getApprovalStatus())) {
                throw new BaseException("只有待审批状态的记录才能删除,入库批次:" + stockInRecord.getInboundBatches());
            }
            // 如果是生产退料入库,删除时需要回滚领料记录的退料数量
            if (StockInQualifiedRecordTypeEnum.FEED_RETURN_IN.getCode().equals(stockInRecord.getRecordType())) {
                rollbackFeedReturnQty(stockInRecord);
            }
        }
        return stockInRecordMapper.deleteBatchIds(ids);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int batchApprove(List<Long> ids, Integer approvalStatus) {
        if (CollectionUtils.isEmpty(ids)) {
            throw new BaseException("请选择至少一条数据");
        }
        if (approvalStatus == null || (!ReviewStatusEnum.APPROVED.getCode().equals(approvalStatus) && !ReviewStatusEnum.REJECTED.getCode().equals(approvalStatus))) {
            throw new BaseException("审批状态值无效");
        }
        for (Long id : ids) {
            StockInRecord stockInRecord = stockInRecordMapper.selectById(id);
            if (stockInRecord == null) {
                throw new BaseException("入库记录不存在,无法审批!!!");
            }
            /*if (stockInRecord.getApprovalStatus() != null && !ReviewStatusEnum.PENDING_REVIEW.getCode().equals(stockInRecord.getApprovalStatus())) {
                throw new BaseException("只有待审批状态的记录才能审批,入库批次:" + stockInRecord.getInboundBatches());
            }*/
            stockInRecord.setApprovalStatus(approvalStatus);
            stockInRecordMapper.updateById(stockInRecord);
            // 审批驳回时,如果是生产退料入库,需要回滚领料记录的退料数量
            if (ReviewStatusEnum.REJECTED.getCode().equals(approvalStatus) &&
                StockInQualifiedRecordTypeEnum.FEED_RETURN_IN.getCode().equals(stockInRecord.getRecordType())) {
                rollbackFeedReturnQty(stockInRecord);
            }
            // 审批通过时,库存增加
            if (ReviewStatusEnum.APPROVED.getCode().equals(approvalStatus)) {
                if ("0".equals(stockInRecord.getType())) {
                    // 合格入库 -> 先查库存,存在则更新,不存在则新增
                    StockInventory stockInventory = getStockInventory(stockInRecord.getProductModelId(), stockInRecord.getBatchNo());
                    StockInventoryDto stockInventoryDto = new StockInventoryDto();
                    stockInventoryDto.setProductModelId(stockInRecord.getProductModelId());
                    stockInventoryDto.setBatchNo(stockInRecord.getBatchNo());
                    stockInventoryDto.setQualitity(stockInRecord.getStockInNum());
                    stockInventoryDto.setRemark(stockInRecord.getRemark());
                    stockInventoryDto.setWarnNum(stockInRecord.getWarnNum());
                    if (stockInventory == null) {
                        stockInventoryMapper.insert(new StockInventory() {{
                            setProductModelId(stockInRecord.getProductModelId());
                            setQualitity(stockInRecord.getStockInNum());
                            setWarnNum(stockInRecord.getWarnNum());
                            setBatchNo(stockInRecord.getBatchNo());
                            setRemark(stockInRecord.getRemark());
                            setVersion(1);
                        }});
                    } else {
                        stockInventoryMapper.updateAddStockInventory(stockInventoryDto);
                    }
                } else if ("1".equals(stockInRecord.getType())) {
                    // 不合格入库 -> 先查库存,存在则更新,不存在则新增
                    StockUninventory stockUninventory = getStockUninventory(stockInRecord.getProductModelId(), stockInRecord.getBatchNo());
                    StockUninventoryDto stockUninventoryDto = new StockUninventoryDto();
                    stockUninventoryDto.setProductModelId(stockInRecord.getProductModelId());
                    stockUninventoryDto.setBatchNo(stockInRecord.getBatchNo());
                    stockUninventoryDto.setQualitity(stockInRecord.getStockInNum());
                    stockUninventoryDto.setRemark(stockInRecord.getRemark());
                    if (stockUninventory == null) {
                        stockUninventoryMapper.insert(new StockUninventory() {{
                            setProductModelId(stockInRecord.getProductModelId());
                            setQualitity(stockInRecord.getStockInNum());
                            setBatchNo(stockInRecord.getBatchNo());
                            setRemark(stockInRecord.getRemark());
                            setVersion(1);
                        }});
                    } else {
                        stockUninventoryMapper.updateAddStockUnInventory(stockUninventoryDto);
                    }
                }
            }
        }
        return ids.size();
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int batchReAudit(List<Long> ids) {
        if (CollectionUtils.isEmpty(ids)) {
            throw new BaseException("请选择至少一条数据");
        }
        for (Long id : ids) {
            StockInRecord stockInRecord = stockInRecordMapper.selectById(id);
            if (stockInRecord == null) {
                throw new BaseException("入库记录不存在,无法重新审核!!!");
            }
            // 只有驳回状态才能重新审核
            if (!ReviewStatusEnum.REJECTED.getCode().equals(stockInRecord.getApprovalStatus())) {
                throw new BaseException("只有驳回状态的记录才能重新审核,入库批次:" + stockInRecord.getInboundBatches());
            }
            // 如果是生产退料入库,恢复退料数量(因为驳回时已扣减)
            if (StockInQualifiedRecordTypeEnum.FEED_RETURN_IN.getCode().equals(stockInRecord.getRecordType())) {
                ProductionOrderPick productionOrderPick = productionOrderPickMapper.selectById(stockInRecord.getRecordId());
                if (productionOrderPick != null) {
                    BigDecimal returnQty = productionOrderPick.getReturnQty();
                    if (returnQty == null) {
                        returnQty = BigDecimal.ZERO;
                    }
                    // 重新审核时恢复退料数量
                    BigDecimal newReturnQty = returnQty.add(stockInRecord.getStockInNum());
                    productionOrderPick.setReturnQty(newReturnQty);
                    // 重新计算实际用量
                    BigDecimal actualQty = productionOrderPick.getQuantity().add(
                        productionOrderPick.getFeedingQty() != null ? productionOrderPick.getFeedingQty() : BigDecimal.ZERO)
                        .subtract(newReturnQty);
                    productionOrderPick.setActualQty(actualQty);
                    productionOrderPick.setReturned(newReturnQty.compareTo(BigDecimal.ZERO) > 0);
                    productionOrderPickMapper.updateById(productionOrderPick);
                }
            }
            // 将状态改为待审核
            stockInRecord.setApprovalStatus(ReviewStatusEnum.PENDING_REVIEW.getCode());
            stockInRecordMapper.updateById(stockInRecord);
        }
        return ids.size();
    }
    @Override
    public AjaxResult importData(MultipartFile file, String type) {
        List<StockInRecordImportDto> rows;
        try {
            ExcelUtil<StockInRecordImportDto> util = new ExcelUtil<>(StockInRecordImportDto.class);
            rows = util.importExcel(file.getInputStream());
        } catch (Exception e) {
            return AjaxResult.error("解析Excel失败:" + e.getMessage());
        }
        if (CollectionUtils.isEmpty(rows)) {
            return AjaxResult.error("导入文件无数据");
        }
        String stockType = StringUtils.isEmpty(type) ? "0" : type.trim();
        String recordType = "1".equals(stockType)
                ? StockInQualifiedRecordTypeEnum.CUSTOMIZATION_UNSTOCK_IN.getCode()
                : StockInQualifiedRecordTypeEnum.CUSTOMIZATION_STOCK_IN.getCode();
        int success = 0;
        List<String> errors = new ArrayList<>();
        for (int i = 0; i < rows.size(); i++) {
            StockInRecordImportDto row = rows.get(i);
            int rowNo = i + 2; // 首行为表头
            try {
                String productName = trim(row.getProductName());
                String model = trim(row.getModel());
                if (StringUtils.isEmpty(productName)) {
                    errors.add("第" + rowNo + "行:产品大类为空");
                    continue;
                }
                if (StringUtils.isEmpty(model)) {
                    errors.add("第" + rowNo + "行:规格型号为空");
                    continue;
                }
                if (row.getStockInNum() == null) {
                    errors.add("第" + rowNo + "行:入库数量为空");
                    continue;
                }
                ProductModel productModel = productModelMapper.selectByProductNameAndModel(productName, model);
                if (productModel == null) {
                    errors.add("第" + rowNo + "行:未找到产品[" + productName + " / " + model + "]");
                    continue;
                }
                StockInRecordDto dto = new StockInRecordDto();
                dto.setProductModelId(productModel.getId());
                dto.setBatchNo(trim(row.getBatchNo()));
                dto.setStockInNum(row.getStockInNum());
                dto.setRemark(row.getRemark());
                dto.setType(stockType);
                dto.setRecordType(recordType);
                dto.setApprovalStatus(ReviewStatusEnum.PENDING_REVIEW.getCode());
                add(dto);
                success++;
            } catch (Exception e) {
                errors.add("第" + rowNo + "行:" + e.getMessage());
            }
        }
        AjaxResult result = AjaxResult.success();
        result.put("totalCount", rows.size());
        result.put("successCount", success);
        result.put("failCount", rows.size() - success);
        result.put("errors", errors);
        result.put("msg", "导入完成:成功 " + success + " 条,失败 " + (rows.size() - success) + " 条");
        return result;
    }
    private String trim(String value) {
        return value == null ? null : value.trim();
    }
}