huminmin
2026-07-15 422a5676c2d96dd37c1be62935a314d70fef79b5
src/main/java/com/ruoyi/quality/service/impl/QualityUnqualifiedServiceImpl.java
@@ -1,38 +1,37 @@
package com.ruoyi.quality.service.impl;
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.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.enums.StockRecordTypeEnum;
import com.ruoyi.common.enums.StockInQualifiedRecordTypeEnum;
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.ProductProcessRouteItemMapper;
import com.ruoyi.production.mapper.ProductProcessRouteMapper;
import com.ruoyi.production.mapper.ProductWorkOrderMapper;
import com.ruoyi.production.mapper.ProductionOperationTaskMapper;
import com.ruoyi.production.mapper.ProductionProductMainMapper;
import com.ruoyi.production.pojo.*;
import com.ruoyi.production.service.ProductOrderService;
import com.ruoyi.quality.mapper.QualityInspectMapper;
import com.ruoyi.production.service.ProductionAccountService;
import com.ruoyi.production.pojo.ProductionOperationTask;
import com.ruoyi.production.pojo.ProductionProductMain;
import com.ruoyi.quality.mapper.QualityUnqualifiedMapper;
import com.ruoyi.quality.pojo.QualityInspect;
import com.ruoyi.quality.pojo.QualityTestStandard;
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.dto.StockUninventoryDto;
import com.ruoyi.stock.service.StockUninventoryService;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.List;
@AllArgsConstructor
@@ -40,14 +39,13 @@
public class QualityUnqualifiedServiceImpl extends ServiceImpl<QualityUnqualifiedMapper, QualityUnqualified> implements IQualityUnqualifiedService {
    private final StockUtils stockUtils;
    private QualityUnqualifiedMapper qualityUnqualifiedMapper;
    private IQualityInspectService qualityInspectService;
    private ProductOrderService productOrderService;
    private ProductionProductMainMapper productionProductMainMapper;
    private ProductProcessRouteMapper productProcessRouteMapper;
    private ProductProcessRouteItemMapper productProcessRouteItemMapper;
    private ProductWorkOrderMapper productWorkOrderMapper;
    private StockUninventoryService stockUninventoryService;
    private final QualityUnqualifiedMapper qualityUnqualifiedMapper;
    private final IQualityInspectService qualityInspectService;
    private final ProductionProductMainMapper productionProductMainMapper;
    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) {
@@ -57,92 +55,200 @@
    @Override
    public void qualityUnqualifiedExport(HttpServletResponse response, QualityUnqualified qualityUnqualified) {
        List<QualityUnqualified> qualityUnqualifieds = qualityUnqualifiedMapper.qualityUnqualifiedExport(qualityUnqualified);
        ExcelUtil<QualityUnqualified> util = new ExcelUtil<QualityUnqualified>(QualityUnqualified.class);
        ExcelUtil<QualityUnqualified> util = new ExcelUtil<>(QualityUnqualified.class);
        util.exportExcel(response, qualityUnqualifieds, "不合格管理导出");
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int deal(QualityUnqualified qualityUnqualified) {
        QualityUnqualified unqualified = qualityUnqualifiedMapper.selectById(qualityUnqualified.getId());
        QualityInspect qualityInspect = qualityInspectService.getById(unqualified.getInspectId());
        switch (qualityUnqualified.getDealResult()) {
            case "返修":
            case "返工":
                //判断质检表是否有相关的报工id,如果有报工id,那么返工需要重新创建生产订单重新生产
                if (ObjectUtils.isNotNull(qualityInspect.getProductMainId())) {
                    //返工需要重新创建生产订单重新生产
                    ProductOrder productOrder = productionProductMainMapper.getOrderByMainId(qualityInspect.getProductMainId());
                    ProductOrder order = new ProductOrder();
                    BeanUtils.copyProperties(productOrder, order);
                    order.setId(null);
                    order.setQuantity(unqualified.getQuantity());
                    order.setCompleteQuantity(BigDecimal.ZERO);
                    order.setStartTime(null);
                    order.setEndTime(null);
                    productOrderService.save(order);
                    //新增生产订单下的工艺路线主表
                    ProductProcessRoute productProcessRoute = productProcessRouteMapper.selectList(Wrappers.<ProductProcessRoute>lambdaQuery().eq(ProductProcessRoute::getProductOrderId, productOrder.getId()).orderByDesc(ProductProcessRoute::getId)).get(0);
                    ProductProcessRoute newProcessRoute = new ProductProcessRoute();
                    BeanUtils.copyProperties(productProcessRoute, newProcessRoute);
                    newProcessRoute.setId(null);
                    newProcessRoute.setProductOrderId(order.getId());
                    productProcessRouteMapper.insert(newProcessRoute);
                    //新增生产订单下的工艺路线子表
                    List<ProductProcessRouteItem> processRouteItems = productProcessRouteItemMapper.selectList(new QueryWrapper<ProductProcessRouteItem>().lambda().eq(ProductProcessRouteItem::getProductRouteId, productProcessRoute.getId()));
                    // 生成当前日期的前缀:年月日
                    String datePrefix = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
                    for (ProductProcessRouteItem processRouteItem : processRouteItems) {
                        ProductProcessRouteItem productProcessRouteItem = new ProductProcessRouteItem();
                        BeanUtils.copyProperties(processRouteItem, productProcessRouteItem);
                        productProcessRouteItem.setId(null);
                        productProcessRouteItem.setProductRouteId(newProcessRoute.getId());
                        int insert = productProcessRouteItemMapper.insert(productProcessRouteItem);
                        if (insert > 0) {
                            // 查询今日已存在的最大工单号
                            QueryWrapper<ProductWorkOrder> queryWrapper = new QueryWrapper<>();
                            queryWrapper.likeRight("work_order_no", datePrefix)
                                    .orderByDesc("work_order_no")
                                    .last("LIMIT 1");
                            ProductWorkOrder lastWorkOrder = productWorkOrderMapper.selectOne(queryWrapper);
                            int sequenceNumber = 1; // 默认序号
                            if (lastWorkOrder != null && lastWorkOrder.getWorkOrderNo() != null) {
                                String lastNo = lastWorkOrder.getWorkOrderNo().toString();
                                if (lastNo.startsWith(datePrefix)) {
                                    String seqStr = lastNo.substring(datePrefix.length());
                                    try {
                                        sequenceNumber = Integer.parseInt(seqStr) + 1;
                                    } catch (NumberFormatException e) {
                                        sequenceNumber = 1;
                                    }
                                }
                            }
                            // 生成完整的工单号
                            String workOrderNoStr = String.format("%s%03d", datePrefix, sequenceNumber);
                            ProductWorkOrder productWorkOrder = new ProductWorkOrder();
                            productWorkOrder.setProductProcessRouteItemId(productProcessRouteItem.getId());
                            productWorkOrder.setProductOrderId(order.getId());
                            productWorkOrder.setPlanQuantity(order.getQuantity());
                            productWorkOrder.setWorkOrderNo(workOrderNoStr);
                            productWorkOrder.setStatus(1);
                            productWorkOrderMapper.insert(productWorkOrder);
                        }
                    }
                }
                break;
            case "报废":
                //调用不合格库存接口 入不合格库
                stockUtils.addUnStock(qualityInspect.getProductModelId(), unqualified.getQuantity(), StockRecordTypeEnum.DEFECTIVE_SCRAP.getCode(),unqualified.getId());
                break;
            case "让步放行":
                //调用提交合格的接口
                stockUtils.addStock(qualityInspect.getProductModelId(), unqualified.getQuantity(), StockRecordTypeEnum.DEFECTIVE_PASS.getCode(),unqualified.getId());
                qualityInspect.setCheckResult("合格");
                qualityInspectService.submit(qualityInspect);
                break;
            default:
                break;
        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 "报工":
                    handleProductionReportResult(qualityUnqualified, unqualified, sourceMain);
                    break;
                case "报废":
                    handleProductionScrapResult(qualityInspect, qualityUnqualified, unqualified, sourceMain, batchNo);
                    break;
                case "让步放行":
                    stockUtils.addStockWithBatchNo(qualityInspect.getProductModelId(), unqualified.getQuantity(),
                            StockInQualifiedRecordTypeEnum.DEFECTIVE_PASS.getCode(), unqualified.getId(), batchNo);
                    break;
                default:
                    break;
            }
        } else {
            Long modelId = qualityUnqualifiedMapper.getModelId(qualityUnqualified.getProductName(), qualityUnqualified.getModel());
            switch (qualityUnqualified.getDealResult()) {
                case "报废":
                    stockUtils.addUnStock(modelId, unqualified.getQuantity(),
                            StockInQualifiedRecordTypeEnum.DEFECTIVE_SCRAP.getCode(), unqualified.getId());
                    break;
                case "让步放行":
                    stockUtils.addStock(modelId, unqualified.getQuantity(),
                            StockInQualifiedRecordTypeEnum.DEFECTIVE_PASS.getCode(), unqualified.getId());
                    break;
                default:
                    break;
            }
        }
        qualityUnqualified.setInspectState(1);//已处理
        qualityUnqualified.setInspectState(1);
        return qualityUnqualifiedMapper.updateById(qualityUnqualified);
    }
    @Override
    public QualityUnqualified getUnqualified(Integer id) {
        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("关联的生产工单不存在");
        }
        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) {
        String datePrefix = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
        String taskPrefix = prefix + datePrefix;
        ProductionOperationTask latestTask = productionOperationTaskMapper.selectOne(
                Wrappers.<ProductionOperationTask>lambdaQuery()
                        .likeRight(ProductionOperationTask::getWorkOrderNo, taskPrefix)
                        .orderByDesc(ProductionOperationTask::getWorkOrderNo)
                        .last("limit 1"));
        int sequence = 1;
        if (latestTask != null && latestTask.getWorkOrderNo() != null && latestTask.getWorkOrderNo().startsWith(taskPrefix)) {
            try {
                sequence = Integer.parseInt(latestTask.getWorkOrderNo().substring(taskPrefix.length())) + 1;
            } catch (NumberFormatException ignored) {
                sequence = 1;
            }
        }
        return taskPrefix + String.format("%03d", sequence);
    }
    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();
    }
}