package com.rongwei.bscommon.sys.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.rongwei.bscommon.sys.service.ApsBlankOrderService;
import com.rongwei.bscommon.sys.service.ApsReportRecordsService;
import com.rongwei.bscommon.sys.service.ReportCheckService;
import com.rongwei.bsentity.domain.*;
import com.rongwei.bsentity.vo.CancelProcessOperationVo;
import com.rongwei.bsentity.vo.CommonUpdateProductionStatusReq;
import com.rongwei.rwadmincommon.system.vo.SysUserVo;
import com.rongwei.rwcommon.base.BaseDo;
import com.rongwei.rwcommon.base.R;
import com.rongwei.rwcommon.base.exception.CustomException;
import com.rongwei.safecommon.utils.CXCommonUtils;
import com.rongwei.wfentity.sys.RunTaskDo;
import com.rongwei.wfentity.sys.vo.RunTaskVo;
import com.rongwei.wfserver.wfcommon.core.service.impl.WorkFlowServiceImpl;
import com.rongwei.wfserver.wfcommon.sys.service.RunTaskService;
import jodd.util.StringUtil;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.rongwei.safecommon.utils.SaveConstans.JobStatus.JOBSTATUS_TO_BE_END;
import static com.rongwei.safecommon.utils.SaveConstans.SMALL_CIRCLE_PROCESSING_PROCESS_NAME;
import static com.rongwei.safecommon.utils.SaveConstans.WorkInProgressStatus.*;
import static com.rongwei.safecommon.utils.SaveConstans.WorkInProgressType.PASS;
import static com.rongwei.safecommon.utils.SaveConstans.WorkInProgressType.REJECTS;

/**
 * ReportCheckServiceImpl class
 *
 * @author XH
 * @date 2024/03/19
 */
@Service
public class ReportCheckServiceImpl implements ReportCheckService {
    public static final String NO_DELETED = "0";
    public static final String DELETED = "1";
    public static final String YES = "是";
    public static final String NO = "否";
    public static final String PENDING_PLAN_REVIEW = "待计划评审";
    public static final String VERIFIED = "已检验";
    public static final String COMPLETED = "已完工";
    public static final String SEMIFINISHED_PRODUCTS = "半成品备料";
    public static final String WORK_IN_PROCESS = "在制品";
    public static final String MATERIAL_PREPARATION_ORDER = "备料订单";
    private final Logger log = LoggerFactory.getLogger(this.getClass().getName());

    private final ConcurrentHashMap<String, String> locks = new ConcurrentHashMap<>();
    @Autowired
    private ApsReportRecordsService apsReportRecordsService;
    @Autowired
    private ApsReportOutputServiceImpl apsReportOutputService;
    @Autowired
    private ApsProcessOperationServiceImpl apsProcessOperationService;
    @Autowired
    private ApsProductionOrderServiceImpl apsProductionOrderService;
    @Autowired
    private ApsWorkInProgressInventoryServiceImpl apsWorkInProgressInventoryService;
    @Autowired
    private ApsProcessOperationOutMaterServiceImpl apsProcessOperationOutMaterService;
    @Autowired
    private ApsProcessOperationProcessEquServiceImpl apsProcessOperationProcessEquService;
    @Autowired
    private ApsBlankOrderService apsBlankOrderService;
    @Autowired
    private WorkFlowServiceImpl workFlowService;
    @Autowired
    private RunTaskService runTaskService;

    /**
     * 保存检验结果后更新相关信息
     *
     * @param id
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R updateRelevantInformation(String id) {
        log.info("保存检验结果后更新相关信息");
        SysUserVo currentUser = CXCommonUtils.getCurrentUser();
        if(locks.containsKey(id) && !locks.get(id).equals(currentUser.getId())){
            log.error("该作业已被其他人：{}检验",locks.get(id));
            return R.error("该作业已被其他人检验");
        }
        try {
            if (StringUtil.isBlank(id)) {
                log.debug("参数为空");
                return R.error("报工检验参数异常");
            }
            // 获取报工记录
            ApsReportRecordsDo reportRecordsDo = apsReportRecordsService.getById(id);
            if (reportRecordsDo == null) {
                log.error("无法根据ID：{}获取到报工记录", id);
                return R.error("无法获取到报工记录");
            }
            if (VERIFIED.equals(reportRecordsDo.getCheckstatus())) {
                log.error("当前报工记录已检验无法再次检验");
                return R.error("该作业已被其他人检验");
            }
            apsReportRecordsService.update(new LambdaUpdateWrapper<ApsReportRecordsDo>().eq(ApsReportRecordsDo::getId, id)
                    .set(ApsReportRecordsDo::getCheckstatus, VERIFIED));
            // 获取该工序生成的物料输出信息
            List<ApsReportOutputDo> reportOutputDos = apsReportOutputService.list(new LambdaQueryWrapper<ApsReportOutputDo>()
                    .eq(ApsReportOutputDo::getMainid, id)
                    .eq(BaseDo::getDeleted, NO_DELETED));
            if (reportOutputDos.isEmpty()) {
                log.error("无法根据ID：{}获取到输出物料信息", id);
                return R.error("无法获取到输出物料信息");
            }
            // 获取工序作业信息
            String currentProcessOperationId = reportRecordsDo.getProcessoperationid();
            if (StringUtils.isBlank(currentProcessOperationId)) {
                log.error("当前报工记录:{}无法获取到对应的工序作业信息!", id);
                return R.error("无法获取到工序作业信息,请联系系统管理员!");
            }
            // 获取工序作业主表信息
            ApsProcessOperationDo currentProcessOperationMainDo = apsProcessOperationService.getOne(new LambdaQueryWrapper<ApsProcessOperationDo>()
                    .eq(ApsProcessOperationDo::getId, currentProcessOperationId)
                    .eq(BaseDo::getDeleted, NO_DELETED));
            if (currentProcessOperationMainDo == null) {
                log.error("无法根据ID：{}获取到工序作业信息", id);
                return R.error("无法获取到工序作业信息,请联系系统管理员!");
            }
            // 获取工序作业明细信息
            ApsProcessOperationProcessEquDo currentProcessOperationEqu = apsProcessOperationProcessEquService.getById(reportRecordsDo.getProcessequid());
            if (currentProcessOperationEqu == null) {
                log.error("无法找到当前报工记录对应的工序作业信息");
                return R.error("无法找到当前报工记录对应的工序作业信息");
            }
            // 获取工序作业对应的输出物料信息
            List<ApsProcessOperationOutMaterDo> operationOutMaterDoList = apsProcessOperationOutMaterService.list(new LambdaQueryWrapper<ApsProcessOperationOutMaterDo>()
                    .eq(ApsProcessOperationOutMaterDo::getMainid, currentProcessOperationId)
                    .eq(BaseDo::getDeleted, NO_DELETED));
            if (operationOutMaterDoList.isEmpty()) {
                log.error("无法根据工序作业ID:{}获取对应的输出物料信息", currentProcessOperationId);
                return R.error("无法获取到对应的输出物料信息");
            }
            // 工序作业明细ID
            String processOperationEquId = currentProcessOperationEqu.getId();
            // 前道工序作业ID
            String previousprocessid = currentProcessOperationMainDo.getPreviousprocessid();
            // 坯料计划ID
            String blankId = currentProcessOperationMainDo.getBlankid();
            if (StringUtils.isBlank(blankId)) {
                log.error("无法根据工序获取到生产订单信息");
                return R.error("无法获取到生产订单信息");
            }
            // 获取坯料计划
            ApsBlankOrderDo blankOrderDo = apsBlankOrderService.getBaseMapper().selectById(blankId);
            if (blankOrderDo == null) {
                log.error("无法根据ID：{}找到对应的坯料计划信息", blankId);
                return R.error("无法获取到坯料计划信息");
            }
            // 获取订单记录
            String productionOrderId = blankOrderDo.getProductionorderid();
            if (StringUtils.isBlank(productionOrderId)) {
                log.error("坯料计划对应的订单ID为空");
                return R.error("无法通过坯料计划找到订单信息");
            }
            // 获取订单信息
            ApsProductionOrderDo productionOrderDo = apsProductionOrderService.getById(productionOrderId);
            if (productionOrderDo == null) {
                log.error("无法根据ID:{},获取到订单信息", productionOrderId);
                return R.error("无法找到订单信息");
            }
            // 获取当前报工作业对应的在制品信息
            List<ApsWorkInProgressInventoryDo> apsWorkInProgressInventoryDos = apsWorkInProgressInventoryService.list(new LambdaQueryWrapper<ApsWorkInProgressInventoryDo>()
                    .eq(ApsWorkInProgressInventoryDo::getWorkreportrecordid, id).eq(BaseDo::getDeleted, NO_DELETED));
            if (apsWorkInProgressInventoryDos.isEmpty()) {
                log.error("无法获取到当前工序对应的在制品信息");
                return R.error("无法获取在制品信息");
            }
            // 更新当前工序作业和作业明细的已检验卷数

            // 当前工序名为小圈成退 检验卷数为 报工记录的BATCHNUMBER 数量
            if (SMALL_CIRCLE_PROCESSING_PROCESS_NAME.contains(currentProcessOperationMainDo.getProcess())) {
                String batchnumber = reportRecordsDo.getBatchnumber();
                if (StringUtils.isBlank(batchnumber)) {
                    log.error("报工记录的批次号为空");
                    throw new RuntimeException("报工记录的批次号为空");
                }
                // inputBatchNum
                int count = (int) Arrays.stream(batchnumber.split(",")).distinct().filter(StringUtils::isNotBlank).count();
                currentProcessOperationMainDo.setCheckoutroll(currentProcessOperationMainDo.getCheckoutroll() + count);
                currentProcessOperationEqu.setCheckoutroll(currentProcessOperationEqu.getCheckoutroll() + count);
            } else {
                currentProcessOperationMainDo.setCheckoutroll(currentProcessOperationMainDo.getCheckoutroll() + 1);
                currentProcessOperationEqu.setCheckoutroll(currentProcessOperationEqu.getCheckoutroll() + 1);
            }
            currentProcessOperationEqu.setWorkstatus(JOBSTATUS_TO_BE_END);
            currentProcessOperationEqu.setBachmaterialprocess("");
            // 更新 更新该报工检验记录所有输出物料 以及待加工料卷批次号
            batchNumCirculation(apsWorkInProgressInventoryDos, reportOutputDos,
                    operationOutMaterDoList,
                    currentProcessOperationMainDo,
                    currentProcessOperationEqu,
                    reportRecordsDo,
                    productionOrderDo);
//        updateProcessOutputMater(apsWorkInProgressInventoryDos, reportOutputDos,
//                operationOutMaterDoList,
//                currentProcessOperationMainDo,
//                currentProcessOperationEqu);
            // 更新当前工序作业和作业明细的数据
            apsProcessOperationService.updateById(currentProcessOperationMainDo);
            apsProcessOperationProcessEquService.updateById(currentProcessOperationEqu);
            // 更新报工输出物料 检验状态
            apsReportOutputService.update(new LambdaUpdateWrapper<ApsReportOutputDo>()
                    .in(ApsReportOutputDo::getId, reportOutputDos.stream().map(ApsReportOutputDo::getId).collect(Collectors.toList()))
                    .set(ApsReportOutputDo::getCheckstatus, VERIFIED));
            if (reportOutputDos.stream().anyMatch(info -> NO.equals(info.getQualified())) && "已发布计划".equals(productionOrderDo.getAuditstatus())) {
                goBackWf(processOperationEquId, blankId, productionOrderDo);
            }
        } finally {
            log.debug("从map中清空当前报工记录:{}",id);
            locks.remove(id);
        }
        return R.ok();
    }

    /**
     * 撤回
     */
    public void goBackWf(String processOperationEquId, String blankId, ApsProductionOrderDo productionOrderDo) {
        CommonUpdateProductionStatusReq req = new CommonUpdateProductionStatusReq();
        req.setProcessOperationEquId(processOperationEquId);
//        req.setProcessOperationId(currentProcessOperationMainDo.getId());
//        req.setOrderId(productionOrderDo.getId());
//        req.setBlankId(blankId);
        // 更新
        apsProcessOperationProcessEquService.updateProductionStatus(req);
        // 备份
        apsProductionOrderService.saveHistoryInfo(productionOrderDo.getId());
        // modify at [ 2024-08-08 11:09:33 ] by xh: 项目经理因需求原因不在执行如下逻辑直接结束
        if (true) {
            return;
        }
        // 坯料计划撤回发布
        apsBlankOrderService.publishCancel(Arrays.asList(blankId));
        productionOrderDo.setAuditstatus("待排程");
        productionOrderDo.setChangedate(new Date());
        productionOrderDo.setIsback("是");
        productionOrderDo.setChangecomment("");
        apsProductionOrderService.update(new LambdaUpdateWrapper<ApsProductionOrderDo>()
                .eq(ApsProductionOrderDo::getId, productionOrderDo.getId())
                .set(ApsProductionOrderDo::getChangedate, new Date())
                .set(ApsProductionOrderDo::getIsback, "是")
                .set(ApsProductionOrderDo::getChangecomment, null));

        String processinstid = productionOrderDo.getProcessinstid();
        RunTaskDo runTaskDo = runTaskService.getOne(new LambdaQueryWrapper<RunTaskDo>().eq(BaseDo::getDeleted, "0")
                .eq(RunTaskDo::getProcessinstid, processinstid)
                .in(RunTaskDo::getTaskstatus, "10", "20")
                .orderByDesc(BaseDo::getCreatedate), false);
        RunTaskVo runTaskVo = new RunTaskVo();
        BeanUtils.copyProperties(runTaskDo, runTaskVo);
        runTaskVo.setProcessInstId(processinstid);
        runTaskVo.setBusinesstableid(productionOrderDo.getId());
        runTaskVo.setProcesstitle("订单评审");
        runTaskVo.setProcessdefid("order_review");
        runTaskVo.setAuditoperation("10");
        runTaskVo.setTaskstatusname("待处理");
        try {
            workFlowService.runTask(runTaskVo, true);
        } catch (Exception e) {
            log.error("流程更新失败");
            throw new RuntimeException(e);
        }
    }

    @Override
    public R preSaveCheck(String id) {
        log.info("报工检验前校验");
        SysUserVo currentUser = CXCommonUtils.getCurrentUser();
        if (locks.containsKey(id)) {
            log.error("报工检验前校验失败,当前报工记录已被他人:{}检验", locks.get(id));
            return R.error("该作业已被其他人检验");
        }
        locks.put(id, currentUser.getId());
        // 获取报工记录
        ApsReportRecordsDo reportRecordsDo = apsReportRecordsService.getById(id);
        if (reportRecordsDo == null) {
            log.error("无法根据ID：{}获取到报工记录", id);
            return R.error("无法获取到报工记录");
        }
        if (VERIFIED.equals(reportRecordsDo.getCheckstatus())) {
            log.error("当前报工记录已检验无法再次检验");
            return R.error("该作业已被其他人检验");
        }
        log.debug("报工检验前校验成功");
        return R.ok();
    }

    /**
     * 批次号流转相关逻辑
     *
     * @param apsWorkInProgressInventoryDos
     * @param reportOutputDos
     * @param operationOutMaterDoList
     * @param currentProcessOperationDo
     * @param currentProcessOperationEqu
     */
    public void batchNumCirculation(List<ApsWorkInProgressInventoryDo> apsWorkInProgressInventoryDos,
                                    List<ApsReportOutputDo> reportOutputDos,
                                    List<ApsProcessOperationOutMaterDo> operationOutMaterDoList,
                                    ApsProcessOperationDo currentProcessOperationDo,
                                    ApsProcessOperationProcessEquDo currentProcessOperationEqu,
                                    ApsReportRecordsDo reportRecordsDo,
                                    ApsProductionOrderDo productionOrderDo) {
        // 获取当前工序作业对应的下一个工序作业ID
        String nextProcessId = currentProcessOperationDo.getNextprocessid();
        // 获取当前工序作业明细对应的下一个工序作也明细ID
        String nextProcessesIds = currentProcessOperationEqu.getNextprocessesids();
        log.debug("当前作业对应的下一个工序作业：{}以及下一个工序作业明细：{}", nextProcessId, nextProcessesIds);
        if ((StringUtils.isNotBlank(nextProcessId) && StringUtils.isBlank(nextProcessesIds)) ||
                StringUtils.isBlank(nextProcessId) && StringUtils.isNotBlank(nextProcessesIds)) {
            log.error("当前作业的下一个 工序作业id或者工序作业明细id 为空");
            throw new CustomException("工序作业数据异常");
        }
        List<ApsProcessOperationProcessEquDo> nextProcessOperationProcessEquDos = new ArrayList<>();
        List<ApsProcessOperationDo> nextProcessList = new ArrayList<>();
        if (StringUtils.isNotBlank(nextProcessesIds)) {
            // 被流转的工序作业明细
            nextProcessOperationProcessEquDos = apsProcessOperationProcessEquService.list(new LambdaQueryWrapper<ApsProcessOperationProcessEquDo>()
                    .in(ApsProcessOperationProcessEquDo::getId, Arrays.asList(nextProcessesIds.split(",")))
                    .eq(BaseDo::getDeleted, NO_DELETED));
            if (nextProcessOperationProcessEquDos.isEmpty()) {
                log.error("无法根据nextProcessesIds:{},找到工序作业明细信息", nextProcessesIds);
                throw new CustomException("无法找到下一个工序作业明细信息");
            }
            List<String> nextProcessIds = nextProcessOperationProcessEquDos.stream().map(ApsProcessOperationProcessEquDo::getProcessid).distinct().collect(Collectors.toList());
            if (nextProcessIds.isEmpty()) {
                log.error("当前作业明细:{},无法找到对应的工序作业信息", nextProcessesIds);
                throw new CustomException("无法找到工序作业信息");
            }
            log.debug("当前工序作业明细对应的下一个工序作业ID:{}", nextProcessIds);
            // 被流转的工序作业
            nextProcessList = apsProcessOperationService.list(new LambdaQueryWrapper<ApsProcessOperationDo>()
                    .in(ApsProcessOperationDo::getId, nextProcessIds)
                    .eq(BaseDo::getDeleted, NO_DELETED));
            if (nextProcessList.isEmpty()) {
                log.error("根据nextProcessIds:{},找到工序作业明细信息", nextProcessesIds);
                throw new CustomException("无法找到工序作业");
            }
        }
        //当前报工记录对应的输入物料数量
        int inputNum = StringUtils.isBlank(reportRecordsDo.getNeedcheckbatchnumber()) ? 1 : reportRecordsDo.getNeedcheckbatchnumber().split(",").length;

        // 当前报工检验卷数
        int checkNum = reportOutputDos.size();
        // 本次检验不合格卷数
        int unqualifiedNum = (int) reportOutputDos.stream().filter(reportOutputDo -> NO.equals(reportOutputDo.getQualified())).count();
        // 合格卷数
        int qualifiedNum = (int) reportOutputDos.stream().filter(reportOutputDo -> YES.equals(reportOutputDo.getQualified())).count();
        if (unqualifiedNum == 0 && qualifiedNum == 0) {
            log.error("当前报工记录暂无合格和不合格卷数");
            throw new CustomException("请检查报工记录信息");
        }
        log.debug("本次已检验卷数:{},合格:{}，不合格：{}", checkNum, qualifiedNum, unqualifiedNum);
        // 对输出物料按照输出物料进行分组
        Map<String, List<ApsReportOutputDo>> reportOutputMap = reportOutputDos.stream()
                .collect(Collectors.groupingBy(ApsReportOutputDo::getOutputid,
                        LinkedHashMap::new, Collectors.toList()));
        currentProcessOperationDo.setOutputunqualifiednum(currentProcessOperationDo.getOutputunqualifiednum() + unqualifiedNum);
        currentProcessOperationDo.setOutputqualifiednum(currentProcessOperationDo.getOutputqualifiednum() + qualifiedNum);
        log.debug("增加当前工序作业的合格：{}和不合格：{}卷数", qualifiedNum, unqualifiedNum);
        String outputId;
        List<ApsReportOutputDo> currentReportOutputs;
        // 需要流转的输出物料id和对应的批次号
        Map<String, List<String>> toBeProcessedDataMap = new HashMap<>();
        // 流转取消的卷数
        Map<String, Integer> circulationCancellationMap = new HashMap<>();
        for (Map.Entry<String, List<ApsReportOutputDo>> entry : reportOutputMap.entrySet()) {
            outputId = entry.getKey();
            currentReportOutputs = entry.getValue();
            // 当前报工检验卷数
            int currentOutputCheckNum = currentReportOutputs.size();
            // 本次检验不合格卷数
            int currentOutputUnqualifiedNum = (int) currentReportOutputs.stream().filter(reportOutputDo -> NO.equals(reportOutputDo.getQualified())).count();
            // 合格卷数
            int currentOutputQualifiedNum = (int) currentReportOutputs.stream().filter(reportOutputDo -> YES.equals(reportOutputDo.getQualified())).count();
            // 获取工序作业对应的输出物料
            ApsProcessOperationOutMaterDo apsProcessOperationOutMaterDo = operationOutMaterDoList.stream()
                    .filter(materDo -> materDo.getId().equals(entry.getKey()))
                    .findFirst()
                    .orElse(null);
            if (apsProcessOperationOutMaterDo == null) {
                log.error("无法根据输出物料id：{}获取到报工记录对应的输出物料信息", outputId);
                throw new CustomException("无法获取报工记录对应的输出物料信息");
            }
            apsProcessOperationOutMaterDo.setInspectrollnum(apsProcessOperationOutMaterDo.getInspectrollnum() + currentOutputCheckNum);
            apsProcessOperationOutMaterDo.setFailrollnum(apsProcessOperationOutMaterDo.getFailrollnum() + currentOutputUnqualifiedNum);
            apsProcessOperationOutMaterDo.setGoodrollnum(apsProcessOperationOutMaterDo.getGoodrollnum() + currentOutputQualifiedNum);
            log.debug("增加工序作业输出物料的已检验卷数:{},合格卷数:{},不合格卷数:{}", currentOutputCheckNum, currentOutputQualifiedNum, currentOutputUnqualifiedNum);
            // 临时计划备料卷数 =本次报工卷数*该作业的输出物料的计划备料卷数/该作业的计划加工卷数
            int blPlanNum = inputNum * apsProcessOperationOutMaterDo.getPlanstockrollnum() / currentProcessOperationDo.getPlanprocessrall();
            // 临时计划流转卷数 =本次报工卷数*该作业的输出物料的计划流转卷数/该作业的计划加工卷数
            int lzPlanNum = inputNum * apsProcessOperationOutMaterDo.getPlanmoverollnum() / currentProcessOperationDo.getPlanprocessrall();
            // 临时计划成品卷数 =本次报工卷数*该作业的输出物料的计划成品卷数/该作业的计划加工卷数
            int cpPlanNum = inputNum * apsProcessOperationOutMaterDo.getPlanprodrollnum() / currentProcessOperationDo.getPlanprocessrall();
            /**
             * •临时本次备料取消卷数=0
             * •临时本次流转取消卷数=0
             * •临时本次成品取消卷数=0
             */
            int blQxNum = 0;
            int lzQxNum = 0;
            int cpQxNum = 0;
            if (currentOutputUnqualifiedNum > 0) {
                //  如果该作业明细输出物料的计划备料卷数≥待分配不合格卷数，
                if (blPlanNum >= currentOutputUnqualifiedNum) {
                    /**
                     *
                     * 临时计算本次备料取消卷数=待分配不合格卷数；
                     * 临时计算待分配不合格卷数=0
                     */
                    blQxNum = currentOutputUnqualifiedNum;
                    currentOutputUnqualifiedNum = 0;
                } else {
                    /**
                     * 临时计算本次备料取消卷数=该作业明细输出物料的计划备料卷数；
                     * 临时计算待分配不合格卷数= 待分配不合格卷数-该作业明细输出物料的计划备料卷数
                     */
                    blQxNum = blPlanNum;
                    currentOutputUnqualifiedNum = currentOutputUnqualifiedNum - blPlanNum;
                }
            }

            if (currentOutputUnqualifiedNum > 0) {
                //如果该作业明细输出物料的计划流转卷数 ≥ 待分配不合格卷数
                if (lzPlanNum >= currentOutputUnqualifiedNum) {
                    /**
                     * 临时计算本次流转取消卷数 =	待分配不合格卷数
                     * 临时计算待分配不合格卷数 = 0
                     */
                    lzQxNum = currentOutputUnqualifiedNum;
                    currentOutputUnqualifiedNum = 0;
                } else {
                    /**
                     * 临时计算本次流转取消卷数=	该作业明细输出物料的计划流转卷数
                     * 临时计算待分配不合格卷数=待分配不合格卷数-该作业明细输出物料的计划流转卷数
                     */
                    lzQxNum = lzPlanNum;
                    currentOutputUnqualifiedNum = -currentOutputUnqualifiedNum - lzPlanNum;
                }
            }

            if (currentOutputUnqualifiedNum > 0) {
                /**
                 * 临时计算本次成品已取消卷数=待分配不合格卷数；
                 */
                cpQxNum = currentOutputUnqualifiedNum;
            }
            // 流转取消卷数>0
            if (lzQxNum > 0) {
                // 首先获取当前值
                Integer currentCount = circulationCancellationMap.get(outputId);
                // 检查是否为 null
                if (currentCount == null) {
                    currentCount = 0;
                }
                // 更新值
                circulationCancellationMap.put(outputId, currentCount + lzQxNum);
            }

            /**
             * 临时本次备料输出卷数=该作业明细输出物料的计划备料卷数-本次备料取消卷数
             * 临时本次流转输出卷数=该作业明细输出物料的计划流转卷数-本次流转取消卷数，
             * 临时本次成品输出卷数=该作业明细输出物料的计划成品数-本次成品取消卷数
             */
            int blScNum = blPlanNum - blQxNum;
            int lzScNum = lzPlanNum - lzQxNum;
            int cpScNum = cpPlanNum - cpQxNum;
            /**
             * 备料已取消卷数=备料已取消卷数+本次备料取消卷数；
             * 备料已输出卷数=备料已输出卷数+本次备料输出卷数
             * 备料剩余需输出卷数=备料剩余需输出卷数-该作业明细输出物料的计划备料卷数
             */
            log.debug("更新输出物料：{}的 备料取消卷数+{},备料输出卷数+{},计划备料卷数-{}", apsProcessOperationOutMaterDo.getId(), blQxNum, blScNum, blPlanNum);
            calculateAboutRollNum(apsProcessOperationOutMaterDo, ApsProcessOperationOutMaterDo::getStockcancelroll,
                    ApsProcessOperationOutMaterDo::setStockcancelroll, blQxNum, 1);
            calculateAboutRollNum(apsProcessOperationOutMaterDo, ApsProcessOperationOutMaterDo::getStockoutroll,
                    ApsProcessOperationOutMaterDo::setStockoutroll, blScNum, 1);
            calculateAboutRollNum(apsProcessOperationOutMaterDo, ApsProcessOperationOutMaterDo::getStockleaveoutroll,
                    ApsProcessOperationOutMaterDo::setStockleaveoutroll, blPlanNum, -1);
            /**
             * 流转已取消卷数=流转已取消卷数+本次流转取消卷数；
             * 流转已输出卷数=流转已输出卷数+本次流转输出卷数
             * 流转剩余需输出卷数=流转剩余需输出卷数-该作业明细输出物料的计划流转卷数
             */
            log.debug("更新输出物料：{}的 流转取消卷数+{},流转输出卷数+{},计划流转卷数-{}", apsProcessOperationOutMaterDo.getId(), lzQxNum, lzScNum, lzPlanNum);
            calculateAboutRollNum(apsProcessOperationOutMaterDo, ApsProcessOperationOutMaterDo::getMovecancelroll,
                    ApsProcessOperationOutMaterDo::setMovecancelroll, lzQxNum, 1);
            calculateAboutRollNum(apsProcessOperationOutMaterDo, ApsProcessOperationOutMaterDo::getMoveoutroll,
                    ApsProcessOperationOutMaterDo::setMoveoutroll, lzScNum, 1);
            calculateAboutRollNum(apsProcessOperationOutMaterDo, ApsProcessOperationOutMaterDo::getMoveleaveoutroll,
                    ApsProcessOperationOutMaterDo::setMoveleaveoutroll, lzPlanNum, -1);
            /**
             * 成品已取消卷数=成品已取消卷数+本次成品取消卷数；
             * 成品已输出卷数=成品已输出卷数+本次成品输出卷数
             * 成品剩余需输出卷数=成品剩余需输出卷数-该作业明细输出物料的计划成品卷数
             */
            log.debug("更新输出物料：{}的 成品取消卷数+{},成品输出卷数+{},计划成品卷数-{}", apsProcessOperationOutMaterDo.getId(), cpQxNum, cpScNum, cpPlanNum);
            calculateAboutRollNum(apsProcessOperationOutMaterDo, ApsProcessOperationOutMaterDo::getProdcancelroll,
                    ApsProcessOperationOutMaterDo::setProdcancelroll, cpQxNum, 1);
            calculateAboutRollNum(apsProcessOperationOutMaterDo, ApsProcessOperationOutMaterDo::getProdoutroll,
                    ApsProcessOperationOutMaterDo::setProdoutroll, cpScNum, 1);
            calculateAboutRollNum(apsProcessOperationOutMaterDo, ApsProcessOperationOutMaterDo::getProdleaveoutroll,
                    ApsProcessOperationOutMaterDo::setProdleaveoutroll, cpPlanNum, -1);
            // 不合格在制品更新
            if (currentOutputUnqualifiedNum > 0) {
                // 更新备料的在制品状态
                updateWorkInProgressInventoryStatus(0, currentReportOutputs.size(), NO, apsWorkInProgressInventoryDos,
                        currentReportOutputs, WAITING, REJECTS);
            }
            // 合格在制品更新状态
            if (currentOutputQualifiedNum > 0) {
                // 更新备料的在制品状态
                updateWorkInProgressInventoryStatus(0, blScNum, YES, apsWorkInProgressInventoryDos,
                        currentReportOutputs, WAITING, PASS);
                // 更新流转的在制品状态
                List<String> outputNumbers = updateWorkInProgressInventoryStatus(blScNum, lzScNum, YES,
                        apsWorkInProgressInventoryDos, currentReportOutputs, WAIT_WORK, PASS);
                if (!outputNumbers.isEmpty()) {
                    // 添加需要流转的编号信息
                    toBeProcessedDataMap.put(outputId, outputNumbers);
                }
                if (MATERIAL_PREPARATION_ORDER.equals(productionOrderDo.getOrdertype())) {
                    log.debug("订单类型为备料");
                    // 更新成品的在制品状态
                    updateWorkInProgressInventoryStatus(blScNum + lzScNum, cpScNum, YES,
                            apsWorkInProgressInventoryDos, currentReportOutputs, WAITING, PASS);
                } else {
                    // 更新成品的在制品状态
                    updateWorkInProgressInventoryStatus(blScNum + lzScNum, cpScNum, YES,
                            apsWorkInProgressInventoryDos, currentReportOutputs, TO_BE_PUT_INTO_STORAGE, PASS);
                }
            }
        }
        log.info("开始更新在制品和工序作业输出物料信息");
        // 保存在制品
        apsWorkInProgressInventoryService.saveOrUpdateBatch(apsWorkInProgressInventoryDos);
        // 保存工序作业输出物料
        apsProcessOperationOutMaterService.saveOrUpdateBatch(operationOutMaterDoList);
        if (!nextProcessOperationProcessEquDos.isEmpty() && !nextProcessList.isEmpty()) {
            // 批次号流转至下一工序
            batchNumberCirculation(nextProcessOperationProcessEquDos, nextProcessList, toBeProcessedDataMap);
            // 更新流转取消数量
            circulationCancellation(nextProcessOperationProcessEquDos, nextProcessList, circulationCancellationMap);
        }
    }

    /**
     * 流转取消逻辑
     *
     * @param circulationCancellationMap
     */
    public void circulationCancellation(List<ApsProcessOperationProcessEquDo> nextProcessOperationProcessEquDos,
                                        List<ApsProcessOperationDo> nextProcessList,
                                        Map<String, Integer> circulationCancellationMap) {
        if (circulationCancellationMap.isEmpty()) {
            log.debug("暂无需要取消流转的批次号信息");
        }
        log.debug("本次检验不合格的输出物料id：{}", circulationCancellationMap.keySet());
        ApsProcessOperationDo apsProcessOperationDo;
        CancelProcessOperationVo cancelProcessOperationVo;
        for (Map.Entry<String, Integer> entry : circulationCancellationMap.entrySet()) {
            Integer value = entry.getValue();
            for (ApsProcessOperationProcessEquDo apsProcessOperationProcessEquDo : nextProcessOperationProcessEquDos) {
                if (value <= 0) {
                    return;
                }
                apsProcessOperationDo = nextProcessList.stream()
                        .filter(info -> apsProcessOperationProcessEquDo.getProcessid().equals(info.getId()))
                        .findFirst()
                        .orElse(null);
                apsProcessOperationService.getById(apsProcessOperationProcessEquDo.getProcessid());
                if (apsProcessOperationDo == null) {
                    log.error("无法根据id：{}获取工序作业信息", apsProcessOperationProcessEquDo.getProcessid());
                    throw new CustomException("无法获取工序作业信息");
                }
                // 剩余待开工卷数
                int toBeStartedQty = apsProcessOperationProcessEquDo.getPlanprocessrall() - apsProcessOperationProcessEquDo.getStartingroll() - apsProcessOperationProcessEquDo.getCancelroll();
                // 本次取消卷数
                int cancelQty = toBeStartedQty >= value ? value : toBeStartedQty;
                log.debug("工序作业:{}的本次取消卷数:{}", apsProcessOperationProcessEquDo.getId(), cancelQty);
                // 改为取消方法更新作业输入已取消卷数
                apsProcessOperationDo.setInputunqualifiedroll(cancelQty + apsProcessOperationDo.getInputunqualifiedroll());
                //取消和更新作业明细
                apsProcessOperationService.cancelProcessOperationEqu(cancelQty, apsProcessOperationDo, apsProcessOperationProcessEquDo);
                //取消和更新工序作业
                cancelProcessOperationVo = new CancelProcessOperationVo();
                cancelProcessOperationVo.setCraftrouteid(null);
                cancelProcessOperationVo.setCurcancelroll(cancelQty);
                cancelProcessOperationVo.setApsprocessoperationdo(apsProcessOperationDo);
                cancelProcessOperationVo.setSetincancelroll(false);
                cancelProcessOperationVo.setUpdatecuropration(false);
                apsProcessOperationService.cancelProcessOperation(cancelProcessOperationVo);
                value = value - cancelQty;
            }
        }
    }

    /**
     * 批次号流转逻辑
     *
     * @param nextProcessOperationProcessEquDos
     * @param nextProcessList
     * @param toBeProcessedDataMap
     */
    public void batchNumberCirculation(List<ApsProcessOperationProcessEquDo> nextProcessOperationProcessEquDos,
                                       List<ApsProcessOperationDo> nextProcessList,
                                       Map<String, List<String>> toBeProcessedDataMap) {
        // 下一个作业明细不为空
        if (nextProcessOperationProcessEquDos.isEmpty() || toBeProcessedDataMap.isEmpty()) {
            log.debug("无法找到下一个工序作业或者需要流转的卷数为{}", toBeProcessedDataMap.size());
            return;
        }
        log.debug("开始执行批次号流转逻辑!如下批次号需要流转：{}", toBeProcessedDataMap);
        List<ApsProcessOperationDo> updateProcessData = new ArrayList<>();
        List<ApsProcessOperationProcessEquDo> updateProcessEquData = new ArrayList<>();
        ApsProcessOperationDo updateOperationDo;
        ApsProcessOperationProcessEquDo apsProcessOperationProcessEquDo;
        // 批次号流转
        for (Map.Entry<String, List<String>> entryentry : toBeProcessedDataMap.entrySet()) {
            List<ApsProcessOperationDo> apsProcessOperationDos = nextProcessList.stream()
                    .filter(info -> entryentry.getKey().equals(info.getPlaninputid()))
                    .collect(Collectors.toList());
            for (ApsProcessOperationDo apsProcessOperationDo : apsProcessOperationDos) {
                updateOperationDo = new ApsProcessOperationDo();
                // 更新ID
                updateOperationDo.setId(apsProcessOperationDo.getId());
                updateOperationDo.setBachmaterialprocess(strAppendListConvertStr(apsProcessOperationDo.getBachmaterialprocess(), entryentry.getValue()));
                updateOperationDo.setModifydate(new Date());
                updateProcessData.add(updateOperationDo);
                log.debug("将批次号：{}流转给工序作业:{}", entryentry.getValue(), updateOperationDo.getId());
                for (ApsProcessOperationProcessEquDo equ : nextProcessOperationProcessEquDos) {
                    if (updateOperationDo.getId().equals(equ.getProcessid())) {
                        apsProcessOperationProcessEquDo = new ApsProcessOperationProcessEquDo();
                        apsProcessOperationProcessEquDo.setId(equ.getId());
                        apsProcessOperationProcessEquDo.setBachmaterialprocess(strAppendListConvertStr(equ.getBachmaterialprocess(), entryentry.getValue()));
                        apsProcessOperationProcessEquDo.setModifydate(new Date());
                        log.debug("将批次号：{}流转给工序作业明细:{}", entryentry.getValue(), equ.getId());
                        updateProcessEquData.add(apsProcessOperationProcessEquDo);
                    }
                }
            }
        }
        // 保存工序作业明细信息
        if (!updateProcessData.isEmpty()) {
            log.debug("开始更新工序作业信息");
            apsProcessOperationService.saveOrUpdateBatch(updateProcessData);
        }
        if (!updateProcessEquData.isEmpty()) {
            log.debug("开始更新工序作业明细信息");
            apsProcessOperationProcessEquService.saveOrUpdateBatch(updateProcessEquData);
        }
    }

    /**
     * 更新在制品状态
     *
     * @param skip
     * @param limit
     * @param workInProgressInventoryDos
     * @param apsReportOutputDos
     * @param workingProcessStatus
     * @return
     */
    public List<String> updateWorkInProgressInventoryStatus(int skip, int limit,
                                                            String qualifiedType,
                                                            List<ApsWorkInProgressInventoryDo> workInProgressInventoryDos,
                                                            List<ApsReportOutputDo> apsReportOutputDos,
                                                            String workingProcessStatus,
                                                            String workingProcessType) {
        List<String> outputNumberList = apsReportOutputDos.stream()
                .filter(reportOutputDo -> qualifiedType.equals(reportOutputDo.getQualified()))
                .skip(skip)
                .limit(limit)
                .map(ApsReportOutputDo::getOutputnumber)
                .collect(Collectors.toList());
        if (outputNumberList.isEmpty()) {
            return outputNumberList;
        }
        log.debug("批次号：{}在制品状态流转为：{},在制品类型流转为：{}", outputNumberList, workingProcessStatus, workingProcessType);
        workInProgressInventoryDos.stream()
                .filter(inventory -> outputNumberList.contains(inventory.getBatchnumber()))
                .collect(Collectors.toList()).forEach(data -> {
                    if (StringUtils.isNotBlank(workingProcessStatus)) {
                        data.setWorkinprocessstatus(workingProcessStatus);
                    }
                    if (StringUtils.isNotBlank(workingProcessType)) {
                        data.setWorkinprocesstype(workingProcessType);
                    }

                });
        return outputNumberList;
    }


    /**
     * 更新工序作业对应的输出物料信息
     *
     * @param apsWorkInProgressInventoryDos 在制品信息
     * @param reportOutputDos               报工输出物料信息
     * @param operationOutMaterDoList
     */
    @Deprecated
    public void updateProcessOutputMater(List<ApsWorkInProgressInventoryDo> apsWorkInProgressInventoryDos,
                                         List<ApsReportOutputDo> reportOutputDos,
                                         List<ApsProcessOperationOutMaterDo> operationOutMaterDoList,
                                         ApsProcessOperationDo currentProcessOperationMainDo,
                                         ApsProcessOperationProcessEquDo currentProcessOperationEqu) {
        log.info("开始更新工序作业的输出物料和在制品相关信息");
        // 需要作为待加工料卷批次号的批次号
        Map<String, List<String>> toBeProcessedDataMap = new HashMap<>();
        List<String> unqualifiedOutPutIds = new ArrayList<>();
        // 循环报工输出物料信息
        reportOutputDos.forEach(reportOutputDo -> {
            String outputid = reportOutputDo.getOutputid();
            // 获取工序作业对应的输出物料
            ApsProcessOperationOutMaterDo apsProcessOperationOutMaterDo = operationOutMaterDoList.stream()
                    .filter(materDo -> materDo.getId().equals(outputid)).findFirst().orElse(null);
            // 报工记录对应的在制品库存信息
            ApsWorkInProgressInventoryDo apsWorkInProgressInventoryDo = apsWorkInProgressInventoryDos.stream()
                    .filter(inventory -> reportOutputDo.getOutputnumber().equals(inventory.getBatchnumber()))
                    .findFirst().orElse(null);
            if (apsProcessOperationOutMaterDo == null) {
                log.error("报工记录:{}无法找到对应的输出物料信息", reportOutputDo.getId());
                throw new CustomException("无法找到对应的输出物料信息");
            }
            if (apsWorkInProgressInventoryDo == null) {
                log.error("报工记录:{}无法找到对应的在制品信息", reportOutputDo.getId());
                throw new CustomException("无法找到对应的在制品信息");
            }
            log.debug("开始更新在制品: {}和输出物料相关信息: {}", apsWorkInProgressInventoryDo.getId(), apsProcessOperationOutMaterDo.getId());
            // 已检验卷数加+1
            apsProcessOperationOutMaterDo.setInspectrollnum(apsProcessOperationOutMaterDo.getInspectrollnum() + 1);
            log.debug("当前检验输出成品:{}校验合格：{}", outputid, reportOutputDo.getQualified());
            // 不合格
            if (NO.equals(reportOutputDo.getQualified())) {
                currentProcessOperationMainDo.setOutputunqualifiednum(currentProcessOperationMainDo.getOutputunqualifiednum() + 1);
                apsProcessOperationOutMaterDo.setFailrollnum(apsProcessOperationOutMaterDo.getFailrollnum() + 1);
                apsWorkInProgressInventoryDo.setWorkinprocessstatus(WAITING);
                apsWorkInProgressInventoryDo.setWorkinprocesstype(REJECTS);
                unqualifiedOutPutIds.add(outputid);
            } else {
                currentProcessOperationMainDo.setOutputqualifiednum(currentProcessOperationMainDo.getOutputqualifiednum() + 1);
                apsProcessOperationOutMaterDo.setGoodrollnum(apsProcessOperationOutMaterDo.getGoodrollnum() + 1);
                apsWorkInProgressInventoryDo.setWorkinprocesstype(PASS);
                if (apsProcessOperationOutMaterDo.getProdleaveoutroll() > 0) {
                    log.debug("更新状态为待入库");
                    /**
                     * 输出物料批次号对应在制品的在制品状态=待入库，
                     * 成品输出卷数加1
                     * 成品剩余需输出卷数减1
                     */
                    apsWorkInProgressInventoryDo.setWorkinprocessstatus(TO_BE_PUT_INTO_STORAGE);
                    apsProcessOperationOutMaterDo.setProdoutroll(apsProcessOperationOutMaterDo.getProdoutroll() + 1);
                    apsProcessOperationOutMaterDo.setProdleaveoutroll(apsProcessOperationOutMaterDo.getProdleaveoutroll() - 1);
                } else if (apsProcessOperationOutMaterDo.getProdleaveoutroll() == 0 && apsProcessOperationOutMaterDo.getMoveleaveoutroll() > 0) {
                    log.debug("更新状态为待加工");
                    /**
                     * 输出物料批次号对应在制品的在制品状态=待加工
                     * 流转输出卷数加1
                     * 流转剩余需输出卷数减1
                     */
                    apsWorkInProgressInventoryDo.setWorkinprocessstatus(WAIT_WORK);
                    toBeProcessedDataMap.computeIfAbsent(outputid, k -> new ArrayList<>()).add(reportOutputDo.getOutputnumber());
                    apsProcessOperationOutMaterDo.setMoveoutroll(apsProcessOperationOutMaterDo.getMoveoutroll() + 1);
                    apsProcessOperationOutMaterDo.setMoveleaveoutroll(apsProcessOperationOutMaterDo.getMoveleaveoutroll() - 1);
                } else {
                    log.debug("更新状态为备料中");
                    /**
                     * 输出物料批次号对应在制品的在制品状态=备料中
                     * 备料输出卷数加1
                     * 备料剩余需输出卷数减1
                     */
                    apsWorkInProgressInventoryDo.setWorkinprocessstatus(WAITING);
                    apsProcessOperationOutMaterDo.setStockoutroll(apsProcessOperationOutMaterDo.getStockoutroll() + 1);
                    apsProcessOperationOutMaterDo.setStockleaveoutroll(apsProcessOperationOutMaterDo.getStockleaveoutroll() - 1);
                }
            }
        });
        log.debug("需要向下一节点流转的批次号：{}", toBeProcessedDataMap);
        // 当前工序的下一个工序作业信息
        String nextprocessid = currentProcessOperationMainDo.getNextprocessid();
        List<ApsProcessOperationDo> nextProcessOperations;
        log.debug("当前工序的下一个工序：{}", nextprocessid);
        // 当前工序存在下一个工序且 存在代加工的在制品信息时
        if (!toBeProcessedDataMap.isEmpty() && StringUtils.isNotBlank(nextprocessid)) {
            log.debug("更新剩余代报工卷数和待开工卷数");
            nextProcessOperations = apsProcessOperationService.list(new LambdaQueryWrapper<ApsProcessOperationDo>()
                    .in(ApsProcessOperationDo::getId, Arrays.asList(nextprocessid.split(",")))
                    .in(ApsProcessOperationDo::getPlaninputid, toBeProcessedDataMap.keySet())
                    .eq(BaseDo::getDeleted, NO_DELETED));
            if (nextProcessOperations.isEmpty()) {
                log.error("无法获取对应输出物料:{}的工序作业信息", toBeProcessedDataMap.keySet());
                throw new CustomException("未找到下一个工序作业信息!");
            }
            String nextProcessesIds = currentProcessOperationEqu.getNextprocessesids();
            if (StringUtils.isBlank(nextProcessesIds)) {
                log.error("当前工序作业明细需要流转的下一个工序作业明细为空");
                throw new CustomException("无法找到下一个工序作业明细");
            }
            List<ApsProcessOperationProcessEquDo> nextProcessOperationProcessEquDos = apsProcessOperationProcessEquService.list(new LambdaQueryWrapper<ApsProcessOperationProcessEquDo>()
                    .in(ApsProcessOperationProcessEquDo::getId, Arrays.asList(nextProcessesIds.split(",")))
                    .eq(BaseDo::getDeleted, NO_DELETED));
            List<ApsProcessOperationDo> updateProcessData = new ArrayList<>();
            List<ApsProcessOperationProcessEquDo> updateProcessEquData = new ArrayList<>();
            ApsProcessOperationDo apsProcessOperationDo;
            ApsProcessOperationProcessEquDo apsProcessOperationProcessEquDo;
            for (ApsProcessOperationDo data : nextProcessOperations) {
                if (toBeProcessedDataMap.containsKey(data.getPlaninputid())) {
                    String oldNums = data.getBachmaterialprocess();
                    List<String> strings = toBeProcessedDataMap.get(data.getPlaninputid());
                    apsProcessOperationDo = new ApsProcessOperationDo();
                    // 更新ID
                    apsProcessOperationDo.setId(data.getId());
                    apsProcessOperationDo.setBachmaterialprocess(strAppendListConvertStr(oldNums, strings));
                    apsProcessOperationDo.setModifydate(new Date());
                    updateProcessData.add(apsProcessOperationDo);
                    for (ApsProcessOperationProcessEquDo equ : nextProcessOperationProcessEquDos) {
                        if (data.getId().equals(equ.getProcessid())) {
                            apsProcessOperationProcessEquDo = new ApsProcessOperationProcessEquDo();
                            apsProcessOperationProcessEquDo.setId(equ.getId());
                            apsProcessOperationProcessEquDo.setBachmaterialprocess(strAppendListConvertStr(equ.getBachmaterialprocess(), strings));
                            apsProcessOperationProcessEquDo.setModifydate(new Date());
                            updateProcessEquData.add(apsProcessOperationProcessEquDo);
                        }
                    }
                }
            }
            // 保存工序作业明细信息
            if (!updateProcessData.isEmpty()) {
                log.debug("开始更新工序作业信息");
                apsProcessOperationService.saveOrUpdateBatch(updateProcessData);
            }
            if (!updateProcessEquData.isEmpty()) {
                log.debug("开始更新工序作业明细信息");
                apsProcessOperationProcessEquService.saveOrUpdateBatch(updateProcessEquData);
            }
        }
        //本次流转取消卷数
        Map<String, Integer> circulationCancellationMap = new HashMap<>();
        if (!unqualifiedOutPutIds.isEmpty()) {
            Map<String, Long> unqualifiedOutPutMap = unqualifiedOutPutIds.stream().collect(Collectors.groupingBy(outputid -> outputid, Collectors.counting()));
            for (Map.Entry<String, Long> entry : unqualifiedOutPutMap.entrySet()) {
                String key = entry.getKey();
                // 待分配取消卷数
                int toBeAllocated = Math.toIntExact(entry.getValue());
                ApsProcessOperationOutMaterDo apsProcessOperationOutMaterDo = operationOutMaterDoList.stream()
                        .filter(outMater -> key.contains(outMater.getId()))
                        .findFirst().orElse(null);
                if (apsProcessOperationOutMaterDo == null) {
                    log.error("无法获取到正确的工序作业输出物料");
                    throw new CustomException("无法获取到正确的工序作业输出物料");
                }
                int cancellation;
                if (toBeAllocated > 0) {
                    // 该作业明细的计划加工卷数*该作业的输出物料的计划备料卷数/该作业的计划加工卷数
                    int planeProcessedRolls = (int) Math.floor((double)
                            (currentProcessOperationEqu.getPlanprocessrall() * apsProcessOperationOutMaterDo.getPlanstockrollnum()) / currentProcessOperationMainDo.getPlanprocessrall());
                    log.info("开始更新备料相关数据!待分配卷数:{},计划备料卷数：{},输出物料id:{}", toBeAllocated, planeProcessedRolls, key);
                    // 如果该作业明细输出物料的计划备料卷数≥待分配取消卷，则本次备料取消卷数=待分配取消卷 否则 本次备料取消卷数=该作业明细输出物料的计划备料卷数；
                    cancellation = Math.min(planeProcessedRolls, toBeAllocated);
                    /**
                     * 备料已取消卷数=备料已取消卷数+本次备料取消卷数
                     * 备料剩余需输出卷数=备料剩余需输出卷数-本次备料取消卷数
                     * 待分配取消卷=待分配取消卷-本次备料取消卷数
                     */
                    calculateAboutRollNum(apsProcessOperationOutMaterDo, ApsProcessOperationOutMaterDo::getStockcancelroll,
                            ApsProcessOperationOutMaterDo::setStockcancelroll, cancellation, 1);
                    calculateAboutRollNum(apsProcessOperationOutMaterDo, ApsProcessOperationOutMaterDo::getStockleaveoutroll,
                            ApsProcessOperationOutMaterDo::setStockleaveoutroll, cancellation, -1);
                    toBeAllocated = toBeAllocated - cancellation;
                }
                // 流转卷数相关
                if (toBeAllocated > 0) {
                    int planCirculationRolls = (int) Math.floor((double) (currentProcessOperationEqu.getPlanprocessrall() *
                            apsProcessOperationOutMaterDo.getPlanmoverollnum()) / currentProcessOperationMainDo.getPlanprocessrall());
                    log.info("开始更新流转卷数相关数据!待分配卷数:{},计划流转卷数：{},输出物料id:{}", toBeAllocated, planCirculationRolls, key);
                    // 如果该作业明细输出物料的计划流转卷数≥待分配取消卷，则本次流转取消卷数=待分配取消卷； 则本次成品取消卷数=该作业明细输出物料的计划流转卷数；
                    cancellation = Math.min(planCirculationRolls, toBeAllocated);
                    /**
                     * 流转已取消卷数=流转已取消卷数+本次流转取消卷数
                     * 流转剩余需输出卷数=流转剩余需输出卷数-本次流转取消卷数
                     * 更新后道作业取消卷数，参见【更新后道作业取消卷数】
                     * 待分配取消卷=待分配取消卷-本次流转取消卷数
                     */
                    calculateAboutRollNum(apsProcessOperationOutMaterDo, ApsProcessOperationOutMaterDo::getMovecancelroll,
                            ApsProcessOperationOutMaterDo::setMovecancelroll, cancellation, 1);
                    calculateAboutRollNum(apsProcessOperationOutMaterDo, ApsProcessOperationOutMaterDo::getMoveleaveoutroll,
                            ApsProcessOperationOutMaterDo::setMoveleaveoutroll, cancellation, -1);
                    if (circulationCancellationMap.containsKey(key)) {
                        circulationCancellationMap.put(key, circulationCancellationMap.get(key) + cancellation);
                    } else {
                        circulationCancellationMap.put(key, cancellation);
                    }
                    toBeAllocated = toBeAllocated - cancellation;
                }
                // 成品输出卷数
                if (toBeAllocated > 0) {
                    int finishedProductRolls = (int) Math.floor((double) (currentProcessOperationEqu.getPlanprocessrall() *
                            apsProcessOperationOutMaterDo.getPlanprodrollnum()) / currentProcessOperationMainDo.getPlanprocessrall());
                    log.info("开始更新成品输出卷数相关数据!待分配卷数:{},计划成品卷数:{},输出物料id:{}", toBeAllocated, finishedProductRolls, key);
                    // 如果该作业明细输出物料的计划成品卷数≥待分配取消卷，则本次流转取消卷数=待分配取消卷； 则本次成品取消卷数=该作业明细输出物料的计划成品卷数；
                    cancellation = Math.min(finishedProductRolls, toBeAllocated);
                    /**
                     * 成品已取消卷数=成品已取消卷数+本次成品取消卷数
                     * 成品剩余需输出卷数=成品剩余需输出卷数-本次成品取消卷数
                     */
                    calculateAboutRollNum(apsProcessOperationOutMaterDo, ApsProcessOperationOutMaterDo::getProdcancelroll,
                            ApsProcessOperationOutMaterDo::setProdcancelroll, cancellation, 1);
                    calculateAboutRollNum(apsProcessOperationOutMaterDo, ApsProcessOperationOutMaterDo::getProdleaveoutroll,
                            ApsProcessOperationOutMaterDo::setProdleaveoutroll, cancellation, -1);

                }
            }
        }
        log.info("开始更新在制品和工序作业输出物料信息");
        // 保存在制品
        apsWorkInProgressInventoryService.saveOrUpdateBatch(apsWorkInProgressInventoryDos);
        // 保存工序作业输出物料
        apsProcessOperationOutMaterService.saveOrUpdateBatch(operationOutMaterDoList);
        // 更新后道作业取消卷数
        if (StringUtils.isNotBlank(nextprocessid) && !circulationCancellationMap.isEmpty()) {
            ApsProcessOperationDo apsProcessOperationDo;
            CancelProcessOperationVo cancelProcessOperationVo;
            for (Map.Entry<String, Integer> entry : circulationCancellationMap.entrySet()) {
                String outPutId = entry.getKey();
                Integer value = entry.getValue();
                List<ApsProcessOperationProcessEquDo> apsProcessOperationProcessEquDos = apsProcessOperationProcessEquService.getBaseMapper().getDataByInputIdAndId(outPutId, Arrays.asList(currentProcessOperationEqu.getNextprocessesids().split(",")));
                for (ApsProcessOperationProcessEquDo apsProcessOperationProcessEquDo : apsProcessOperationProcessEquDos) {
                    if (value <= 0) {
                        return;
                    }
                    apsProcessOperationDo = apsProcessOperationService.getById(apsProcessOperationProcessEquDo.getProcessid());
                    if (apsProcessOperationDo == null) {
                        log.error("无法根据id：{}获取工序作业信息", apsProcessOperationProcessEquDo.getProcessid());
                        throw new CustomException("无法获取工序作业信息");
                    }
                    // 剩余待开工卷数
                    int toBeStartedQty = apsProcessOperationProcessEquDo.getPlanprocessrall() - apsProcessOperationProcessEquDo.getStartingroll() - apsProcessOperationProcessEquDo.getCancelroll();
                    // 本次取消卷数
                    int cancelQty = toBeStartedQty >= value ? value : toBeStartedQty;
                    log.debug("工序作业:{}的本次取消卷数:{}", apsProcessOperationProcessEquDo.getId(), cancelQty);
                    // 改为取消方法更新作业输入已取消卷数
                    apsProcessOperationDo.setInputunqualifiedroll(cancelQty + apsProcessOperationDo.getInputunqualifiedroll());
                    //取消和更新作业明细
                    apsProcessOperationService.cancelProcessOperationEqu(cancelQty, apsProcessOperationDo, apsProcessOperationProcessEquDo);
                    //取消和更新工序作业
                    cancelProcessOperationVo = new CancelProcessOperationVo();
                    cancelProcessOperationVo.setCraftrouteid(null);
                    cancelProcessOperationVo.setCurcancelroll(cancelQty);
                    cancelProcessOperationVo.setApsprocessoperationdo(apsProcessOperationDo);
                    cancelProcessOperationVo.setSetincancelroll(false);
                    cancelProcessOperationVo.setUpdatecuropration(false);
                    apsProcessOperationService.cancelProcessOperation(cancelProcessOperationVo);
                    value = value - cancelQty;
                }
            }
        }
        log.info("更新工序作业的输出物料和在制品相关信息结束");
    }

    /**
     * 设置值
     *
     * @param materDo
     * @param getPojoData
     * @param toBeAllocatedCancelRollNum
     * @return
     */
    @Deprecated
    public int checkAndSet(ApsProcessOperationOutMaterDo materDo,
                           Function<ApsProcessOperationOutMaterDo, Integer> getPojoData,
                           int toBeAllocatedCancelRollNum) {
        Integer compareA = getPojoData.apply(materDo);
        if (compareA >= toBeAllocatedCancelRollNum) {
            return toBeAllocatedCancelRollNum;
        } else {
            return compareA;
        }
    }

    /**
     * 计算相关卷数
     *
     * @param materDo
     * @param getPojoData
     * @param setPojoData
     * @param changeRollNum
     * @param type          1:加法 -1:减法
     * @return
     */
    public void calculateAboutRollNum(ApsProcessOperationOutMaterDo materDo,
                                      Function<ApsProcessOperationOutMaterDo, Integer> getPojoData,
                                      BiConsumer<ApsProcessOperationOutMaterDo, Integer> setPojoData,
                                      int changeRollNum,
                                      int type) {
        if (1 == type) {
            setPojoData.accept(materDo, getPojoData.apply(materDo) + changeRollNum);
        } else {
            setPojoData.accept(materDo, getPojoData.apply(materDo) - changeRollNum);
        }

    }

    /**
     * str 拼接List 转str
     *
     * @param oldValue
     * @param newValues
     * @return
     */
    public String strAppendListConvertStr(String oldValue, List<String> newValues) {
        if (StringUtils.isBlank(oldValue) && (newValues == null || newValues.isEmpty())) {
            return "";
        }
        if (oldValue == null) {
            oldValue = "";
        }
        List<String> oldValues = new ArrayList<>(Arrays.asList(oldValue.split(",")));
        oldValues.addAll(newValues);
        return oldValues.stream().distinct().filter(StringUtils::isNotBlank).collect(Collectors.joining(","));
    }

    /**
     * 更新工序作业对应的订单信息
     * 如果存在不合格的批次信息 需要更新 订单相关信息
     *
     * @param productionOrderDo
     * @param unqualificationOutPutList
     */
    @Deprecated
    public void currentProductionOrderDoDispose(ApsProductionOrderDo productionOrderDo, List<ApsReportOutputDo> unqualificationOutPutList) {
        // 存在不合格的批次信息
        if (unqualificationOutPutList.isEmpty()) {
            return;
        }
        // TODO:XH add 2024/3/20 发送不合格提醒
    }

    /**
     * 更新当前输出物料的在制品信息
     *
     * @param unqualificationOutPutList     不合格批次信息
     * @param qualifiedOutPutList           合格批次信息
     * @param apsReportRecordsDo            报工记录
     * @param apsWorkInProgressInventoryDos 在制品信息
     * @param nextProcessOperationDo        后道工序信息
     * @return
     */
    @Deprecated
    public void updateArticlesProcessData(List<ApsReportOutputDo> unqualificationOutPutList,
                                          List<ApsReportOutputDo> qualifiedOutPutList,
                                          ApsReportRecordsDo apsReportRecordsDo,
                                          List<ApsWorkInProgressInventoryDo> apsWorkInProgressInventoryDos,
                                          List<ApsProcessOperationDo> nextProcessOperationDo,
                                          ApsProcessOperationDo processOperationDo,
                                          ApsProcessOperationOutMaterDo operationOutMaterDo) {
        if (apsWorkInProgressInventoryDos.isEmpty()) {
            return;
        }
        // 1.如果报工输出物料的是否合格=否，则在制品状态=备料中
        List<String> unqualificationNum = unqualificationOutPutList.stream().map(ApsReportOutputDo::getOutputnumber).collect(Collectors.toList());
        apsWorkInProgressInventoryDos.forEach(info -> {
            if (unqualificationNum.contains(info.getBatchnumber())) {
                info.setWorkinprocessstatus(WAITING);
                info.setWorkinprocesstype(REJECTS);
            }
        });

        /**
         * 1没有后续工序作业 且 报工输出物料的是否合格=是，并且报工输出物料对应工序作业输出物料的输出合格卷数<=计划成品卷数，则从在制品库存删除该在制品
         * 2.1如果报工输出物料的是否合格=是，并且报工输出物料有对应后道工序作业，并且报工输出物料对应工序作业输出物料的输出合格卷数>计划成品卷数+报工输出物料对应所有后道工序作业的计划加工卷数，则在制品状态=备料中；
         * 2.2如果报工输出物料的是否合格=是，并且报工输出物料有对应后道工序作业，并且报工输出物料对应工序作业输出物料的输出合格卷数<=计划成品卷数+报工输出物料对应所有后道工序作业的计划加工卷数，则在制品状态=待加工；
         *
         */
        List<String> qualificationNum = qualifiedOutPutList.stream().map(ApsReportOutputDo::getOutputnumber).collect(Collectors.toList());
        int nextProcessingPlannedNum = nextProcessOperationDo.stream().mapToInt(info -> info.getPlanprocessrall() - (info.getCancelroll() == null ? 0 : info.getCancelroll())).sum();
        // 判断在制品是否存在库存信息
        List<String> collect = apsWorkInProgressInventoryDos.stream().map(ApsWorkInProgressInventoryDo::getBatchnumber).collect(Collectors.toList());
        if (qualificationNum.stream().anyMatch(info -> !collect.contains(info))) {
            throw new RuntimeException("无法获取当前检验产品的库存信息");
        }
        apsWorkInProgressInventoryDos.forEach(info -> {
            // 如果当前合格的批次号在在制品中不存在 不更新在制品库存信息
            if (!qualificationNum.contains(info.getBatchnumber())) {
                return;
            }
            if (nextProcessOperationDo.isEmpty()) {
                // modify at [ 2024-06-28 10:13:44 ] by xh: 根据需求不在删除在制品库存信息改为将在制品变为待入库状态
                if (processOperationDo.getOutputqualifiednum() + 1 <= operationOutMaterDo.getPlanprodrollnum()) {
//                    info.setDeleted(DELETED);
                    info.setWorkinprocesstype(WORK_IN_PROCESS);
                    info.setWorkinprocessstatus(TO_BE_PUT_INTO_STORAGE);
                } else {
                    info.setWorkinprocessstatus(WAIT_WORK);
                    info.setWorkinprocesstype(WORK_IN_PROCESS);
                }
            } else {
                // 输出合格卷数>计划成品卷数+下一个工序的(计划加工卷数-已取消卷数)
                if (processOperationDo.getOutputqualifiednum() + 1 > operationOutMaterDo.getPlanprodrollnum() + nextProcessingPlannedNum) {
                    info.setWorkinprocessstatus(WAITING);
                    info.setWorkinprocesstype(SEMIFINISHED_PRODUCTS);
                } else { // if (processOperationDo.getOutputqualifiednum() + 1 <= operationOutMaterDo.getPlanprodrollnum() + nextProcessingPlannedNum)
                    info.setWorkinprocessstatus(WAIT_WORK);
                    info.setWorkinprocesstype(WORK_IN_PROCESS);
                }
            }
            processOperationDo.setOutputqualifiednum(processOperationDo.getOutputqualifiednum() + 1);
        });
    }
}
