gongchunyi
10 小时以前 ec7d3b867e7b7f5073dda1684a8720424b9da5ad
src/main/java/com/ruoyi/production/service/impl/ProductionRecordServiceImpl.java
@@ -7,9 +7,7 @@
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.other.mapper.TempFileMapper;
import com.ruoyi.other.pojo.TempFile;
import com.ruoyi.production.dto.ProductionProductRouteItemDto;
import com.ruoyi.production.dto.ProductionProductRouteItemParamDto;
import com.ruoyi.production.dto.ProductionRecordDto;
import com.ruoyi.production.dto.*;
import com.ruoyi.production.enums.ProductOrderStatusEnum;
import com.ruoyi.production.pojo.*;
import com.ruoyi.production.service.*;
@@ -197,21 +195,21 @@
            throw new ServiceException("报工失败,生产订单不存在");
        }
        //  当前报工合格数量
        //  当前报工合格数量(前端已保证 = 投入数量 - 不合格数量,直接使用)
        BigDecimal qualifiedQty = dto.getQualifiedQuantity() == null ? BigDecimal.ZERO : dto.getQualifiedQuantity();
        //  已完成数量
        //  订单已完成数量(历史累计)
        BigDecimal completeQty = productOrder.getCompleteQuantity() == null ? BigDecimal.ZERO : productOrder.getCompleteQuantity();
        //  累加(只算合格)
        BigDecimal newCompleteQty = completeQty.add(qualifiedQty);
        productOrder.setCompleteQuantity(newCompleteQty);
        //  需求数量
        BigDecimal totalQty = productOrder.getQuantity() == null ? BigDecimal.ZERO : productOrder.getQuantity();
        // 剩余可报工数量
        //  剩余可报工数量
        BigDecimal remainQty = totalQty.subtract(completeQty);
        //  本次报工不能超过剩余数量
        //  本次报工合格数量不能超过剩余数量(校验必须在累加前执行)
        if (qualifiedQty.compareTo(remainQty) > 0) {
            throw new ServiceException("报工失败,本次报工数量不能大于剩余可报工数量");
            throw new ServiceException("报工失败,本次报工数量不能大于剩余可报工数量,剩余报工数量:[" + remainQty + "]");
        }
        //  历史已完成 + 本次合格数量
        BigDecimal newCompleteQty = completeQty.add(qualifiedQty);
        productOrder.setCompleteQuantity(newCompleteQty);
        //  设置开始时间(第一次报工)
        if (productOrder.getStartTime() == null) {
            productOrder.setStartTime(LocalDateTime.now());
@@ -230,9 +228,11 @@
        //  完成报工主表-投入表-产出表数据
        ProductionProductMain productionProductMain = new ProductionProductMain();
        productionProductMain.setProductNo("BG" + UUID.randomUUID());
        productionProductMain.setProductNo(productionProductMainService.generateProductNo());
        productionProductMain.setProductOrderId(dto.getProductOrderId());
        productionProductMain.setSchedule(dto.getSchedule());
        productionProductMain.setPostName(dto.getPostName());
        productionProductMain.setReportingTime(LocalDateTime.now());
        boolean result = productionProductMainService.save(productionProductMain);
        if (!result) {
            throw new ServiceException("报工失败,数据存储失败");
@@ -276,6 +276,7 @@
                    ProductionProductRouteItemParam paramEntity = new ProductionProductRouteItemParam();
                    BeanUtils.copyProperties(productRouteItemParamDto, paramEntity, "id");
                    paramEntity.setProductionProductRouteItemId(productRouteItemEntity.getId());
                    paramEntity.setOrderItemParamId(productRouteItemParamDto.getId());
                    if (paramEntity.getProductId() == null) {
                        ProductionOrderRouteItemParam productionOrderRouteItemParam = productionOrderRouteItemParamService.getById(productRouteItemParamDto.getId());
                        paramEntity.setParamName(productionOrderRouteItemParam.getParamName());
@@ -344,4 +345,381 @@
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteProductMain(Long productMainId) {
        if (productMainId == null) {
            throw new ServiceException("删除失败,报工ID不能为空");
        }
        //  校验报工主表是否存在
        ProductionProductMain productMain = productionProductMainService.getById(productMainId);
        if (productMain == null) {
            throw new ServiceException("删除失败,报工记录不存在");
        }
        //  查询该报工下所有工序记录
        List<ProductionProductRouteItem> routeItemList = productionProductRouteItemService.list(new LambdaQueryWrapper<ProductionProductRouteItem>()
                .eq(ProductionProductRouteItem::getProductMainId, productMainId));
        if (routeItemList != null && !routeItemList.isEmpty()) {
            List<Long> routeItemIds = routeItemList.stream()
                    .map(ProductionProductRouteItem::getId)
                    .collect(Collectors.toList());
            //  查询并删除每个工序下的附件
            List<ProductionProductRouteItemFile> fileList = productionProductRouteItemFileService.list(new LambdaQueryWrapper<ProductionProductRouteItemFile>()
                    .in(ProductionProductRouteItemFile::getProductionProductRouteItemId, routeItemIds));
            if (fileList != null && !fileList.isEmpty()) {
                for (ProductionProductRouteItemFile file : fileList) {
                    if (StringUtils.hasText(file.getFileUrl())) {
                        try {
                            Files.deleteIfExists(Paths.get(file.getFileUrl()));
                        } catch (IOException e) {
                            log.warn("删除附件文件失败: {}", file.getFileUrl(), e);
                        }
                    }
                }
                productionProductRouteItemFileService.remove(new LambdaQueryWrapper<ProductionProductRouteItemFile>()
                        .in(ProductionProductRouteItemFile::getProductionProductRouteItemId, routeItemIds));
            }
            //  删除工序参数
            productionProductRouteItemParamService.remove(new LambdaQueryWrapper<ProductionProductRouteItemParam>()
                    .in(ProductionProductRouteItemParam::getProductionProductRouteItemId, routeItemIds));
            //  删除工序记录
            productionProductRouteItemService.remove(new LambdaQueryWrapper<ProductionProductRouteItem>()
                    .eq(ProductionProductRouteItem::getProductMainId, productMainId));
        }
        //  删除投入表
        productionProductInputService.remove(new LambdaQueryWrapper<ProductionProductInput>()
                .eq(ProductionProductInput::getProductMainId, productMainId));
        //  回滚生产订单完成数量
        ProductionProductOutput output = productionProductOutputService.getOne(new LambdaQueryWrapper<ProductionProductOutput>()
                .eq(ProductionProductOutput::getProductMainId, productMainId)
                .last("LIMIT 1"));
        ProductOrder productOrder = productOrderService.getById(productMain.getProductOrderId());
        if (productOrder == null) {
            throw new ServiceException("删除失败,关联的生产订单不存在");
        }
        BigDecimal qualifiedQty = (output != null && output.getQuantity() != null)
                ? output.getQuantity() : BigDecimal.ZERO;
        BigDecimal currentCompleteQty = productOrder.getCompleteQuantity() == null
                ? BigDecimal.ZERO : productOrder.getCompleteQuantity();
        BigDecimal totalQty = productOrder.getQuantity() == null ? BigDecimal.ZERO : productOrder.getQuantity();
        //  扣减本条报工贡献的合格数量,不允许出现负数
        BigDecimal updatedCompleteQty = currentCompleteQty.subtract(qualifiedQty).max(BigDecimal.ZERO);
        productOrder.setCompleteQuantity(updatedCompleteQty);
        //  重新判断订单状态
        if (updatedCompleteQty.compareTo(totalQty) >= 0 && totalQty.compareTo(BigDecimal.ZERO) > 0) {
            //  完成数 >= 需求数 → 已完成
            productOrder.setStatus(ProductOrderStatusEnum.FINISHED.getCode());
        } else if (updatedCompleteQty.compareTo(BigDecimal.ZERO) == 0) {
            //  完成数归零 → 回到待开始,清空开始/结束时间
            productOrder.setStatus(ProductOrderStatusEnum.WAIT.getCode());
            productOrder.setStartTime(null);
            productOrder.setEndTime(null);
        } else {
            //  完成数介于 0 ~ 需求数之间 → 进行中
            productOrder.setStatus(ProductOrderStatusEnum.RUNNING.getCode());
            productOrder.setEndTime(null);
        }
        productOrderService.updateById(productOrder);
        //  删除产出表
        productionProductOutputService.remove(new LambdaQueryWrapper<ProductionProductOutput>()
                .eq(ProductionProductOutput::getProductMainId, productMainId));
        //  删除报工主表
        productionProductMainService.removeById(productMainId);
        log.info("报工记录删除成功,productMainId={}", productMainId);
    }
    @Override
    public ProductionRecordDto detailProductMain(Long productMainId) {
        if (productMainId == null) {
            throw new ServiceException("查询失败,报工ID不能为空");
        }
        //  查询报工主表
        ProductionProductMain productMain = productionProductMainService.getById(productMainId);
        if (productMain == null) {
            throw new ServiceException("查询失败,报工记录不存在");
        }
        ProductionRecordDto dto = new ProductionRecordDto();
        dto.setProductMainId(productMainId);
        dto.setProductOrderId(productMain.getProductOrderId());
        dto.setPostName(productMain.getPostName());
        dto.setSchedule(productMain.getSchedule());
        dto.setReportingTime(productMain.getReportingTime());
        dto.setCreateTime(productMain.getCreateTime());
        dto.setUpdateTime(productMain.getUpdateTime());
        //  生产订单信息
        ProductOrder productOrder = productOrderService.getById(productMain.getProductOrderId());
        if (productOrder == null) {
            throw new ServiceException("查询失败,未查询到生产订单信息");
        }
        dto.setNpsNo(productOrder.getNpsNo());
        /// 产品信息
        ProductMaterialSkuDto productMaterialSkuDto = productMaterialService.selectProductByProductMainId(productOrder.getId());
        dto.setProductName(productMaterialSkuDto.getProductName());
        dto.setMaterialCode(productMaterialSkuDto.getMaterialCode());
        dto.setModel(productMaterialSkuDto.getModel());
        //  查询投入表(获取产品ID和投入数量)
        ProductionProductInput input = productionProductInputService.getOne(
                new LambdaQueryWrapper<ProductionProductInput>()
                        .eq(ProductionProductInput::getProductMainId, productMainId)
                        .last("LIMIT 1"));
        if (input != null) {
            dto.setProductId(input.getProductModelId());
            dto.setQuantity(input.getQuantity());
        }
        //  查询产出表(获取合格/不合格数量)
        ProductionProductOutput output = productionProductOutputService.getOne(
                new LambdaQueryWrapper<ProductionProductOutput>()
                        .eq(ProductionProductOutput::getProductMainId, productMainId)
                        .last("LIMIT 1"));
        if (output != null) {
            dto.setQualifiedQuantity(output.getQuantity());
            dto.setUnqualifiedQuantity(output.getScrapQty());
        }
        //  查询工序列表
        List<ProductionProductRouteItem> routeItemList = productionProductRouteItemService.list(
                new LambdaQueryWrapper<ProductionProductRouteItem>()
                        .eq(ProductionProductRouteItem::getProductMainId, productMainId));
        if (routeItemList != null && !routeItemList.isEmpty()) {
            List<ProductionProductRouteItemDto> routeItemDtoList = routeItemList.stream().map(routeItem -> {
                ProductionProductRouteItemDto routeItemDto = new ProductionProductRouteItemDto();
                BeanUtils.copyProperties(routeItem, routeItemDto);
                //  查询工序参数
                List<ProductionProductRouteItemParam> paramList = productionProductRouteItemParamService.list(
                        new LambdaQueryWrapper<ProductionProductRouteItemParam>()
                                .eq(ProductionProductRouteItemParam::getProductionProductRouteItemId, routeItem.getId()));
                if (paramList != null && !paramList.isEmpty()) {
                    List<ProductionProductRouteItemParamDto> paramDtoList = paramList.stream().map(param -> {
                        ProductionProductRouteItemParamDto paramDto = new ProductionProductRouteItemParamDto();
                        BeanUtils.copyProperties(param, paramDto);
                        if (paramDto.getProductId() != null) {
                            ProductMaterialSkuDto materialSkuDto = productMaterialService.selectProductByModelId(paramDto.getProductId());
                            productMaterialService.selectProductByModelId(paramDto.getProductId());
                            paramDto.setParamName(materialSkuDto.getProductName());
                        }
                        return paramDto;
                    }).collect(Collectors.toList());
                    routeItemDto.setProductionProductRouteItemParamDtoList(paramDtoList);
                }
                //  查询工序附件
                List<ProductionProductRouteItemFile> fileRecordList = productionProductRouteItemFileService.list(
                        new LambdaQueryWrapper<ProductionProductRouteItemFile>()
                                .eq(ProductionProductRouteItemFile::getProductionProductRouteItemId, routeItem.getId()));
                if (fileRecordList != null && !fileRecordList.isEmpty()) {
                    List<ProductionProductRouteItemFileDto> fileDtoList = fileRecordList.stream().map(file -> {
                        ProductionProductRouteItemFileDto fileDto = new ProductionProductRouteItemFileDto();
                        BeanUtils.copyProperties(file, fileDto);
                        return fileDto;
                    }).collect(Collectors.toList());
                    routeItemDto.setFileList(fileDtoList);
                }
                return routeItemDto;
            }).collect(Collectors.toList());
            dto.setProductionProductRouteItemDtoList(routeItemDtoList);
        }
        return dto;
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void editProductMain(ProductionRecordDto dto) {
        if (dto == null || dto.getProductMainId() == null) {
            throw new ServiceException("编辑失败,报工ID不能为空");
        }
        if (dto.getProductId() == null) {
            throw new ServiceException("编辑失败,产品信息不能为空");
        }
        if (StringUtils.isEmpty(dto.getPostName()) || StringUtils.isEmpty(dto.getSchedule())) {
            throw new ServiceException("编辑失败,岗位人员/班次信息不能为空");
        }
        Long productMainId = dto.getProductMainId();
        //  查询报工主表是否存在
        ProductionProductMain productMain = productionProductMainService.getById(productMainId);
        if (productMain == null) {
            throw new ServiceException("编辑失败,报工记录不存在");
        }
        //  回滚生产订单的合格数量
        ProductionProductOutput oldOutput = productionProductOutputService.getOne(new LambdaQueryWrapper<ProductionProductOutput>()
                .eq(ProductionProductOutput::getProductMainId, productMainId)
                .last("LIMIT 1"));
        ProductOrder productOrder = productOrderService.getById(productMain.getProductOrderId());
        if (productOrder == null) {
            throw new ServiceException("编辑失败,关联的生产订单不存在");
        }
        BigDecimal oldQualifiedQty = (oldOutput != null && oldOutput.getQuantity() != null)
                ? oldOutput.getQuantity() : BigDecimal.ZERO;
        BigDecimal newQualifiedQty = dto.getQualifiedQuantity() == null ? BigDecimal.ZERO : dto.getQualifiedQuantity();
        BigDecimal currentCompleteQty = productOrder.getCompleteQuantity() == null
                ? BigDecimal.ZERO : productOrder.getCompleteQuantity();
        BigDecimal totalQty = productOrder.getQuantity() == null ? BigDecimal.ZERO : productOrder.getQuantity();
        //  回滚旧值,计算本次编辑后的新完成数量
        BigDecimal baseQty = currentCompleteQty.subtract(oldQualifiedQty);
        BigDecimal updatedCompleteQty = baseQty.add(newQualifiedQty);
        //  新的合格数量不能超过(需求数量 - 报工总完成数量)
        if (newQualifiedQty.compareTo(totalQty.subtract(baseQty)) > 0) {
            throw new ServiceException("编辑失败,本次报工合格数量不能大于剩余可报工数量");
        }
        //  重新判断生产订单状态
        productOrder.setCompleteQuantity(updatedCompleteQty);
        if (updatedCompleteQty.compareTo(totalQty) >= 0 && totalQty.compareTo(BigDecimal.ZERO) > 0) {
            productOrder.setStatus(ProductOrderStatusEnum.FINISHED.getCode());
            productOrder.setEndTime(LocalDateTime.now());
        } else {
            productOrder.setStatus(ProductOrderStatusEnum.RUNNING.getCode());
            productOrder.setEndTime(null);
        }
        productOrderService.updateById(productOrder);
        //  更新报工主表
        productMain.setPostName(dto.getPostName());
        productMain.setSchedule(dto.getSchedule());
        productMain.setReportingTime(dto.getReportingTime());
        productionProductMainService.updateById(productMain);
        //  更新投入表
        ProductionProductInput input = productionProductInputService.getOne(new LambdaQueryWrapper<ProductionProductInput>()
                .eq(ProductionProductInput::getProductMainId, productMainId)
                .last("LIMIT 1"));
        if (input != null) {
            input.setProductModelId(dto.getProductId());
            input.setQuantity(dto.getQuantity());
            productionProductInputService.updateById(input);
        }
        //  更新产出表
        if (oldOutput != null) {
            oldOutput.setProductModelId(dto.getProductId());
            oldOutput.setQuantity(dto.getQualifiedQuantity());
            oldOutput.setScrapQty(dto.getUnqualifiedQuantity());
            productionProductOutputService.updateById(oldOutput);
        }
        //  处理工序列表
        List<ProductionProductRouteItemDto> routeItemDtoList = dto.getProductionProductRouteItemDtoList();
        if (routeItemDtoList == null || routeItemDtoList.isEmpty()) {
            throw new ServiceException("编辑失败,工序参数不能为空");
        }
        for (ProductionProductRouteItemDto routeItemDto : routeItemDtoList) {
            //  更新已有工序
            ProductionProductRouteItem routeItemEntity = productionProductRouteItemService.getById(routeItemDto.getId());
            if (routeItemEntity == null) {
                log.error("编辑失败,工序记录不存在,ID={}", routeItemDto.getId());
                throw new ServiceException("编辑失败,工序记录不存在");
            }
            BeanUtils.copyProperties(routeItemDto, routeItemEntity, "id", "productMainId", "createTime", "tenantId");
            productionProductRouteItemService.updateById(routeItemEntity);
            final Long routeItemId = routeItemEntity.getId();
            //  处理工序参数: 先删除该工序下所有旧参数,再重新插入传入的参数
            productionProductRouteItemParamService.remove(new LambdaQueryWrapper<ProductionProductRouteItemParam>()
                    .eq(ProductionProductRouteItemParam::getProductionProductRouteItemId, routeItemId));
            List<ProductionProductRouteItemParamDto> paramDtoList = routeItemDto.getProductionProductRouteItemParamDtoList();
                if (paramDtoList != null && !paramDtoList.isEmpty()) {
                for (ProductionProductRouteItemParamDto paramDto : paramDtoList) {
                    ProductionProductRouteItemParam paramEntity = new ProductionProductRouteItemParam();
                    BeanUtils.copyProperties(paramDto, paramEntity, "id");
                    paramEntity.setProductionProductRouteItemId(routeItemId);
                    if (paramEntity.getProductId() == null && paramDto.getId() != null) {
                        ProductionOrderRouteItemParam orderParam = productionOrderRouteItemParamService.getById(paramDto.getId());
                        if (orderParam != null) {
                            paramEntity.setOrderItemParamId(orderParam.getId());
                            paramEntity.setParamName(orderParam.getParamName());
                            paramEntity.setParamType(orderParam.getParamType());
                            paramEntity.setParamFormat(orderParam.getParamFormat());
                            paramEntity.setValueMode(orderParam.getValueMode());
                        }
                    }
                    productionProductRouteItemParamService.save(paramEntity);
                }
            }
            //  处理新上传的临时附件
            List<String> newFiles = routeItemDto.getFiles();
            if (newFiles != null && !newFiles.isEmpty()) {
                for (String tempId : newFiles) {
                    TempFile tempFile = tempFileMapper.selectById(tempId);
                    if (tempFile == null) {
                        log.warn("未找到临时文件记录: {}", tempId);
                        continue;
                    }
                    try {
                        String formalDir = uploadDir + LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE);
                        Path formalDirPath = Paths.get(formalDir);
                        if (!Files.exists(formalDirPath)) {
                            Files.createDirectories(formalDirPath);
                        }
                        String originalFilename = tempFile.getOriginalName();
                        String fileExtension = FilenameUtils.getExtension(originalFilename);
                        String formalFilename = routeItemId + "_"
                                + System.currentTimeMillis() + "_"
                                + UUID.randomUUID().toString().substring(0, 8)
                                + (StringUtils.hasText(fileExtension) ? "." + fileExtension : "");
                        Path formalFilePath = formalDirPath.resolve(formalFilename);
                        Files.copy(Paths.get(tempFile.getTempPath()), formalFilePath, StandardCopyOption.REPLACE_EXISTING);
                        Files.deleteIfExists(Paths.get(tempFile.getTempPath()));
                        ProductionProductRouteItemFile fileEntity = new ProductionProductRouteItemFile();
                        fileEntity.setProductionProductRouteItemId(routeItemId);
                        fileEntity.setFileName(originalFilename);
                        fileEntity.setFileUrl(formalFilePath.toString());
                        fileEntity.setFileSuffix(fileExtension);
                        fileEntity.setFileSize(Files.size(formalFilePath));
                        fileEntity.setCreateTime(LocalDateTime.now());
                        fileEntity.setTenantId(SecurityUtils.getLoginUser().getTenantId());
                        productionProductRouteItemFileService.save(fileEntity);
                        tempFileMapper.deleteById(tempId);
                        log.info("编辑-工序附件迁移成功: {} -> {}", tempFile.getTempPath(), formalFilePath);
                    } catch (IOException e) {
                        log.error("编辑-工序附件迁移失败: {}", tempFile.getTempPath(), e);
                        throw new ServiceException("工序附件处理异常: " + e.getMessage());
                    }
                }
            }
            List<Long> delFileIds = routeItemDto.getDelFileIds();
            if (delFileIds != null && !delFileIds.isEmpty()) {
                delFileIds.forEach(productionProductRouteItemFileService::deleteFile);
            }
        }
        log.info("报工记录编辑成功,productMainId={}", productMainId);
    }
}