4 天以前 741918a903e17b2ec7522556d2c043b8d35dd8a1
src/main/java/com/ruoyi/production/service/impl/ProductionProductMainServiceImpl.java
@@ -1,9 +1,8 @@
package com.ruoyi.production.service.impl;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
@@ -13,14 +12,14 @@
import com.ruoyi.basic.mapper.ProductModelMapper;
import com.ruoyi.basic.pojo.Product;
import com.ruoyi.basic.pojo.ProductModel;
import com.ruoyi.common.enums.StockInQualifiedRecordTypeEnum;
import com.ruoyi.common.enums.StockOutQualifiedRecordTypeEnum;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.bean.BeanUtils;
import com.ruoyi.procurementrecord.mapper.ProcurementRecordOutMapper;
import com.ruoyi.procurementrecord.pojo.ProcurementRecordOut;
import com.ruoyi.procurementrecord.utils.StockUtils;
import com.ruoyi.production.controller.ProductWorkOrderController;
import com.ruoyi.production.dto.ProductStructureDto;
import com.ruoyi.production.dto.ProductionProductMainDto;
import com.ruoyi.production.bean.dto.ProductionProductMainDto;
import com.ruoyi.production.enums.ProductOrderStatusEnum;
import com.ruoyi.production.mapper.*;
import com.ruoyi.production.pojo.*;
import com.ruoyi.production.service.ProductionProductMainService;
@@ -28,291 +27,722 @@
import com.ruoyi.project.system.mapper.SysUserMapper;
import com.ruoyi.quality.mapper.*;
import com.ruoyi.quality.pojo.*;
import io.swagger.models.auth.In;
import com.ruoyi.stock.pojo.StockInRecord;
import com.ruoyi.stock.service.StockInRecordService;
import com.ruoyi.stock.dto.StockInventoryDto;
import com.ruoyi.stock.service.StockInventoryService;
import com.ruoyi.technology.mapper.TechnologyOperationMapper;
import com.ruoyi.technology.mapper.TechnologyRoutingOperationMapper;
import com.ruoyi.technology.pojo.TechnologyOperation;
import com.ruoyi.technology.pojo.TechnologyRoutingOperation;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.ruoyi.production.mapper.ProductionProductMainMapper;
import oshi.driver.mac.net.NetStat;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
@Service
@AllArgsConstructor
@Transactional(rollbackFor = Exception.class)
public class ProductionProductMainServiceImpl extends ServiceImpl<ProductionProductMainMapper, ProductionProductMain> implements ProductionProductMainService {
    private final ProcurementRecordOutMapper procurementRecordOutMapper;
    private ProductionProductMainMapper productionProductMainMapper;
    private ProductWorkOrderController productWorkOrderController;
    private ProductWorkOrderMapper productWorkOrderMapper;
    private ProductProcessRouteItemMapper productProcessRouteItemMapper;
    private SysUserMapper userMapper;
    private ProductionProductOutputMapper productionProductOutputMapper;
    private ProcessRouteItemMapper processRouteItemMapper;
    private ProductModelMapper productModelMapper;
    private QualityInspectMapper qualityInspectMapper;
    private ProductProcessMapper productProcessMapper;
    private ProductProcessRouteMapper productProcessRouteMapper;
    private ProductMapper productMapper;
    private QualityTestStandardParamMapper qualityTestStandardParamMapper;
    private QualityTestStandardBindingMapper qualityTestStandardBindingMapper;
    private QualityTestStandardMapper qualityTestStandardMapper;
    private QualityInspectParamMapper qualityInspectParamMapper;
    private ProductStructureMapper productStructureMapper;
    private ProductionProductInputMapper productionProductInputMapper;
    private ProductOrderMapper productOrderMapper;
    private SalesLedgerProductionAccountingMapper salesLedgerProductionAccountingMapper;
    private StockUtils stockUtils;
    private final ProductionProductMainMapper productionProductMainMapper;
    private final SysUserMapper userMapper;
    private final ProductionProductOutputMapper productionProductOutputMapper;
    private final ProductModelMapper productModelMapper;
    private final QualityInspectMapper qualityInspectMapper;
    private final QualityUnqualifiedMapper qualityUnqualifiedMapper;
    private final ProductMapper productMapper;
    private final QualityTestStandardParamMapper qualityTestStandardParamMapper;
    private final QualityTestStandardMapper qualityTestStandardMapper;
    private final QualityInspectParamMapper qualityInspectParamMapper;
    private final ProductionProductInputMapper productionProductInputMapper;
    private final ProductionAccountMapper productionAccountMapper;
    private final ProductionOperationTaskMapper productionOperationTaskMapper;
    private final ProductionOrderMapper productionOrderMapper;
    private final ProductionOrderRoutingOperationMapper productionOrderRoutingOperationMapper;
    private final ProductionOrderRoutingOperationParamMapper productionOrderRoutingOperationParamMapper;
    private final TechnologyRoutingOperationMapper technologyRoutingOperationMapper;
    private final TechnologyOperationMapper technologyOperationMapper;
    private final StockUtils stockUtils;
    private final StockInRecordService stockInRecordService;
    private final StockInventoryService stockInventoryService;
    @Override
    public IPage<ProductionProductMainDto> listPageProductionProductMainDto(Page page, ProductionProductMainDto productionProductMainDto) {
        return productionProductMainMapper.listPageProductionProductMainDto(page, productionProductMainDto);
        // 分页查询生产报工主表
        IPage<ProductionProductMainDto> result = productionProductMainMapper.listPageProductionProductMainDto(page, productionProductMainDto);
        fillOperationParamList(result.getRecords());
        return result;
    }
    @Override
    public IPage<ProductionProductMainDto> pageProductionProductMain(Page page, ProductionProductMainDto productionProductMainDto) {
        // 分页查询生产报工主表
        return listPageProductionProductMainDto(page, productionProductMainDto);
    }
    @Override
    public ProductionProductMainDto getProductionProductMainInfo(Long id) {
        // 获取生产产品主表详情
        return listPageProductionProductMainDto(new Page<>(1, 1), new ProductionProductMainDto() {{
            setId(id);
        }}).getRecords().stream().findFirst().orElse(null);
    }
    private void fillOperationParamList(List<ProductionProductMainDto> recordList) {
        // 填充工序参数列表
        if (recordList == null || recordList.isEmpty()) {
            return;
        }
        // 遍历处理数据并组装结果
        Set<Long> mainIdSet = recordList.stream()
                .map(ProductionProductMainDto::getId)
                .filter(Objects::nonNull)
                .collect(Collectors.toCollection(LinkedHashSet::new));
        if (mainIdSet.isEmpty()) {
            recordList.forEach(item -> item.setProductionOperationParamList(Collections.emptyList()));
            return;
        }
        // 查询并准备业务数据
        List<ProductionOrderRoutingOperationParam> paramList = productionOrderRoutingOperationParamMapper.selectList(
                Wrappers.<ProductionOrderRoutingOperationParam>lambdaQuery()
                        .in(ProductionOrderRoutingOperationParam::getProductionProductMainId, mainIdSet)
                        .orderByAsc(ProductionOrderRoutingOperationParam::getId));
        Map<Long, List<ProductionOrderRoutingOperationParam>> paramGroupMap = new HashMap<>();
        for (ProductionOrderRoutingOperationParam param : paramList) {
            if (param == null || param.getProductionProductMainId() == null) {
                continue;
            }
            paramGroupMap.computeIfAbsent(param.getProductionProductMainId(), key -> new ArrayList<>()).add(param);
        }
        Set<Long> missingMainIdSet = new LinkedHashSet<>();
        for (ProductionProductMainDto item : recordList) {
            Long mainId = item.getId();
            if (mainId == null) {
                item.setProductionOperationParamList(Collections.emptyList());
                continue;
            }
            List<ProductionOrderRoutingOperationParam> params = paramGroupMap.get(mainId);
            if (params != null && !params.isEmpty()) {
                item.setProductionOperationParamList(params);
                continue;
            }
            missingMainIdSet.add(mainId);
        }
        if (missingMainIdSet.isEmpty()) {
            return;
        }
        // 兼容历史数据:旧报工记录没有按报工ID落参数快照时,回退展示工序模板参数。
        List<ProductionProductMain> mainList = productionProductMainMapper.selectBatchIds(missingMainIdSet);
        Map<Long, Long> mainIdToTaskIdMap = mainList.stream()
                .filter(Objects::nonNull)
                .filter(item -> item.getId() != null)
                .collect(Collectors.toMap(ProductionProductMain::getId,
                        ProductionProductMain::getProductionOperationTaskId, (left, right) -> left));
        Set<Long> taskIdSet = mainIdToTaskIdMap.values().stream()
                .filter(Objects::nonNull)
                .collect(Collectors.toCollection(LinkedHashSet::new));
        if (taskIdSet.isEmpty()) {
            for (ProductionProductMainDto item : recordList) {
                if (item.getId() != null && missingMainIdSet.contains(item.getId())) {
                    item.setProductionOperationParamList(Collections.emptyList());
                }
            }
            return;
        }
        List<ProductionOperationTask> taskList = productionOperationTaskMapper.selectList(
                Wrappers.<ProductionOperationTask>lambdaQuery()
                        .in(ProductionOperationTask::getId, taskIdSet));
        Map<Long, Long> taskIdToRoutingOperationIdMap = taskList.stream()
                .filter(Objects::nonNull)
                .filter(item -> item.getId() != null)
                .collect(Collectors.toMap(ProductionOperationTask::getId,
                        ProductionOperationTask::getProductionOrderRoutingOperationId, (left, right) -> left));
        Set<Long> routingOperationIdSet = taskIdToRoutingOperationIdMap.values().stream()
                .filter(Objects::nonNull)
                .collect(Collectors.toCollection(LinkedHashSet::new));
        if (routingOperationIdSet.isEmpty()) {
            for (ProductionProductMainDto item : recordList) {
                if (item.getId() != null && missingMainIdSet.contains(item.getId())) {
                    item.setProductionOperationParamList(Collections.emptyList());
                }
            }
            return;
        }
        List<ProductionOrderRoutingOperationParam> fallbackParamList = productionOrderRoutingOperationParamMapper.selectList(
                Wrappers.<ProductionOrderRoutingOperationParam>lambdaQuery()
                        .in(ProductionOrderRoutingOperationParam::getProductionOrderRoutingOperationId, routingOperationIdSet)
                        .isNull(ProductionOrderRoutingOperationParam::getProductionProductMainId)
                        .orderByAsc(ProductionOrderRoutingOperationParam::getId));
        Map<Long, List<ProductionOrderRoutingOperationParam>> fallbackGroupMap = new HashMap<>();
        for (ProductionOrderRoutingOperationParam param : fallbackParamList) {
            if (param == null || param.getProductionOrderRoutingOperationId() == null) {
                continue;
            }
            fallbackGroupMap.computeIfAbsent(param.getProductionOrderRoutingOperationId(), key -> new ArrayList<>()).add(param);
        }
        for (ProductionProductMainDto item : recordList) {
            Long mainId = item.getId();
            if (mainId == null || !missingMainIdSet.contains(mainId)) {
                continue;
            }
            Long taskId = mainIdToTaskIdMap.get(mainId);
            Long routingOperationId = taskId == null ? null : taskIdToRoutingOperationIdMap.get(taskId);
            if (routingOperationId == null) {
                item.setProductionOperationParamList(Collections.emptyList());
                continue;
            }
            item.setProductionOperationParamList(fallbackGroupMap.getOrDefault(routingOperationId, Collections.emptyList()));
        }
    }
    @Override
    public Boolean addProductMain(ProductionProductMainDto dto) {
        SysUser user = userMapper.selectUserById(dto.getUserId());
        ProductionProductMain productionProductMain = new ProductionProductMain();
        //当前工艺路线对应的工序详情
        ProductProcessRouteItem productProcessRouteItem = productProcessRouteItemMapper.selectById(dto.getProductProcessRouteItemId());
        if (productProcessRouteItem == null) {
            throw new RuntimeException("工艺路线项不存在");
        // 兼容旧流程:如果没有开始报工记录ID,走一步报工(自动创建+结束)
        Long startRecordId = dto.resolveStartRecordId();
        if (startRecordId == null) {
            return oneStepWork(dto);
        }
        //当前具体工序
        ProductProcess productProcess = productProcessMapper.selectById(productProcessRouteItem.getProcessId());
        //工艺路线中当前工序对应的产出规格型号
        ProductModel productModel = productModelMapper.selectById(productProcessRouteItem.getProductModelId());
        //查询该生产订单对应的bom
        ProductProcessRoute productProcessRoute = productProcessRouteMapper.selectById(productProcessRouteItem.getProductRouteId());
        /*新增报工主表*/
        //查询最大报工编号
        dto.setId(startRecordId);
        return finishWork(dto);
    }
    @Override
    public Boolean startWork(ProductionProductMainDto dto) {
        // 开始报工:创建报工记录,标记实际开始时间
        Long taskId = resolveTaskId(dto);
        if (taskId == null) {
            throw new ServiceException("请传入生产工单ID");
        }
        ProductionOperationTask task = productionOperationTaskMapper.selectById(taskId);
        if (task == null) {
            throw new ServiceException("生产工单不存在");
        }
        // 权限校验:已指派时仅被指派人可操作
        validateWorkerPermission(task);
        if (task.getStatus() != null && task.getStatus() != 2 && task.getStatus() != 3) {
            throw new ServiceException("当前工单状态不允许开始报工,仅待生产或生产中状态的工单可操作");
        }
        SysUser user = userMapper.selectUserById(dto.getUserId());
        ProductionProductMain main = new ProductionProductMain();
        main.setProductNo(generateProductNo());
        main.setUserId(user == null ? dto.getUserId() : user.getUserId());
        main.setUserName(user == null ? dto.getUserName() : user.getNickName());
        main.setProductionOperationTaskId(taskId);
        main.setStatus(0);
        main.setActualStartTime(LocalDateTime.now());
        productionProductMainMapper.insert(main);
        // 工单状态 -> 生产中
        task.setStatus(3);
        if (task.getActualStartTime() == null) {
            task.setActualStartTime(LocalDateTime.now());
        }
        productionOperationTaskMapper.updateById(task);
        // 生产订单 -> 生产进行中
        ProductionOrder productionOrder = productionOrderMapper.selectById(task.getProductionOrderId());
        if (productionOrder != null && productionOrder.getStartTime() == null) {
            productionOrder.setStartTime(LocalDateTime.now());
            productionOrder.setStatus(ProductOrderStatusEnum.RUNNING.getCode());
            productionOrderMapper.updateById(productionOrder);
        }
        return true;
    }
    private Boolean oneStepWork(ProductionProductMainDto dto) {
        // 兼容旧一步报工流程:自动创建开始记录并立即结束
        Long taskId = resolveTaskId(dto);
        if (taskId == null) {
            throw new ServiceException("请传入生产工单ID");
        }
        ProductionOperationTask task = productionOperationTaskMapper.selectById(taskId);
        if (task == null) {
            throw new ServiceException("生产工单不存在");
        }
        validateWorkerPermission(task);
        if (task.getStatus() != null && task.getStatus() != 2 && task.getStatus() != 3) {
            throw new ServiceException("当前工单状态不允许报工");
        }
        SysUser user = userMapper.selectUserById(dto.getUserId());
        ProductionProductMain main = new ProductionProductMain();
        main.setProductNo(generateProductNo());
        main.setUserId(user == null ? dto.getUserId() : user.getUserId());
        main.setUserName(user == null ? dto.getUserName() : user.getNickName());
        main.setProductionOperationTaskId(taskId);
        main.setStatus(0);
        main.setActualStartTime(LocalDateTime.now());
        productionProductMainMapper.insert(main);
        if (task.getActualStartTime() == null) {
            task.setActualStartTime(LocalDateTime.now());
        }
        task.setStatus(3);
        productionOperationTaskMapper.updateById(task);
        ProductionOrder productionOrder = productionOrderMapper.selectById(task.getProductionOrderId());
        if (productionOrder != null && productionOrder.getStartTime() == null) {
            productionOrder.setStartTime(LocalDateTime.now());
            productionOrder.setStatus(ProductOrderStatusEnum.RUNNING.getCode());
            productionOrderMapper.updateById(productionOrder);
        }
        dto.setId(main.getId());
        return finishWork(dto);
    }
    private Boolean finishWork(ProductionProductMainDto dto) {
        // 结束报工:更新开始报工记录,创建产出、投入品、核算记录
        ProductionProductMain currentMain = productionProductMainMapper.selectById(dto.getId());
        if (currentMain == null) {
            throw new ServiceException("开始报工记录不存在");
        }
        if (currentMain.getStatus() == null || currentMain.getStatus() != 0) {
            throw new ServiceException("该报工记录已结束或状态异常");
        }
        Long taskId = currentMain.getProductionOperationTaskId();
        ProductionOperationTask task = productionOperationTaskMapper.selectById(taskId);
        if (task == null) {
            throw new ServiceException("生产工单不存在");
        }
        validateWorkerPermission(task);
        // 更新报工记录为已结束,记录结束时间
        currentMain.setActualEndTime(LocalDateTime.now());
        currentMain.setStatus(1);
        if (dto.getWorkHour() != null) {
            currentMain.setWorkHour(dto.getWorkHour());
        } else if (currentMain.getActualStartTime() != null) {
            // 根据开始/结束时间计算实际工时(小时)
            long seconds = Duration.between(currentMain.getActualStartTime(), currentMain.getActualEndTime()).getSeconds();
            currentMain.setWorkHour(BigDecimal.valueOf(seconds).divide(BigDecimal.valueOf(3600), 4, RoundingMode.HALF_UP));
        }
        productionProductMainMapper.updateById(currentMain);
        // 同步工序参数
        ProductionOrderRoutingOperation routingOperation = productionOrderRoutingOperationMapper.selectById(task.getProductionOrderRoutingOperationId());
        if (routingOperation != null) {
            syncOperationParamInputValue(dto, routingOperation.getId(), currentMain.getId());
        }
        // 获取产品规格
        ProductModel productModel = null;
        if (routingOperation != null) {
            productModel = productModelMapper.selectById(
                    routingOperation.getProductModelId() != null ? routingOperation.getProductModelId() : getProductionOrderProductModelId(task));
        }
        if (productModel == null) {
            throw new ServiceException("产品规格不存在");
        }
        // 投入品
        ProductionProductInput productionProductInput = new ProductionProductInput();
        productionProductInput.setProductionProductMainId(currentMain.getId());
        productionProductInput.setProductModelId(productModel.getId());
        productionProductInput.setInputQuantity(defaultDecimal(dto.getQuantity()));
        productionProductInput.setQuantity(productionProductInput.getInputQuantity());
        productionProductInputMapper.insert(productionProductInput);
        // 产出品
        ProductionProductOutput productionProductOutput = new ProductionProductOutput();
        productionProductOutput.setProductionProductMainId(currentMain.getId());
        productionProductOutput.setProductModelId(productModel.getId());
        productionProductOutput.setQuantity(defaultDecimal(dto.getQuantity()));
        productionProductOutput.setScrapQty(defaultDecimal(dto.getScrapQty()));
        productionProductOutputMapper.insert(productionProductOutput);
        BigDecimal reportQty = defaultDecimal(productionProductOutput.getQuantity());
        BigDecimal scrapQty = defaultDecimal(productionProductOutput.getScrapQty());
        BigDecimal productQty = reportQty;
        String qualifiedBatchNo = null;
        TechnologyRoutingOperation technologyRoutingOperation = routingOperation != null
                ? technologyRoutingOperationMapper.selectById(routingOperation.getTechnologyRoutingOperationId()) : null;
        TechnologyOperation technologyOperation = technologyRoutingOperation == null ? null
                : technologyOperationMapper.selectById(technologyRoutingOperation.getTechnologyOperationId());
        List<ProductionOrderRoutingOperation> routingOperationList = routingOperation != null ? productionOrderRoutingOperationMapper.selectList(
                Wrappers.<ProductionOrderRoutingOperation>lambdaQuery()
                        .eq(ProductionOrderRoutingOperation::getOrderRoutingId, routingOperation.getOrderRoutingId())
                        .eq(ProductionOrderRoutingOperation::getProductionOrderId, routingOperation.getProductionOrderId())) : new ArrayList<>();
        boolean isLastOperation = routingOperation != null && routingOperation.getDragSort() != null
                && routingOperation.getDragSort().equals(routingOperationList.size());
        if (productQty.compareTo(BigDecimal.ZERO) > 0) {
            if (routingOperation != null && Boolean.TRUE.equals(routingOperation.getIsQuality())) {
                int inspectType = isLastOperation ? 2 : 1;
                String process = isLastOperation ? null : technologyOperation == null ? null : technologyOperation.getName();
                Product product = productMapper.selectById(productModel.getProductId());
                QualityInspect qualityInspect = new QualityInspect();
                qualityInspect.setProductId(product.getId());
                qualityInspect.setProductName(product.getProductName());
                qualityInspect.setModel(productModel.getModel());
                qualityInspect.setUnit(productModel.getUnit());
                qualityInspect.setQuantity(productQty);
                qualityInspect.setProcess(process);
                qualityInspect.setInspectState(0);
                qualityInspect.setInspectType(inspectType);
                qualityInspect.setProductMainId(currentMain.getId());
                qualityInspect.setProductModelId(productModel.getId());
                qualityInspectMapper.insert(qualityInspect);
                List<QualityTestStandard> qualityTestStandard = qualityTestStandardMapper.getQualityTestStandardByProductId(product.getId(), inspectType, process);
                if (!qualityTestStandard.isEmpty()) {
                    qualityInspect.setTestStandardId(qualityTestStandard.get(0).getId());
                    qualityInspectMapper.updateById(qualityInspect);
                    qualityTestStandardParamMapper.selectList(Wrappers.<QualityTestStandardParam>lambdaQuery()
                                    .eq(QualityTestStandardParam::getTestStandardId, qualityTestStandard.get(0).getId()))
                            .forEach(qualityTestStandardParam -> {
                                QualityInspectParam param = new QualityInspectParam();
                                BeanUtils.copyProperties(qualityTestStandardParam, param);
                                param.setId(null);
                                param.setInspectId(qualityInspect.getId());
                                qualityInspectParamMapper.insert(param);
                            });
                }
            } else {
                StockInventoryDto stockInventoryDto = new StockInventoryDto();
                stockInventoryDto.setRecordId(currentMain.getId());
                stockInventoryDto.setRecordType(String.valueOf(StockInQualifiedRecordTypeEnum.PRODUCTION_REPORT_STOCK_IN.getCode()));
                stockInventoryDto.setQualitity(productQty);
                stockInventoryDto.setProductModelId(productModel.getId());
                stockInventoryService.addStockInRecordOnly(stockInventoryDto);
                qualifiedBatchNo = resolveLatestStockInBatchNo(
                        currentMain.getId(),
                        StockInQualifiedRecordTypeEnum.PRODUCTION_REPORT_STOCK_IN.getCode(),
                        productModel.getId(),
                        "0");
            }
            task.setCompleteQuantity(defaultDecimal(task.getCompleteQuantity()).add(productQty));
            if (task.getActualStartTime() == null) {
                task.setActualStartTime(LocalDateTime.now());
            }
            task.setStatus(3);
            if (task.getPlanQuantity() != null
                    && task.getCompleteQuantity().compareTo(task.getPlanQuantity()) >= 0) {
                task.setActualEndTime(LocalDateTime.now());
                task.setStatus(4);
            }
            productionOperationTaskMapper.updateById(task);
            ProductionOrder productionOrder = productionOrderMapper.selectById(task.getProductionOrderId());
            if (productionOrder != null) {
                if (productionOrder.getStartTime() == null) {
                    productionOrder.setStartTime(LocalDateTime.now());
                }
                productionOrder.setStatus(ProductOrderStatusEnum.RUNNING.getCode());
                if (isLastOperation) {
                    productionOrder.setCompleteQuantity(defaultDecimal(productionOrder.getCompleteQuantity()).add(productQty));
                    if (productionOrder.getQuantity() != null
                            && productionOrder.getCompleteQuantity().compareTo(productionOrder.getQuantity()) >= 0) {
                        productionOrder.setEndTime(LocalDateTime.now());
                        productionOrder.setStatus(ProductOrderStatusEnum.FINISHED.getCode());
                    }
                }
                productionOrderMapper.updateById(productionOrder);
            }
            BigDecimal workHours = BigDecimal.ZERO;
            if (technologyOperation != null && technologyOperation.getSalaryQuota() != null) {
                workHours = Integer.valueOf(1).equals(technologyOperation.getType())
                        ? technologyOperation.getSalaryQuota().multiply(productQty)
                        : technologyOperation.getSalaryQuota();
            }
            ProductionAccount productionAccount = new ProductionAccount();
            productionAccount.setProductionProductMainId(currentMain.getId());
            SysUser user = userMapper.selectUserById(dto.getUserId());
            productionAccount.setSchedulingUserId(user == null ? currentMain.getUserId() : user.getUserId());
            productionAccount.setSchedulingUserName(user == null ? currentMain.getUserName() : user.getNickName());
            productionAccount.setFinishedNum(productQty);
            productionAccount.setWorkHours(technologyOperation != null ? technologyOperation.getSalaryQuota() : null);
            productionAccount.setTechnologyOperationName(technologyOperation == null ? null : technologyOperation.getName());
            productionAccount.setSchedulingDate(currentMain.getActualEndTime() != null ? currentMain.getActualEndTime() : LocalDateTime.now());
            productionAccountMapper.insert(productionAccount);
        }
        if (scrapQty.compareTo(BigDecimal.ZERO) > 0) {
            stockUtils.addUnStockWithBatchNo(
                    productModel.getId(),
                    scrapQty,
                    StockInQualifiedRecordTypeEnum.PRODUCTION_SCRAP.getCode(),
                    currentMain.getId(),
                    qualifiedBatchNo);
        }
        return true;
    }
    private Long getProductionOrderProductModelId(ProductionOperationTask task) {
        // 从生产订单获取产品规格ID
        if (task == null || task.getProductionOrderId() == null) {
            return null;
        }
        ProductionOrder order = productionOrderMapper.selectById(task.getProductionOrderId());
        return order == null ? null : order.getProductModelId();
    }
    private void validateWorkerPermission(ProductionOperationTask task) {
        // 校验当前用户是否有权操作此工单:未指派时人人可操作,已指派时仅被指派人可操作
        if (task == null) {
            return;
        }
        String userIds = task.getUserIds();
        if (userIds == null || userIds.isEmpty() || "[]".equals(userIds.trim())) {
            return;
        }
        Long currentUserId = SecurityUtils.getUserId();
        if (currentUserId == null) {
            return;
        }
        List<Long> assignedIds = JSON.parseArray(userIds, Long.class);
        if (assignedIds == null || !assignedIds.contains(currentUserId)) {
            throw new ServiceException("您未被指派到此工单,无法操作");
        }
    }
    @Override
    public Boolean saveProductionProductMain(ProductionProductMainDto productionProductMainDto) {
        // 保存生产报工主记录
        return addProductMain(productionProductMainDto);
    }
    @Override
    public Boolean removeProductMain(Long id) {
        // 删除生产报工主记录
        ProductionProductMain currentMain = productionProductMainMapper.selectById(id);
        if (currentMain == null) {
            return true;
        }
        return removeProductMainByProductionTask(currentMain);
    }
    private String resolveLatestStockInBatchNo(Long recordId,
                                               String recordType,
                                               Long productModelId,
                                               String stockType) {
        if (recordId == null || productModelId == null) {
            return null;
        }
        StockInRecord stockInRecord = stockInRecordService.getOne(
                Wrappers.<StockInRecord>lambdaQuery()
                        .eq(StockInRecord::getRecordId, recordId)
                        .eq(StockInRecord::getRecordType, recordType)
                        .eq(StockInRecord::getProductModelId, productModelId)
                        .eq(StockInRecord::getType, stockType)
                        .orderByDesc(StockInRecord::getId)
                        .last("limit 1"),
                false);
        if (stockInRecord == null) {
            throw new ServiceException("未找到对应的入库申请记录");
        }
        return stockInRecord.getBatchNo();
    }
    private void syncOperationParamInputValue(ProductionProductMainDto dto,
                                              Long productionOrderRoutingOperationId,
                                              Long productionProductMainId) {
        if (dto == null || productionOrderRoutingOperationId == null || productionProductMainId == null) {
            return;
        }
        List<ProductionOrderRoutingOperationParam> paramList = dto.getProductionOperationParamList();
        if (paramList == null || paramList.isEmpty()) {
            return;
        }
        Set<Long> sourceParamIdSet = paramList.stream()
                .filter(Objects::nonNull)
                .map(ProductionOrderRoutingOperationParam::getId)
                .filter(Objects::nonNull)
                .collect(Collectors.toCollection(LinkedHashSet::new));
        if (sourceParamIdSet.isEmpty()) {
            return;
        }
        List<ProductionOrderRoutingOperationParam> dbParamList = productionOrderRoutingOperationParamMapper.selectList(
                Wrappers.<ProductionOrderRoutingOperationParam>lambdaQuery()
                        .in(ProductionOrderRoutingOperationParam::getId, sourceParamIdSet)
                        .eq(ProductionOrderRoutingOperationParam::getProductionOrderRoutingOperationId, productionOrderRoutingOperationId));
        if (dbParamList == null || dbParamList.isEmpty()) {
            return;
        }
        Map<Long, ProductionOrderRoutingOperationParam> dbParamMap = dbParamList.stream()
                .filter(item -> item != null && item.getId() != null)
                .collect(Collectors.toMap(ProductionOrderRoutingOperationParam::getId, item -> item, (left, right) -> left));
        for (ProductionOrderRoutingOperationParam param : paramList) {
            if (param == null || param.getId() == null) {
                continue;
            }
            ProductionOrderRoutingOperationParam dbParam = dbParamMap.get(param.getId());
            if (dbParam == null) {
                throw new ServiceException("工序参数不存在或不属于当前工单工序,ID=" + param.getId());
            }
            productionOrderRoutingOperationParamMapper.insert(buildReportParamSnapshot(dbParam, param.getInputValue(), productionProductMainId));
        }
    }
    private ProductionOrderRoutingOperationParam buildReportParamSnapshot(ProductionOrderRoutingOperationParam source,
                                                                          String inputValue,
                                                                          Long productionProductMainId) {
        ProductionOrderRoutingOperationParam target = new ProductionOrderRoutingOperationParam();
        target.setProductionOrderId(source.getProductionOrderId());
        target.setTechnologyRoutingOperationParamId(source.getTechnologyRoutingOperationParamId());
        target.setParamCode(source.getParamCode());
        target.setParamName(source.getParamName());
        target.setParamType(source.getParamType());
        target.setParamFormat(source.getParamFormat());
        target.setUnit(source.getUnit());
        target.setIsRequired(source.getIsRequired());
        target.setRemark(source.getRemark());
        target.setParamId(source.getParamId());
        target.setTechnologyOperationId(source.getTechnologyOperationId());
        target.setTechnologyOperationParamId(source.getTechnologyOperationParamId());
        target.setStandardValue(source.getStandardValue());
        target.setInputValue(inputValue);
        target.setProductionOrderRoutingOperationId(source.getProductionOrderRoutingOperationId());
        target.setProductionProductMainId(productionProductMainId);
        return target;
    }
    private Boolean removeProductMainByProductionTask(ProductionProductMain productionProductMain) {
        // 按生产任务回滚并删除报工主记录
        List<QualityInspect> qualityInspects = qualityInspectMapper.selectList(
                Wrappers.<QualityInspect>lambdaQuery().eq(QualityInspect::getProductMainId, productionProductMain.getId()));
        // 参数与前置条件校验
        if (qualityInspects.size() > 0) {
            List<QualityUnqualified> qualityUnqualifieds = qualityUnqualifiedMapper.selectList(
                    Wrappers.<QualityUnqualified>lambdaQuery()
        // 遍历处理数据并组装结果
                            .in(QualityUnqualified::getInspectId, qualityInspects.stream().map(QualityInspect::getId).collect(Collectors.toList())));
            if (qualityUnqualifieds.size() > 0 && qualityUnqualifieds.get(0).getInspectState() == 1) {
                throw new ServiceException("该条报工已经不合格处理了,不允许删除");
            }
        }
        ProductionProductOutput productionProductOutput = productionProductOutputMapper.selectList(
                Wrappers.<ProductionProductOutput>lambdaQuery()
                        .eq(ProductionProductOutput::getProductionProductMainId, productionProductMain.getId()))
                .stream().findFirst().orElse(null);
        productionAccountMapper.delete(new LambdaQueryWrapper<ProductionAccount>()
                .eq(ProductionAccount::getProductionProductMainId, productionProductMain.getId()));
        ProductionOperationTask productionOperationTask = productionOperationTaskMapper.selectById(productionProductMain.getProductionOperationTaskId());
        if (productionOperationTask != null && productionProductOutput != null) {
            BigDecimal reportQuantity = defaultDecimal(productionProductOutput.getQuantity());
            productionOperationTask.setCompleteQuantity(defaultDecimal(productionOperationTask.getCompleteQuantity()).subtract(reportQuantity));
            productionOperationTask.setActualEndTime(null);
            if (defaultDecimal(productionOperationTask.getCompleteQuantity()).compareTo(BigDecimal.ZERO) <= 0) {
                productionOperationTask.setCompleteQuantity(BigDecimal.ZERO);
                productionOperationTask.setActualStartTime(null);
                productionOperationTask.setStatus(2);
            } else {
                productionOperationTask.setStatus(3);
            }
        // 持久化或输出处理结果
            productionOperationTaskMapper.updateById(productionOperationTask);
            ProductionOrder productionOrder = productionOrderMapper.selectById(productionOperationTask.getProductionOrderId());
            ProductionOrderRoutingOperation routingOperation = productionOrderRoutingOperationMapper.selectById(productionOperationTask.getProductionOrderRoutingOperationId());
            if (productionOrder != null && routingOperation != null) {
                // 只有最后一道工序的报工才会影响生产订单完工数量。
                List<ProductionOrderRoutingOperation> routingOperationList = productionOrderRoutingOperationMapper.selectList(
                        Wrappers.<ProductionOrderRoutingOperation>lambdaQuery()
                                .eq(ProductionOrderRoutingOperation::getOrderRoutingId, routingOperation.getOrderRoutingId())
                                .eq(ProductionOrderRoutingOperation::getProductionOrderId, routingOperation.getProductionOrderId()));
                boolean isLastOperation = routingOperation.getDragSort() != null && routingOperation.getDragSort().equals(routingOperationList.size());
                if (isLastOperation) {
                    BigDecimal newCompleteQty = defaultDecimal(productionOrder.getCompleteQuantity()).subtract(reportQuantity);
                    productionOrder.setCompleteQuantity(newCompleteQty.compareTo(BigDecimal.ZERO) < 0 ? BigDecimal.ZERO : newCompleteQty);
                    productionOrder.setEndTime(null);
                }
                if (defaultDecimal(productionOrder.getCompleteQuantity()).compareTo(BigDecimal.ZERO) <= 0) {
                    productionOrder.setStartTime(null);
                    productionOrder.setStatus(ProductOrderStatusEnum.WAIT.getCode());
                } else {
                    productionOrder.setStatus(ProductOrderStatusEnum.RUNNING.getCode());
                }
                productionOrderMapper.updateById(productionOrder);
            }
        }
        qualityInspectMapper.selectList(new LambdaQueryWrapper<QualityInspect>()
                .eq(QualityInspect::getProductMainId, productionProductMain.getId())).forEach(q -> {
            qualityInspectParamMapper.delete(new LambdaQueryWrapper<QualityInspectParam>()
                    .eq(QualityInspectParam::getInspectId, q.getId()));
            qualityInspectMapper.deleteById(q.getId());
            stockUtils.deleteStockInRecord(q.getId(), StockInQualifiedRecordTypeEnum.QUALITYINSPECT_STOCK_IN.getCode());
        });
        productionProductOutputMapper.delete(new LambdaQueryWrapper<ProductionProductOutput>()
                .eq(ProductionProductOutput::getProductionProductMainId, productionProductMain.getId()));
        productionProductInputMapper.delete(new LambdaQueryWrapper<ProductionProductInput>()
                .eq(ProductionProductInput::getProductionProductMainId, productionProductMain.getId()));
        productionOrderRoutingOperationParamMapper.delete(
                Wrappers.<ProductionOrderRoutingOperationParam>lambdaQuery()
                        .eq(ProductionOrderRoutingOperationParam::getProductionProductMainId, productionProductMain.getId()));
        stockUtils.deleteStockInRecord(productionProductMain.getId(), StockInQualifiedRecordTypeEnum.PRODUCTION_SCRAP.getCode());
        stockUtils.deleteStockInRecord(productionProductMain.getId(), StockInQualifiedRecordTypeEnum.PRODUCTION_REPORT_STOCK_IN.getCode());
        stockUtils.deleteStockOutRecord(productionProductMain.getId(), StockOutQualifiedRecordTypeEnum.PRODUCTION_REPORT_STOCK_OUT.getCode());
        productionProductMainMapper.deleteById(productionProductMain.getId());
        return true;
    }
    private String generateProductNo() {
        // 生成下一个生产产品编号
        String datePrefix = "BG" + LocalDate.now().format(DateTimeFormatter.ofPattern("yyMMdd"));
        QueryWrapper<ProductionProductMain> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("MAX(product_no) as maxNo")
                .likeRight("product_no", datePrefix);
        queryWrapper.select("MAX(product_no) as maxNo").likeRight("product_no", datePrefix);
        List<Map<String, Object>> resultList = productionProductMainMapper.selectMaps(queryWrapper);
        int sequenceNumber = 1;
        if (resultList != null && !resultList.isEmpty()) {
            Map<String, Object> result = resultList.get(0);
            if (result != null) {
                Object maxNoObj = result.get("maxNo");
                if (maxNoObj != null) {
                    String lastNo = maxNoObj.toString();
                    System.out.println("lastNo: " + lastNo);
                    if (lastNo.startsWith(datePrefix)) {
                        try {
                            String seqStr = lastNo.substring(datePrefix.length());
                            sequenceNumber = Integer.parseInt(seqStr) + 1;
                        } catch (NumberFormatException e) {
                            sequenceNumber = 1;
                        }
            if (result != null && result.get("maxNo") != null) {
                String lastNo = result.get("maxNo").toString();
                if (lastNo.startsWith(datePrefix)) {
                    try {
                        sequenceNumber = Integer.parseInt(lastNo.substring(datePrefix.length())) + 1;
                    } catch (NumberFormatException e) {
                        sequenceNumber = 1;
                    }
                }
            }
        }
        String productNo = String.format("%s%03d", datePrefix, sequenceNumber);
        productionProductMain.setProductNo(productNo);
        productionProductMain.setUserId(dto.getUserId());
        productionProductMain.setUserName(dto.getUserName());
        productionProductMain.setProductProcessRouteItemId(dto.getProductProcessRouteItemId());
        productionProductMain.setWorkOrderId(dto.getWorkOrderId());
        productionProductMain.setStatus(0);
        productionProductMainMapper.insert(productionProductMain);
        /*新增报工投入表*/
        List<ProductStructureDto> productStructureDtos = productStructureMapper.listBybomAndProcess(productProcessRoute.getBomId(), productProcess.getId());
        if (productStructureDtos.size() == 0) {
            //如果该工序没有产品结构的投入品,那这个投入品和产出品是同一个
            ProductStructureDto productStructureDto = new ProductStructureDto();
            productStructureDto.setProductModelId(productProcessRouteItem.getProductModelId());
            productStructureDto.setUnitQuantity(BigDecimal.ONE);
            productStructureDtos.add(productStructureDto);
        return String.format("%s%03d", datePrefix, sequenceNumber);
    }
    private BigDecimal defaultDecimal(BigDecimal value) {
        // 将空数量兜底为0,避免空指针异常
        return value == null ? BigDecimal.ZERO : value;
    }
    private Long resolveTaskId(ProductionProductMainDto dto) {
        // 从入参中解析生产工单ID并校验
        if (dto == null) {
            return null;
        }
        for (ProductStructureDto productStructureDto : productStructureDtos) {
            ProductModel productModel1 = productModelMapper.selectById(productStructureDto.getProductModelId());
            Product product = productMapper.selectById(productModel1.getProductId());
            BigDecimal stockQuantity = stockUtils.getStockQuantity(productModel1.getId()).get("stockQuantity");
            if (!(stockQuantity.compareTo(BigDecimal.ZERO) > 0)) {
                throw new RuntimeException(product.getProductName()+"产品的"+productModel1.getModel() + "的规格库存为0");
            }
            if (stockQuantity.compareTo(productStructureDto.getUnitQuantity().multiply(dto.getQuantity())) < 0) {
                throw new RuntimeException(product.getProductName()+"产品的"+productModel1.getModel() + "的规格库存不足");
            }
            ProductionProductInput productionProductInput = new ProductionProductInput();
            productionProductInput.setProductModelId(productStructureDto.getProductModelId());
            productionProductInput.setQuantity(productStructureDto.getUnitQuantity().multiply(dto.getQuantity()));
            productionProductInput.setProductMainId(productionProductMain.getId());
            productionProductInputMapper.insert(productionProductInput);
            //对应的库存出库
            DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("yyyyMMdd");
            LocalDate now = LocalDate.now();
            ProcurementRecordOut procurementRecordOut1 = procurementRecordOutMapper.selectCode(dateFormat.format(now));
            Long aLong = procurementRecordOut1 == null ? 1L : Long.valueOf(procurementRecordOut1.getCode().split("LS" + dateFormat.format(now))[1]);
            ProcurementRecordOut.ProcurementRecordOutBuilder procurementRecordOut = ProcurementRecordOut.builder()
                    .procurementRecordStorageId(0)
                    .code("LS" + dateFormat.format(now) + String.format("%03d", aLong + 1))
                    .salesLedgerProductId(productionProductMain.getId())//关联报工产出
                    .inboundBatches(aLong.equals(0L) ? "第1批次" : "第" + (aLong + 1) + "批次")
                    .inboundNum(productionProductInput.getQuantity())
                    .type(4)
                    .createBy(user.getNickName())
                    .productModelId(productModel1.getId());
            procurementRecordOutMapper.insert(procurementRecordOut.build());
        }
        /*新增报工产出表*/
        ProductionProductOutput productionProductOutput = new ProductionProductOutput();
        productionProductOutput.setProductMainId(productionProductMain.getId());
        productionProductOutput.setProductModelId(productProcessRouteItem.getProductModelId());
        productionProductOutput.setQuantity(dto.getQuantity() != null ? dto.getQuantity() : BigDecimal.ZERO);
        productionProductOutputMapper.insert(productionProductOutput);
        /*新增质检*/
        //对应的过程检或者出厂检
        List<ProductProcessRouteItem> productProcessRouteItems = productProcessRouteItemMapper.selectList(Wrappers.<ProductProcessRouteItem>lambdaQuery().eq(ProductProcessRouteItem::getProductRouteId, productProcessRouteItem.getProductRouteId()));
        int inspectType = 1;
        String process = productProcess.getName();//工序
        if (productProcessRouteItem.getDragSort()==productProcessRouteItems.size()){
            //最后一道工序生成出厂检
            inspectType = 2;
            process = null;
        }
        Product product = productMapper.selectById(productModel.getProductId());
        QualityInspect qualityInspect = new QualityInspect();
        qualityInspect.setProductId(product.getId());
        qualityInspect.setProductName(product.getProductName());
        qualityInspect.setModel(productModel.getModel());
        qualityInspect.setUnit(productModel.getUnit());
        qualityInspect.setQuantity(dto.getQuantity());
        qualityInspect.setProcess(productProcess.getName());
        qualityInspect.setInspectState(0);
        qualityInspect.setInspectType(inspectType);
        qualityInspect.setProductMainId(productionProductMain.getId());
        qualityInspect.setProductModelId(productModel.getId());
        qualityInspectMapper.insert(qualityInspect);
        List<QualityTestStandard> qualityTestStandard = qualityTestStandardMapper.getQualityTestStandardByProductId(product.getId(), inspectType,process);
        if (qualityTestStandard.size()>0){
            qualityInspect.setTestStandardId(qualityTestStandard.get(0).getId());
            qualityInspectMapper.updateById(qualityInspect);
            qualityTestStandardParamMapper.selectList(Wrappers.<QualityTestStandardParam>lambdaQuery()
                    .eq(QualityTestStandardParam::getTestStandardId,qualityTestStandard.get(0).getId()))//默认获取最新的
                    .forEach(qualityTestStandardParam -> {
                QualityInspectParam param = new QualityInspectParam();
                BeanUtils.copyProperties(qualityTestStandardParam, param);
                param.setId(null);
                param.setInspectId(qualityInspect.getId());
                qualityInspectParamMapper.insert(param);
            });
        }
        /*更新工单和生产订单*/
        ProductWorkOrder productWorkOrder = productWorkOrderMapper.selectById(dto.getWorkOrderId());
        productWorkOrder.setCompleteQuantity(productWorkOrder.getCompleteQuantity().add(dto.getQuantity()));
        if (ObjectUtils.isNull(productWorkOrder.getActualStartTime())){
            productWorkOrder.setActualStartTime(LocalDate.now());//实际开始时间
        }
        if (productWorkOrder.getCompleteQuantity().compareTo(productWorkOrder.getPlanQuantity()) == 0){
            productWorkOrder.setActualEndTime(LocalDate.now());//实际结束时间
        }
        productWorkOrderMapper.updateById(productWorkOrder);
        //生产订单
        ProductOrder productOrder = productOrderMapper.selectById(productWorkOrder.getProductOrderId());
        if (ObjectUtils.isNull(productOrder.getStartTime())){
            productOrder.setStartTime(LocalDateTime.now());//开始时间
        }
        if (productProcessRouteItem.getDragSort()==productProcessRouteItems.size()){
            //如果是最后一道工序报工之后生产订单完成数量+
            productOrder.setCompleteQuantity(productOrder.getCompleteQuantity().add(dto.getQuantity()));
            if (productOrder.getCompleteQuantity().compareTo(productOrder.getQuantity()) == 0){
                productOrder.setEndTime(LocalDateTime.now());//结束时间
            }
        }
        productOrderMapper.updateById(productOrder);
        /*添加生产核算*/
        SalesLedgerProductionAccounting salesLedgerProductionAccounting = SalesLedgerProductionAccounting.builder()
                .salesLedgerWorkId(productionProductMain.getId())
                .salesLedgerSchedulingId(0L)
                .salesLedgerId(productOrder.getSalesLedgerId())
                .salesLedgerProductId(productOrder.getProductModelId())
                .schedulingUserId(user.getUserId())
                .schedulingUserName(user.getNickName())
                .finishedNum(dto.getQuantity() != null ? dto.getQuantity() : BigDecimal.ZERO)
                .workHours(productProcess.getSalaryQuota())
                .process(productProcess.getName())
                .schedulingDate(LocalDate.now())
                .tenantId(dto.getTenantId())
                .build();
        salesLedgerProductionAccountingMapper.insert(salesLedgerProductionAccounting);
        return true;
        return dto.getProductionOperationTaskId();
    }
    @Override
    public Boolean removeProductMain(ProductionProductMainDto dto) {
        ProductionProductMain productionProductMain = productionProductMainMapper.selectById(dto.getId());
        //该报工对应的工艺路线详情
        ProductProcessRouteItem productProcessRouteItem = productProcessRouteItemMapper.selectById(productionProductMain.getProductProcessRouteItemId());
        ProductionProductOutput productionProductOutput = productionProductOutputMapper.selectList(Wrappers.<ProductionProductOutput>lambdaQuery().eq(ProductionProductOutput::getProductMainId, productionProductMain.getId())).get(0);
        /*删除核算*/
        salesLedgerProductionAccountingMapper.delete(
                new LambdaQueryWrapper<SalesLedgerProductionAccounting>()
                        .eq(SalesLedgerProductionAccounting::getSalesLedgerWorkId, productionProductMain.getId())
        );
        /*更新工单和生产订单*/
        ProductWorkOrder productWorkOrder = productWorkOrderMapper.selectById(productionProductMain.getWorkOrderId());
        productWorkOrder.setCompleteQuantity(productWorkOrder.getCompleteQuantity().subtract(productionProductOutput.getQuantity()));
        productWorkOrder.setActualEndTime(null);
        productWorkOrderMapper.updateById(productWorkOrder);
        //判断是否是最后一道工序
        List<ProductProcessRouteItem> productProcessRouteItems = productProcessRouteItemMapper.selectList(Wrappers.<ProductProcessRouteItem>lambdaQuery().eq(ProductProcessRouteItem::getProductRouteId, productProcessRouteItem.getProductRouteId()));
        if (productProcessRouteItem.getDragSort() == productProcessRouteItems.size()){
            ProductOrder productOrder = productOrderMapper.selectById(productWorkOrder.getProductOrderId());
            productOrder.setCompleteQuantity(productOrder.getCompleteQuantity().subtract(productionProductOutput.getQuantity()));
            productOrder.setEndTime(null);
            productOrderMapper.updateById(productOrder);
        }
        /*删除产出*/
        //删除质检
        qualityInspectMapper.selectList(
                new LambdaQueryWrapper<QualityInspect>()
                        .eq(QualityInspect::getProductMainId, productionProductMain.getId())
        ).forEach(q -> {
            qualityInspectParamMapper.delete(
                    new LambdaQueryWrapper<QualityInspectParam>()
                            .eq(QualityInspectParam::getInspectId, q.getId()));
            qualityInspectMapper.deleteById(q.getId());
        });
        // 删除产出记录
        productionProductOutputMapper.delete(new LambdaQueryWrapper<ProductionProductOutput>()
                .eq(ProductionProductOutput::getProductMainId, productionProductMain.getId()));
        /*删除投入*/
        procurementRecordOutMapper.delete(new LambdaQueryWrapper<ProcurementRecordOut>()
                .eq(ProcurementRecordOut::getSalesLedgerProductId, productionProductMain.getId()));
        productionProductInputMapper.delete(new LambdaQueryWrapper<ProductionProductInput>()
                .eq(ProductionProductInput::getProductMainId, productionProductMain.getId()));
        // 删除主表
        productionProductMainMapper.deleteById(productionProductMain.getId());
        return true;
    public ArrayList<Long> listMain(List<Long> idList) {
        // 查询主表ID集合
        return productionProductMainMapper.listMain(idList);
    }
}