huminmin
2026-07-15 422a5676c2d96dd37c1be62935a314d70fef79b5
src/main/java/com/ruoyi/quality/service/impl/QualityUnqualifiedServiceImpl.java
@@ -6,37 +6,33 @@
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.enums.StockInQualifiedRecordTypeEnum;
import com.ruoyi.common.enums.StockInUnQualifiedRecordTypeEnum;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.bean.BeanUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.procurementrecord.utils.StockUtils;
import com.ruoyi.production.mapper.ProductionOperationTaskMapper;
import com.ruoyi.production.mapper.ProductionOrderMapper;
import com.ruoyi.production.mapper.ProductionOrderRoutingMapper;
import com.ruoyi.production.mapper.ProductionOrderRoutingOperationMapper;
import com.ruoyi.production.mapper.ProductionProductMainMapper;
import com.ruoyi.production.service.ProductionAccountService;
import com.ruoyi.production.pojo.ProductionOperationTask;
import com.ruoyi.production.pojo.ProductionOrder;
import com.ruoyi.production.pojo.ProductionOrderRouting;
import com.ruoyi.production.pojo.ProductionOrderRoutingOperation;
import com.ruoyi.production.pojo.ProductionProductMain;
import com.ruoyi.quality.mapper.QualityUnqualifiedMapper;
import com.ruoyi.quality.pojo.QualityInspect;
import com.ruoyi.quality.pojo.QualityUnqualified;
import com.ruoyi.stock.pojo.StockInRecord;
import com.ruoyi.stock.service.StockInRecordService;
import com.ruoyi.quality.service.IQualityInspectService;
import com.ruoyi.quality.service.IQualityUnqualifiedService;
import com.ruoyi.stock.service.StockUninventoryService;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import jakarta.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
@AllArgsConstructor
@Service
@@ -46,11 +42,10 @@
    private final QualityUnqualifiedMapper qualityUnqualifiedMapper;
    private final IQualityInspectService qualityInspectService;
    private final ProductionProductMainMapper productionProductMainMapper;
    private final ProductionOrderMapper productionOrderMapper;
    private final ProductionOrderRoutingMapper productionOrderRoutingMapper;
    private final ProductionOrderRoutingOperationMapper productionOrderRoutingOperationMapper;
    private final ProductionOperationTaskMapper productionOperationTaskMapper;
    private final StockUninventoryService stockUninventoryService;
    private final StockInRecordService stockInRecordService;
    private final ProductionAccountService productionAccountService;
    @Override
    public IPage<QualityUnqualified> qualityUnqualifiedListPage(Page page, QualityUnqualified qualityUnqualified) {
@@ -65,28 +60,27 @@
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int deal(QualityUnqualified qualityUnqualified) {
        QualityUnqualified unqualified = qualityUnqualifiedMapper.selectById(qualityUnqualified.getId());
        QualityInspect qualityInspect = qualityInspectService.getById(unqualified.getInspectId());
        String batchNo = qualityInspect == null ? null
                : resolveProductionBatchNo(qualityInspect.getProductMainId(), qualityInspect.getId(), qualityInspect.getProductModelId());
        if (ObjectUtils.isNotNull(qualityInspect) && qualityInspect.getInspectType() != 0) {
            ProductionProductMain sourceMain = null;
            if (ObjectUtils.isNotNull(qualityInspect.getProductMainId())) {
                sourceMain = productionProductMainMapper.selectById(qualityInspect.getProductMainId());
            }
            switch (qualityUnqualified.getDealResult()) {
                case "返修":
                case "返工":
                    if (ObjectUtils.isNotNull(qualityInspect.getProductMainId())) {
                        ProductionProductMain sourceMain = productionProductMainMapper.selectById(qualityInspect.getProductMainId());
                        if (sourceMain == null || sourceMain.getProductionOperationTaskId() == null) {
                            throw new ServiceException("历史报工未绑定生产工单,无法按新模型返修/返工");
                        }
                        createReworkProductionByNewModel(sourceMain, unqualified.getQuantity());
                    }
                case "报工":
                    handleProductionReportResult(qualityUnqualified, unqualified, sourceMain);
                    break;
                case "报废":
                    stockUtils.addUnStock(qualityInspect.getProductModelId(), unqualified.getQuantity(),
                            StockInUnQualifiedRecordTypeEnum.DEFECTIVE_SCRAP.getCode(), unqualified.getId());
                    handleProductionScrapResult(qualityInspect, qualityUnqualified, unqualified, sourceMain, batchNo);
                    break;
                case "让步放行":
                    stockUtils.addStock(qualityInspect.getProductModelId(), unqualified.getQuantity(),
                            StockInQualifiedRecordTypeEnum.DEFECTIVE_PASS.getCode(), unqualified.getId());
                    stockUtils.addStockWithBatchNo(qualityInspect.getProductModelId(), unqualified.getQuantity(),
                            StockInQualifiedRecordTypeEnum.DEFECTIVE_PASS.getCode(), unqualified.getId(), batchNo);
                    break;
                default:
                    break;
@@ -96,7 +90,7 @@
            switch (qualityUnqualified.getDealResult()) {
                case "报废":
                    stockUtils.addUnStock(modelId, unqualified.getQuantity(),
                            StockInUnQualifiedRecordTypeEnum.DEFECTIVE_SCRAP.getCode(), unqualified.getId());
                            StockInQualifiedRecordTypeEnum.DEFECTIVE_SCRAP.getCode(), unqualified.getId());
                    break;
                case "让步放行":
                    stockUtils.addStock(modelId, unqualified.getQuantity(),
@@ -115,87 +109,87 @@
        return qualityUnqualifiedMapper.getUnqualified(id);
    }
    private void handleProductionReportResult(QualityUnqualified qualityUnqualified,
                                              QualityUnqualified sourceUnqualified,
                                              ProductionProductMain sourceMain) {
        Integer dealType = qualityUnqualified.getDealType();
        if (dealType == null) {
            throw new ServiceException("报工时必须选择不合格类型");
        }
        if (sourceMain == null || sourceMain.getProductionOperationTaskId() == null) {
            throw new ServiceException("历史报工未绑定生产工单,无法执行报工处理");
        }
        if (Integer.valueOf(0).equals(dealType)) {
            applyLightRework(sourceMain, qualityUnqualified);
            return;
        }
        if (Integer.valueOf(1).equals(dealType)) {
            markSourceMainAsUnpaid(sourceMain, 2);
            createReworkProductionByNewModel(sourceMain, sourceUnqualified.getQuantity());
            qualityUnqualified.setRegenerateNewOrder(1);
            productionAccountService.rebuildByProductionProductMainId(sourceMain.getId());
            return;
        }
        throw new ServiceException("报工处理仅支持轻微返工或严重返工");
    }
    private void handleProductionScrapResult(QualityInspect qualityInspect,
                                             QualityUnqualified qualityUnqualified,
                                             QualityUnqualified sourceUnqualified,
                                             ProductionProductMain sourceMain,
                                             String batchNo) {
        if (sourceMain == null || sourceMain.getProductionOperationTaskId() == null) {
            throw new ServiceException("历史报工未绑定生产工单,无法执行报废处理");
        }
        markSourceMainAsUnpaid(sourceMain, 3);
        createReworkProductionByNewModel(sourceMain, sourceUnqualified.getQuantity());
        qualityUnqualified.setRegenerateNewOrder(1);
        productionAccountService.rebuildByProductionProductMainId(sourceMain.getId());
        // 原有报废库存处理逻辑先按新规则注释,不再直接按不合格单执行报废出库。
        // stockUtils.addUnStockWithBatchNo(qualityInspect.getProductModelId(), sourceUnqualified.getQuantity(),
        //         StockInQualifiedRecordTypeEnum.DEFECTIVE_SCRAP.getCode(), sourceUnqualified.getId(), batchNo);
    }
    private void applyLightRework(ProductionProductMain sourceMain, QualityUnqualified qualityUnqualified) {
        if (qualityUnqualified.getRepairWorkHour() == null
                || qualityUnqualified.getRepairWorkHour().compareTo(BigDecimal.ZERO) <= 0) {
            throw new ServiceException("轻微返工必须填写大于0的修理工时");
        }
        ProductionProductMain update = new ProductionProductMain();
        update.setId(sourceMain.getId());
        update.setReportType(1);
        update.setRepairWorkHour(defaultDecimal(sourceMain.getRepairWorkHour()).add(defaultDecimal(qualityUnqualified.getRepairWorkHour())));
        productionProductMainMapper.updateById(update);
        qualityUnqualified.setRegenerateNewOrder(0);
        productionAccountService.rebuildByProductionProductMainId(sourceMain.getId());
    }
    private void markSourceMainAsUnpaid(ProductionProductMain sourceMain, int reportType) {
        ProductionProductMain update = new ProductionProductMain();
        update.setId(sourceMain.getId());
        update.setReportType(reportType);
        productionProductMainMapper.updateById(update);
    }
    private void createReworkProductionByNewModel(ProductionProductMain sourceMain, BigDecimal quantity) {
        ProductionOperationTask sourceTask = productionOperationTaskMapper.selectById(sourceMain.getProductionOperationTaskId());
        if (sourceTask == null) {
            throw new ServiceException("关联的生产工单不存在");
        }
        ProductionOrder sourceOrder = productionOrderMapper.selectById(sourceTask.getProductionOrderId());
        if (sourceOrder == null) {
            throw new ServiceException("关联的生产订单不存在");
        }
        ProductionOrder newOrder = new ProductionOrder();
        BeanUtils.copyProperties(sourceOrder, newOrder);
        newOrder.setId(null);
        newOrder.setNpsNo(generateNextProductionOrderNo("FG"));
        newOrder.setQuantity(defaultDecimal(quantity));
        newOrder.setCompleteQuantity(BigDecimal.ZERO);
        newOrder.setStartTime(null);
        newOrder.setEndTime(null);
        newOrder.setCreateTime(null);
        newOrder.setUpdateTime(null);
        productionOrderMapper.insert(newOrder);
        Map<Long, Long> routingIdMap = new HashMap<>();
        List<ProductionOrderRouting> sourceRoutings = productionOrderRoutingMapper.selectList(
                Wrappers.<ProductionOrderRouting>lambdaQuery()
                        .eq(ProductionOrderRouting::getProductionOrderId, sourceOrder.getId())
                        .orderByAsc(ProductionOrderRouting::getId));
        for (ProductionOrderRouting sourceRouting : sourceRoutings) {
            ProductionOrderRouting newRouting = new ProductionOrderRouting();
            BeanUtils.copyProperties(sourceRouting, newRouting);
            newRouting.setId(null);
            newRouting.setProductionOrderId(newOrder.getId());
            newRouting.setCreateTime(null);
            newRouting.setUpdateTime(null);
            productionOrderRoutingMapper.insert(newRouting);
            routingIdMap.put(sourceRouting.getId(), newRouting.getId());
        }
        List<ProductionOrderRoutingOperation> sourceOperations = productionOrderRoutingOperationMapper.selectList(
                Wrappers.<ProductionOrderRoutingOperation>lambdaQuery()
                        .eq(ProductionOrderRoutingOperation::getProductionOrderId, sourceOrder.getId())
                        .orderByAsc(ProductionOrderRoutingOperation::getDragSort)
                        .orderByAsc(ProductionOrderRoutingOperation::getId));
        for (ProductionOrderRoutingOperation sourceOperation : sourceOperations) {
            ProductionOrderRoutingOperation newOperation = new ProductionOrderRoutingOperation();
            BeanUtils.copyProperties(sourceOperation, newOperation);
            newOperation.setId(null);
            newOperation.setProductionOrderId(newOrder.getId());
            newOperation.setOrderRoutingId(routingIdMap.get(sourceOperation.getOrderRoutingId()));
            newOperation.setCreateTime(null);
            newOperation.setUpdateTime(null);
            productionOrderRoutingOperationMapper.insert(newOperation);
            ProductionOperationTask newTask = new ProductionOperationTask();
            newTask.setProductionOrderRoutingOperationId(newOperation.getId());
            newTask.setProductionOrderId(newOrder.getId());
            newTask.setPlanQuantity(newOrder.getQuantity());
            newTask.setCompleteQuantity(BigDecimal.ZERO);
            newTask.setWorkOrderNo(generateNextTaskNo("FG"));
            newTask.setStatus(1);
            productionOperationTaskMapper.insert(newTask);
        }
    }
    private String generateNextProductionOrderNo(String prefix) {
        String datePrefix = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
        String orderPrefix = prefix + datePrefix;
        ProductionOrder latestOrder = productionOrderMapper.selectOne(
                Wrappers.<ProductionOrder>lambdaQuery()
                        .likeRight(ProductionOrder::getNpsNo, orderPrefix)
                        .orderByDesc(ProductionOrder::getNpsNo)
                        .last("limit 1"));
        int sequence = 1;
        if (latestOrder != null && latestOrder.getNpsNo() != null && latestOrder.getNpsNo().startsWith(orderPrefix)) {
            try {
                sequence = Integer.parseInt(latestOrder.getNpsNo().substring(orderPrefix.length())) + 1;
            } catch (NumberFormatException ignored) {
                sequence = 1;
            }
        }
        return orderPrefix + String.format("%04d", sequence);
        ProductionOperationTask newTask = new ProductionOperationTask();
        BeanUtils.copyProperties(sourceTask, newTask);
        newTask.setId(null);
        newTask.setWorkOrderNo(generateNextTaskNo("FG"));
        newTask.setPlanQuantity(defaultDecimal(quantity));
        newTask.setCompleteQuantity(BigDecimal.ZERO);
        newTask.setPlanStartTime(null);
        newTask.setPlanEndTime(null);
        newTask.setActualStartTime(null);
        newTask.setActualEndTime(null);
        newTask.setStatus(1);
        newTask.setCreateTime(null);
        newTask.setUpdateTime(null);
        productionOperationTaskMapper.insert(newTask);
    }
    private String generateNextTaskNo(String prefix) {
@@ -220,4 +214,41 @@
    private BigDecimal defaultDecimal(BigDecimal value) {
        return value == null ? BigDecimal.ZERO : value;
    }
    private String resolveProductionBatchNo(Long productionProductMainId,
                                            Long qualityInspectId,
                                            Long productModelId) {
        if (productModelId == null) {
            return null;
        }
        if (productionProductMainId != null) {
            StockInRecord productionRecord = stockInRecordService.getOne(
                    Wrappers.<StockInRecord>lambdaQuery()
                            .eq(StockInRecord::getRecordId, productionProductMainId)
                            .eq(StockInRecord::getProductModelId, productModelId)
                            .in(StockInRecord::getRecordType, Arrays.asList(
                                    StockInQualifiedRecordTypeEnum.PRODUCTION_REPORT_STOCK_IN.getCode(),
                                    StockInQualifiedRecordTypeEnum.PRODUCTION_SCRAP.getCode()))
                            .isNotNull(StockInRecord::getBatchNo)
                            .orderByDesc(StockInRecord::getId)
                            .last("limit 1"),
                    false);
            if (productionRecord != null) {
                return productionRecord.getBatchNo();
            }
        }
        if (qualityInspectId == null) {
            return null;
        }
        StockInRecord inspectRecord = stockInRecordService.getOne(
                Wrappers.<StockInRecord>lambdaQuery()
                        .eq(StockInRecord::getRecordId, qualityInspectId)
                        .eq(StockInRecord::getProductModelId, productModelId)
                        .eq(StockInRecord::getRecordType, StockInQualifiedRecordTypeEnum.QUALITYINSPECT_STOCK_IN.getCode())
                        .isNotNull(StockInRecord::getBatchNo)
                        .orderByDesc(StockInRecord::getId)
                        .last("limit 1"),
                false);
        return inspectRecord == null ? null : inspectRecord.getBatchNo();
    }
}