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);
}
}