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.ProductionProductRouteItemDto; import com.ruoyi.production.dto.ProductionProductRouteItemParamDto; import com.ruoyi.production.dto.ProductionRecordDto; 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 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("报工失败,本次报工数量不能大于剩余可报工数量"); } // 设置开始时间(第一次报工) 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("BG" + UUID.randomUUID()); productionProductMain.setProductOrderId(dto.getProductOrderId()); productionProductMain.setSchedule(dto.getSchedule()); 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()); if (paramEntity.getProductId() != null) { } else { 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()); } } } } } }