package com.ruoyi.quality.service.impl; import com.baomidou.mybatisplus.core.metadata.IPage; 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.basic.enums.RecordTypeEnum; import com.ruoyi.basic.utils.FileUtil; import com.ruoyi.common.exception.ServiceException; import com.ruoyi.common.utils.bean.BeanUtils; 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.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.mapper.QualityUnqualifiedOrderMapper; import com.ruoyi.quality.pojo.QualityInspect; import com.ruoyi.quality.pojo.QualityUnqualified; import com.ruoyi.quality.pojo.QualityUnqualifiedOrder; import com.ruoyi.quality.service.IQualityInspectService; import com.ruoyi.quality.service.IQualityUnqualifiedOrderService; import jakarta.servlet.http.HttpServletResponse; import lombok.AllArgsConstructor; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.poifs.filesystem.POIFSFileSystem; import org.apache.poi.ss.usermodel.*; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.io.InputStream; import java.io.OutputStream; import java.math.BigDecimal; import java.net.URLEncoder; import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; @AllArgsConstructor @Service public class QualityUnqualifiedOrderServiceImpl extends ServiceImpl implements IQualityUnqualifiedOrderService { private final QualityUnqualifiedOrderMapper orderMapper; private final FileUtil fileUtil; 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; @Override public IPage listPage(Page page, QualityUnqualifiedOrder query) { return orderMapper.listPage(page, query); } @Override public QualityUnqualifiedOrder getDetail(Long id) { QualityUnqualifiedOrder order = orderMapper.selectById(id); if (order != null) { order.setStorageBlobVOs(fileUtil.getStorageBlobVOsByRecordTypeAndRecordId(RecordTypeEnum.QUALITY_UNQUALIFIED_ORDER, order.getId())); } return order; } @Override @Transactional(rollbackFor = Exception.class) public boolean save(QualityUnqualifiedOrder order) { boolean result = super.save(order); // 处置方式包含"维修"(2=厂内维修, 3=返厂维修)时,自动创建返修生产订单 if (order.getDisposalMethod() != null && (order.getDisposalMethod() == 2 || order.getDisposalMethod() == 3)) { createReworkProductionOrder(order); } return result; } @Override @Transactional(rollbackFor = Exception.class) public boolean deal(QualityUnqualifiedOrder order) { QualityUnqualifiedOrder existing = orderMapper.selectById(order.getId()); if (existing == null) { return false; } existing.setDisposalMethod(order.getDisposalMethod()); existing.setRepairEvaluation(order.getRepairEvaluation()); existing.setReasonAnalysis(order.getReasonAnalysis()); existing.setCorrectionAction(order.getCorrectionAction()); existing.setPreventiveAction(order.getPreventiveAction()); existing.setRemark(order.getRemark()); existing.setDeptOpinion(order.getDeptOpinion()); existing.setCompanyDecision(order.getCompanyDecision()); existing.setGeneralManagerOpinion(order.getGeneralManagerOpinion()); existing.setStatus(3); boolean result = updateById(existing); if (result && existing.getDisposalMethod() != null && (existing.getDisposalMethod() == 2 || existing.getDisposalMethod() == 3)) { createReworkProductionOrder(existing); } return result; } @Override @Transactional(rollbackFor = Exception.class) public boolean updateWithRework(QualityUnqualifiedOrder order) { boolean result = updateById(order); if (result && order.getDisposalMethod() != null && (order.getDisposalMethod() == 2 || order.getDisposalMethod() == 3)) { createReworkProductionOrder(order); } return result; } @Override public void export(Long id, HttpServletResponse response) { QualityUnqualifiedOrder order = getDetail(id); if (order == null) { throw new ServiceException("处理单不存在"); } try (InputStream is = this.getClass().getResourceAsStream("/static/不合格品处理单.xls"); POIFSFileSystem fs = new POIFSFileSystem(is); HSSFWorkbook wb = new HSSFWorkbook(fs)) { Sheet sheet = wb.getSheetAt(0); setCellValue(sheet, 2, 1, order.getProjectName()); setCellValue(sheet, 2, 5, order.getProjectNo()); setCellValue(sheet, 3, 1, order.getEquipmentName()); setCellValue(sheet, 3, 5, order.getEquipmentDrawingNo()); setCellValue(sheet, 4, 1, order.getMaterialName()); setCellValue(sheet, 4, 5, order.getMaterialDrawingNo()); setCellValue(sheet, 5, 1, order.getSpecificationModel()); setCellValue(sheet, 5, 3, order.getMaterialQuality()); setCellValue(sheet, 5, 5, order.getQuantity()); setCellValue(sheet, 5, 7, order.getUnqualifiedQuantity()); // 不合格工序 - 只替换对应的□为√ if (order.getUnqualifiedProcess() != null) { String origin = "□来料 □制程 □成品"; int idx = order.getUnqualifiedProcess() - 1; if (idx >= 0 && idx < 3) { origin = origin.replaceFirst("□", "√"); if (idx >= 1) origin = origin.replaceFirst("□", idx == 1 ? "√" : "□"); if (idx >= 2) origin = origin.replaceFirst("□", "√"); } // 按索引依次替换 StringBuilder sb = new StringBuilder(); int found = 0; for (char c : "□来料 □制程 □成品".toCharArray()) { if (c == '□') { sb.append(found == idx ? '☑' : '□'); found++; } else { sb.append(c); } } setCellValue(sheet, 6, 1, sb.toString()); } setCellValue(sheet, 6, 5, order.getSupplierName()); setCellValue(sheet, 7, 1, order.getInspectorName()); setCellValue(sheet, 7, 3, order.getInspectDate()); setCellValue(sheet, 7, 5, order.getResponsiblePerson()); setCellValue(sheet, 7, 7, order.getResponsibleDept()); setCellValue(sheet, 8, 1, order.getProblemDescription()); setCellValue(sheet, 9, 1, order.getReasonAnalysis()); setCellValue(sheet, 10, 1, order.getCorrectionAction()); // 处置方式 - 只替换对应的□为√ if (order.getDisposalMethod() != null) { String template = "□让步接收 □厂内维修 □返厂维修 □换货 □退货 □报废"; int idx = order.getDisposalMethod() - 1; StringBuilder sb = new StringBuilder(); int found = 0; for (char c : template.toCharArray()) { if (c == '□') { sb.append(found == idx ? '☑' : '□'); found++; } else { sb.append(c); } } setCellValue(sheet, 11, 1, sb.toString()); } setCellValue(sheet, 12, 1, order.getRepairEvaluation()); setCellValue(sheet, 13, 1, order.getPreventiveAction()); setCellValue(sheet, 14, 1, order.getDeptOpinion()); setCellValue(sheet, 15, 1, order.getCompanyDecision()); setCellValue(sheet, 16, 1, order.getGeneralManagerOpinion()); response.setContentType("application/vnd.ms-excel"); String fileName = URLEncoder.encode("不合格品处理单_" + order.getOrderNo(), "UTF-8"); response.setHeader("Access-Control-Expose-Headers", "Content-Disposition"); response.setHeader("Content-Disposition", "attachment;filename=" + fileName + ".xls"); OutputStream os = response.getOutputStream(); wb.write(os); os.flush(); os.close(); } catch (Exception e) { throw new RuntimeException("导出失败", e); } } private void setCellValue(Sheet sheet, int rowIdx, int colIdx, Object value) { if (value == null) return; Row row = sheet.getRow(rowIdx); if (row == null) row = sheet.createRow(rowIdx); Cell cell = row.getCell(colIdx); if (cell == null) cell = row.createCell(colIdx); if (value instanceof Date) { cell.setCellValue((Date) value); } else if (value instanceof Number) { cell.setCellValue(((Number) value).doubleValue()); } else { cell.setCellValue(value.toString()); } } private void createReworkProductionOrder(QualityUnqualifiedOrder order) { if (order.getUnqualifiedId() == null) { return; } QualityUnqualified unqualified = qualityUnqualifiedMapper.selectById(order.getUnqualifiedId()); if (unqualified == null || unqualified.getInspectId() == null) { return; } QualityInspect qualityInspect = qualityInspectService.getById(unqualified.getInspectId()); if (qualityInspect == null || qualityInspect.getProductMainId() == null) { return; } ProductionProductMain sourceMain = productionProductMainMapper.selectById(qualityInspect.getProductMainId()); if (sourceMain == null || sourceMain.getProductionOperationTaskId() == null) { return; } ProductionOperationTask sourceTask = productionOperationTaskMapper.selectById(sourceMain.getProductionOperationTaskId()); if (sourceTask == null) { return; } ProductionOrder sourceOrder = productionOrderMapper.selectById(sourceTask.getProductionOrderId()); if (sourceOrder == null) { return; } BigDecimal reworkQty = order.getUnqualifiedQuantity() != null ? order.getUnqualifiedQuantity() : unqualified.getQuantity() != null ? unqualified.getQuantity() : BigDecimal.ONE; ProductionOrder newOrder = new ProductionOrder(); BeanUtils.copyProperties(sourceOrder, newOrder); newOrder.setId(null); newOrder.setNpsNo(generateNextProductionOrderNo("FG")); newOrder.setQuantity(reworkQty); newOrder.setCompleteQuantity(BigDecimal.ZERO); newOrder.setStartTime(null); newOrder.setEndTime(null); newOrder.setCreateTime(null); newOrder.setUpdateTime(null); newOrder.setDisposalMethod(order.getDisposalMethod()); productionOrderMapper.insert(newOrder); Map routingIdMap = new HashMap<>(); List sourceRoutings = productionOrderRoutingMapper.selectList( Wrappers.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 sourceOperations = productionOrderRoutingOperationMapper.selectList( Wrappers.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.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); } private String generateNextTaskNo(String prefix) { String datePrefix = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")); String taskPrefix = prefix + datePrefix; ProductionOperationTask latestTask = productionOperationTaskMapper.selectOne( Wrappers.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); } }