package com.ruoyi.production.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.ruoyi.common.exception.ServiceException; import com.ruoyi.common.utils.DateUtils; import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.other.mapper.TempFileMapper; import com.ruoyi.other.pojo.TempFile; import com.ruoyi.production.dto.*; import com.ruoyi.production.enums.ProductOrderStatusEnum; import com.ruoyi.production.pojo.*; import com.ruoyi.production.service.*; import com.ruoyi.production.vo.ProductionOrderRouteItemParamVo; import com.ruoyi.production.vo.ProductionOrderRouteItemVo; import com.ruoyi.production.vo.ProductionOrderStructureVo; import com.ruoyi.production.vo.ProductionRecordVo; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.FilenameUtils; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.io.IOException; import java.math.BigDecimal; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.List; import java.util.UUID; import java.util.stream.Collectors; /** *
* 生产报工记录Service实现类 *
* * @author deslrey * @version 1.0 * @since 2026/03/23 10:23 */ @Slf4j @Service public class ProductionRecordServiceImpl implements ProductionRecordService { @Value("${file.upload-dir}") private String uploadDir; @Autowired private ProductionProductMainService productionProductMainService; @Autowired private ProductionProductInputService productionProductInputService; @Autowired private ProductionProductOutputService productionProductOutputService; @Autowired private IProductionProductRouteItemService productionProductRouteItemService; @Autowired private IProductionProductRouteItemParamService productionProductRouteItemParamService; @Autowired private IProductionProductRouteItemFileService productionProductRouteItemFileService; @Autowired private ProductOrderService productOrderService; @Autowired private IProductionOrderRouteItemService productionOrderRouteItemService; @Autowired private IProductionOrderRouteItemParamService productionOrderRouteItemParamService; @Autowired private IProductionOrderStructureService productionOrderStructureService; @Autowired private ProductProcessService productProcessService; @Autowired private ProductMaterialSkuService productMaterialSkuService; @Autowired private ProductMaterialService productMaterialService; @Autowired private TempFileMapper tempFileMapper; @Override public ProductionRecordVo productRouteItem(Long productOrderId) { if (productOrderId == null) { throw new ServiceException("请选择生产订单再新增报工"); } // 查询出生产订单绑定的工艺路线 ProductOrder productOrder = productOrderService.getById(productOrderId); if (productOrder == null) { throw new ServiceException("未查询到该生产订单的信息"); } // 通过工艺路线查询出绑定的工序及参数 List productionOrderRouteItemList = productionOrderRouteItemService.list(new LambdaQueryWrapper() .eq(ProductionOrderRouteItem::getOrderId, productOrderId) .eq(ProductionOrderRouteItem::getRouteId, productOrder.getRouteId())); if (productionOrderRouteItemList == null || productionOrderRouteItemList.isEmpty()) { throw new ServiceException("该生产订单绑定的工艺路线无工序"); } ProductionRecordVo productionRecordVo = new ProductionRecordVo(); productionRecordVo.setProductOrderId(productOrderId); productionRecordVo.setProductRouteId(productOrder.getRouteId()); List productionOrderRouteItemVoList = new ArrayList<>(); // 根据绑定的工序查询出工序参数及BOM需要消耗的产品 for (ProductionOrderRouteItem orderRouteItem : productionOrderRouteItemList) { ProductionOrderRouteItemVo productionOrderRouteItemVo = new ProductionOrderRouteItemVo(); productionOrderRouteItemVo.setProcessId(orderRouteItem.getProcessId()); if (orderRouteItem.getProcessId() != null) { ProductProcess process = productProcessService.getById(orderRouteItem.getProcessId()); if (process != null) { productionOrderRouteItemVo.setProcessName(process.getName()); } } List orderRouteItemParamList = productionOrderRouteItemParamService.list(new LambdaQueryWrapper() .eq(ProductionOrderRouteItemParam::getOrderId, productOrderId) .eq(ProductionOrderRouteItemParam::getRouteItemId, orderRouteItem.getId())); if (orderRouteItemParamList != null && !orderRouteItemParamList.isEmpty()) { List paraVoList = orderRouteItemParamList.stream().map(param -> { ProductionOrderRouteItemParamVo paraVo = new ProductionOrderRouteItemParamVo(); BeanUtils.copyProperties(param, paraVo); paraVo.setParamFormat(DateUtils.toUpperCasePattern(paraVo.getParamFormat())); return paraVo; }).collect(Collectors.toList()); productionOrderRouteItemVo.setOrderRouteItemParaVos(paraVoList); } List orderStructureList = productionOrderStructureService.list(new LambdaQueryWrapper() .eq(ProductionOrderStructure::getOrderId, productOrderId) .eq(ProductionOrderStructure::getProcessId, orderRouteItem.getProcessId())); if (orderStructureList != null && !orderStructureList.isEmpty()) { List structureVoList = orderStructureList.stream().map(struct -> { ProductionOrderStructureVo structureVo = new ProductionOrderStructureVo(); BeanUtils.copyProperties(struct, structureVo); // 查询出产品的名称/模型 ProductMaterialSku productMaterialSku = productMaterialSkuService.getById(structureVo.getProductModelId()); ProductMaterial productMaterial = productMaterialService.getById(productMaterialSku.getProductId()); structureVo.setProductName(productMaterial.getProductName()); structureVo.setModel(productMaterialSku.getModel()); return structureVo; }).collect(Collectors.toList()); productionOrderRouteItemVo.setOrderStructureVos(structureVoList); } // 每一个工序的参数、BOM消耗产品 productionOrderRouteItemVoList.add(productionOrderRouteItemVo); } productionRecordVo.setProductionOrderRouteItemVos(productionOrderRouteItemVoList); return productionRecordVo; } @Override @Transactional(rollbackFor = Exception.class) public void addProductionRecordService(ProductionRecordDto dto) { if (dto == null) { throw new ServiceException("报工失败,数据不能为空"); } if (dto.getProductOrderId() == null) { throw new ServiceException("报工失败,生产订单ID不能为空"); } if (dto.getProductId() == null) { throw new ServiceException("报工失败,产品信息不能为空"); } if (StringUtils.isEmpty(dto.getPostName()) || StringUtils.isEmpty(dto.getSchedule())) { throw new ServiceException("报工失败,岗位人员/班次信息不能为空"); } // 更新生产订单数据 ProductOrder productOrder = productOrderService.getById(dto.getProductOrderId()); if (productOrder == null) { throw new ServiceException("报工失败,生产订单不存在"); } // 当前报工合格数量(前端已保证 = 投入数量 - 不合格数量,直接使用) BigDecimal qualifiedQty = dto.getQualifiedQuantity() == null ? BigDecimal.ZERO : dto.getQualifiedQuantity(); // 订单已完成数量(历史累计) BigDecimal completeQty = productOrder.getCompleteQuantity() == null ? BigDecimal.ZERO : productOrder.getCompleteQuantity(); // 需求数量 BigDecimal totalQty = productOrder.getQuantity() == null ? BigDecimal.ZERO : productOrder.getQuantity(); // 剩余可报工数量 BigDecimal remainQty = totalQty.subtract(completeQty); // 本次报工合格数量不能超过剩余数量(校验必须在累加前执行) if (qualifiedQty.compareTo(remainQty) > 0) { throw new ServiceException("报工失败,本次报工数量不能大于剩余可报工数量,剩余报工数量:[" + remainQty + "]"); } // 历史已完成 + 本次合格数量 BigDecimal newCompleteQty = completeQty.add(qualifiedQty); productOrder.setCompleteQuantity(newCompleteQty); // 设置开始时间(第一次报工) if (productOrder.getStartTime() == null) { productOrder.setStartTime(LocalDateTime.now()); } // 状态判断 if (newCompleteQty.compareTo(totalQty) >= 0 && totalQty.compareTo(BigDecimal.ZERO) > 0) { productOrder.setStatus(ProductOrderStatusEnum.FINISHED.getCode()); productOrder.setEndTime(LocalDateTime.now()); } else { productOrder.setStatus(ProductOrderStatusEnum.RUNNING.getCode()); } boolean update = productOrderService.updateById(productOrder); if (!update) { throw new ServiceException("报工失败,生产订单更新失败"); } // 完成报工主表-投入表-产出表数据 ProductionProductMain productionProductMain = new ProductionProductMain(); 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("报工失败,数据存储失败"); } ProductionProductInput productionProductInput = new ProductionProductInput(); productionProductInput.setProductMainId(productionProductMain.getId()); productionProductInput.setProductModelId(dto.getProductId()); productionProductInput.setQuantity(dto.getQuantity()); result = productionProductInputService.save(productionProductInput); if (!result) { throw new ServiceException("报工失败,生产投入存储失败"); } ProductionProductOutput productionProductOutput = new ProductionProductOutput(); productionProductOutput.setProductMainId(productionProductMain.getId()); productionProductOutput.setProductModelId(dto.getProductId()); productionProductOutput.setQuantity(dto.getQualifiedQuantity()); productionProductOutput.setScrapQty(dto.getUnqualifiedQuantity()); result = productionProductOutputService.save(productionProductOutput); if (!result) { throw new ServiceException("报工失败,生产产出存储失败"); } // 处理工序 List productionProductRouteItemDtoList = dto.getProductionProductRouteItemDtoList(); if (productionProductRouteItemDtoList == null || productionProductRouteItemDtoList.isEmpty()) { throw new ServiceException("报工失败,工序参数不能为空"); } for (ProductionProductRouteItemDto productRouteItemDto : productionProductRouteItemDtoList) { // 处理工序主表 ProductionProductRouteItem productRouteItemEntity = new ProductionProductRouteItem(); BeanUtils.copyProperties(productRouteItemDto, productRouteItemEntity, "id"); productRouteItemEntity.setProductMainId(productionProductMain.getId()); productionProductRouteItemService.save(productRouteItemEntity); // 处理参数及消耗产品 List productionProductRouteItemParamDtoList = productRouteItemDto.getProductionProductRouteItemParamDtoList(); if (productionProductRouteItemParamDtoList != null && !productionProductRouteItemParamDtoList.isEmpty()) { for (ProductionProductRouteItemParamDto productRouteItemParamDto : productionProductRouteItemParamDtoList) { 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()); paramEntity.setParamType(productionOrderRouteItemParam.getParamType()); paramEntity.setParamFormat(productionOrderRouteItemParam.getParamFormat()); paramEntity.setValueMode(productionOrderRouteItemParam.getValueMode()); } productionProductRouteItemParamService.save(paramEntity); } } // 处理工序上传的附件 List files = productRouteItemDto.getFiles(); if (files != null && !files.isEmpty()) { for (String tempId : files) { 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 = productRouteItemEntity.getId() + "_" + 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(productRouteItemEntity.getId()); 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()); } } } } } @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 routeItemList = productionProductRouteItemService.list(new LambdaQueryWrapper() .eq(ProductionProductRouteItem::getProductMainId, productMainId)); if (routeItemList != null && !routeItemList.isEmpty()) { List routeItemIds = routeItemList.stream() .map(ProductionProductRouteItem::getId) .collect(Collectors.toList()); // 查询并删除每个工序下的附件 List fileList = productionProductRouteItemFileService.list(new LambdaQueryWrapper() .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() .in(ProductionProductRouteItemFile::getProductionProductRouteItemId, routeItemIds)); } // 删除工序参数 productionProductRouteItemParamService.remove(new LambdaQueryWrapper() .in(ProductionProductRouteItemParam::getProductionProductRouteItemId, routeItemIds)); // 删除工序记录 productionProductRouteItemService.remove(new LambdaQueryWrapper() .eq(ProductionProductRouteItem::getProductMainId, productMainId)); } // 删除投入表 productionProductInputService.remove(new LambdaQueryWrapper() .eq(ProductionProductInput::getProductMainId, productMainId)); // 回滚生产订单完成数量 ProductionProductOutput output = productionProductOutputService.getOne(new LambdaQueryWrapper() .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() .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() .eq(ProductionProductInput::getProductMainId, productMainId) .last("LIMIT 1")); if (input != null) { dto.setProductId(input.getProductModelId()); dto.setQuantity(input.getQuantity()); } // 查询产出表(获取合格/不合格数量) ProductionProductOutput output = productionProductOutputService.getOne( new LambdaQueryWrapper() .eq(ProductionProductOutput::getProductMainId, productMainId) .last("LIMIT 1")); if (output != null) { dto.setQualifiedQuantity(output.getQuantity()); dto.setUnqualifiedQuantity(output.getScrapQty()); } // 查询工序列表 List routeItemList = productionProductRouteItemService.list( new LambdaQueryWrapper() .eq(ProductionProductRouteItem::getProductMainId, productMainId)); if (routeItemList != null && !routeItemList.isEmpty()) { List routeItemDtoList = routeItemList.stream().map(routeItem -> { ProductionProductRouteItemDto routeItemDto = new ProductionProductRouteItemDto(); BeanUtils.copyProperties(routeItem, routeItemDto); // 查询工序参数 List paramList = productionProductRouteItemParamService.list( new LambdaQueryWrapper() .eq(ProductionProductRouteItemParam::getProductionProductRouteItemId, routeItem.getId())); if (paramList != null && !paramList.isEmpty()) { List 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 fileRecordList = productionProductRouteItemFileService.list( new LambdaQueryWrapper() .eq(ProductionProductRouteItemFile::getProductionProductRouteItemId, routeItem.getId())); if (fileRecordList != null && !fileRecordList.isEmpty()) { List 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() .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() .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 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() .eq(ProductionProductRouteItemParam::getProductionProductRouteItemId, routeItemId)); List 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 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 delFileIds = routeItemDto.getDelFileIds(); if (delFileIds != null && !delFileIds.isEmpty()) { delFileIds.forEach(productionProductRouteItemFileService::deleteFile); } } log.info("报工记录编辑成功,productMainId={}", productMainId); } }