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

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.StopWatch;
import cn.hutool.core.util.ObjectUtil;
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.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.gson.Gson;
import com.rongwei.bscommon.sys.dao.*;
import com.rongwei.bscommon.sys.service.*;
import com.rongwei.bscommon.sys.utils.ApsUtils;
import com.rongwei.bsentity.domain.*;
import com.rongwei.bsentity.vo.*;
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.rwcommon.utils.SecurityUtil;
import com.rongwei.rwcommon.utils.StringUtils;
import com.rongwei.rwcommon.vo.ColumnQueryType;
import com.rongwei.rwcommon.vo.CriteriaQuery;
import com.rongwei.rwcommon.vo.SearchColumnVo;
import com.rongwei.rwcommon.vo.SearchOrderVo;
import com.rongwei.safecommon.utils.CXCommonUtils;
import com.rongwei.safecommon.utils.SaveConstans;
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.math.BigDecimal;
import java.math.RoundingMode;
import java.text.Collator;
import java.time.ZoneId;
import java.util.*;
import java.util.stream.Collectors;

import static com.rongwei.bscommon.sys.service.impl.ApsProductionOrderServiceImpl.ERROR_MSG;
import static com.rongwei.bscommon.sys.utils.ApsUtils.addNewConflictsDesc;
import static com.rongwei.bscommon.sys.utils.ApsUtils.removeConflictsDesc;
import static com.rongwei.safecommon.utils.SaveConstans.ForcedConflictsDescription.EXCEEDING_THE_PROMISED_DELIVERY_DEADLINE;
import static com.rongwei.safecommon.utils.SaveConstans.JobStatus.*;
import static com.rongwei.safecommon.utils.SaveConstans.LockmarkType.LOCKMARK_N;
import static com.rongwei.safecommon.utils.SaveConstans.LockmarkType.LOCKMARK_Y;
import static com.rongwei.safecommon.utils.SaveConstans.ProductionStatus.*;
import static com.rongwei.safecommon.utils.SaveConstans.SchedulstatusTyep.*;

/**
 * <p>
 * 生产订单_坯料计划 服务实现类
 * </p>
 *
 * @author fpy
 * @since 2024-03-01
 */
@Service
public class ApsBlankOrderServiceImpl extends ServiceImpl<ApsBlankOrderDao, ApsBlankOrderDo> implements ApsBlankOrderService {

    public static final String LOG_MSG = "当前坯料计划的排程交货期-起：%tc,承诺交货期-起: %tc,排程交货期-止 : %tc,承诺交货期-止: %tc";
    private static final Logger logger = LoggerFactory.getLogger(ApsBlankOrderServiceImpl.class);
    @Autowired
    private ApsConflictLogServiceImpl apsConflictLogService;
    @Autowired
    private ApsProcessOperationService apsProcessOperationService;
    @Autowired
    private AspCheckItemsService aspCheckItemsService;
    @Autowired
    private ApsBlankOrderDao apsBlankOrderDao;
    @Autowired
    private ApsBlankOrderService apsBlankOrderService;
    @Autowired
    private ApsProcessOperationProcessEquServiceImpl processOperationProcessEquService;
    @Autowired
    private ApsScheduleConfigService apsScheduleConfigService;
    @Autowired
    private ApsProductionOrderService apsProductionOrderService;
    @Autowired
    private ApsProcessOperationBackupDao apsProcessOperationBackupDao;
    @Autowired
    private ApsProcessOperationProcessEquBackupDao apsProcessOperationProcessEquBackupDao;
    @Autowired
    private ApsProductionOrderDao apsProcessOperationDao;
    @Autowired
    private ApsProductDetailService apsProductDetailService;
    @Autowired
    private ApsProductDetailDao apsProductDetailDao;
    @Autowired
    private ApsWorkInProgressInventoryService apsWorkInProgressInventoryService;
    @Autowired
    private ApsProcessOutputProductService apsProcessOutputProductService;
    @Autowired
    private ApsProcessOperationOutMaterService apsProcessOperationOutMaterService;
    @Autowired
    private ApsProcessOperationEquService apsProcessOperationEquService;
    @Autowired
    private ApsProcessOperationProcessEquService apsProcessOperationProcessEquService;
    @Autowired
    private ApsEquipmentCalendarService apsEquipmentCalendarService;
    @Autowired
    private ApsService apsService;
    @Autowired
    private ApsProductionTechnicalRequirementService apsProductionTechnicalRequirementService;
    @Autowired
    private ApsProcessOperationBackupService apsProcessOperationBackupService;
    @Autowired
    private ApsProcessOperationProcessEquBackupService apsProcessOperationProcessEquBackupService;
    @Autowired
    private ReportCheckService reportCheckService;
    @Autowired
    private ApsProcessOperationProcessEquServiceImpl apsProcessOperationProcessEquServiceImpl;
    @Autowired
    private ApsProcessOperationProcessEquDao apsProcessOperationProcessEquDao;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void test() {
        ApsBlankOrderDo a1 = this.getById("19fcf845701746968602e5298f7b9144");
        this.removeById("19fcf845701746968602e5298f7b9144");
        ApsBlankOrderDo a2 = this.getById("19fcf845701746968602e5298f7b9144");
        System.out.println(a1.getMaterialname());
    }

    /**
     * 取消订单排程
     *
     * @param apsProductionOrderIds
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void orderApsCancel(List<String> apsProductionOrderIds) throws Exception {
        if (apsProductionOrderIds.isEmpty()) {
            log.debug("取消排程参数为空");
            return;
        }
        // 获取坯料计划
        List<ApsBlankOrderDo> blankOrderDos = this.list(new LambdaQueryWrapper<ApsBlankOrderDo>()
                .in(ApsBlankOrderDo::getProductionorderid, apsProductionOrderIds));
        if (blankOrderDos.isEmpty()) {
            return;
        }
        // 校验取消的订单中是否存在已加工的工序作业
        List<String> startWorkOrderNo = apsProcessOperationDao.getStartWorkById(apsProductionOrderIds);
        if (!startWorkOrderNo.isEmpty()) {
            throw new CustomException(CollUtil.join(startWorkOrderNo, ",") + " 以上客户所对应的订单存在已开工的坯料计划，不可以取消排程!");
        }
        List<String> blankids = blankOrderDos.stream().map(ApsBlankOrderDo::getId).collect(Collectors.toList());
        //清除排程备份和状态修改
        deleteBackup(blankids, apsProductionOrderIds);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void blankApsCancel(List<String> apsBlankIds) {
        ApsUtils.checkScheduling(null);
        if (ObjectUtil.isEmpty(apsBlankIds)) {
            log.debug("坯料计划排程取消参数为空");
            return;
        }
        // 判断批量计划是否存在已开工的数据
        // 校验取消的订单中是否存在已加工的工序作业
        List<String> startWorkOrderNo = apsProcessOperationDao.getStartWorkByBlankId(apsBlankIds);
        if (!startWorkOrderNo.isEmpty()) {
            throw new CustomException(CollUtil.join(startWorkOrderNo, ",") + " 以上客户所对应的订单存在已开工的坯料计划，不可以取消排程!");
        }
        //获取订单ID
        List<ApsBlankOrderDo> blankOrderDoList = this.list(new QueryWrapper<ApsBlankOrderDo>().lambda().in(ApsBlankOrderDo::getId, apsBlankIds));
        Set<String> orderIds = blankOrderDoList.stream().map(ApsBlankOrderDo::getProductionorderid).collect(Collectors.toSet());
        //清除排程备份
        deleteBackup(apsBlankIds, new ArrayList<>(orderIds));
        for (String apsBlankId : apsBlankIds) {
            removeBatchNumberByBlankId(apsBlankId);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public R publish(List<String> blankIds) {
        ApsUtils.checkScheduling(null);
        if (blankIds == null || blankIds.isEmpty()) {
            log.debug("坯料计划为空");
            return R.error("坯料计划id 必传");
        }
        // 校验是否存在冲突的工序作业明细
        int i = this.baseMapper.checkConflictByBlankId(blankIds);
        if (i != 0) {
            logger.error("所选坯料计划:{}存在冲突", blankIds);
            return R.error("请先解决排程冲突在发布");
        }
        // 如果 订单排程交货期>承诺交货期 抛出异常信息
        List<ApsProductionOrderDo> dataByBlankId = apsProcessOperationDao.getDataByBlankId(blankIds);
        if (!dataByBlankId.isEmpty()) {
            String collect = dataByBlankId.stream()
                    .map(info -> info.getOrderno() + "-" + info.getCustomname())
                    .collect(Collectors.joining(";"));
            logger.error("存在排程交货期大于承诺交货期 的订单：{}", collect);
            return R.error(String.format(ERROR_MSG, collect));
        }
        // 获取工序作业明细信息判断是否存在 冲突或者警告
        List<ApsProcessOperationProcessEquDo> processOperationProcessEquDos = processOperationProcessEquService.list(new LambdaQueryWrapper<ApsProcessOperationProcessEquDo>()
                .in(ApsProcessOperationProcessEquDo::getBlankid, blankIds)
                .eq(BaseDo::getDeleted, "0"));
        if (processOperationProcessEquDos.isEmpty()) {
            log.error("当前坯料计划不存在工序作业明细");
            return R.error("无法根据坯料计划找到工序作业明细");
        }
        // 判断是否存在强制冲突不为空的数据
        boolean b = processOperationProcessEquDos.stream().anyMatch(info -> StringUtils.isNotBlank(info.getConflictdes()));
        if (b) {
            log.error("存在强制冲突无法发布");
            return R.error("请先解决冲突在发布");
        }
        List<String> productionOrderIds = new ArrayList<>();
        blankIds.forEach(blankId -> {
            // 更新工序作业生产状态
            apsProcessOperationService.update(new LambdaUpdateWrapper<ApsProcessOperationDo>()
                    .eq(ApsProcessOperationDo::getBlankid, blankId)
                    .eq(ApsProcessOperationDo::getProcessstatus, TO_BE_PUBLISHED)
                    .set(ApsProcessOperationDo::getProcessstatus, TO_BE_STARTED)
                    .set(ApsProcessOperationDo::getLockmark, LOCKMARK_Y));
            // 工序作业信息
            List<ApsProcessOperationDo> processOperationDos = apsProcessOperationService.list(new LambdaQueryWrapper<ApsProcessOperationDo>()
                    .eq(ApsProcessOperationDo::getBlankid, blankId).orderByDesc(ApsProcessOperationDo::getProcessstatus));
            // 坯料计划排程状态
            String schedulingstatus;
            // 坯料计划生产状态
            String producttStatus;
            if (processOperationDos.get(0).getProcessstatus().equals(TO_BE_PUBLISHED)) {
                schedulingstatus = SCHEDULSTATUS_TORELEASE;
                producttStatus = TO_BE_PUBLISHED;
            } else if (processOperationDos.get(0).getProcessstatus().equals(TO_BE_SCHEDULED)) {
                schedulingstatus = SCHEDULSTATUS_TOSCHEDULE;
                producttStatus = TO_BE_SCHEDULED;
            } else {
                schedulingstatus = SCHEDULSTATUS_HASRELEASE;
                List<String> processStatusList = processOperationDos.stream()
                        .map(ApsProcessOperationDo::getProcessstatus)
                        .distinct()
                        .collect(Collectors.toList());
                if (processOperationDos.get(0).getProcessstatus().equals(TO_BE_STARTED)) {
                    producttStatus = TO_BE_STARTED;
                } else if (processStatusList.size() == 1 && processStatusList.get(0).equals(COMPLETED)) {
                    producttStatus = COMPLETED;
                } else {
                    producttStatus = PROCESSING;
                }
            }
            ApsBlankOrderDo blankOrderDo = this.getById(blankId);
            productionOrderIds.add(blankOrderDo.getProductionorderid());
            this.update(new LambdaUpdateWrapper<ApsBlankOrderDo>().eq(ApsBlankOrderDo::getId, blankId)
                    .set(ApsBlankOrderDo::getProductstatus, producttStatus)
                    .set(ApsBlankOrderDo::getSchedulingstatus, schedulingstatus));
            //存储日志
            apsConflictLogService.recordLog("", "更新坯料计划状态为待排程，坯料计划ID为：" + blankId, "", 1, null);
        });
//        // 更新产品明细的生产状态
//        apsProductDetailDao.publishCancel(blankIds);
        // 更新订单的生产状态
        apsProcessOperationDao.publishCancel(productionOrderIds);
        //更新产品明细的生产状态
        for (String productionOrderId : productionOrderIds) {
            apsProcessOperationProcessEquServiceImpl.updateProductDetail(productionOrderId);
        }
        // 根据坯料ID 删除 备份数据
        ApsUtils.clearBackup(null);
        return R.ok();
    }

    /**
     * 取消发布
     *
     * @param blankIds
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R publishCancel(List<String> blankIds) {
        ApsUtils.checkScheduling(null);
        if (blankIds == null || blankIds.isEmpty()) {
            log.debug("坯料计划为空");
            return R.error("坯料计划id 必传");
        }
        List<String> productionOrderIds = new ArrayList<>();
        // 虚幻坯料计划
        blankIds.forEach(blankId -> {
            // 更新工序作业生产状态
            apsProcessOperationService.update(new LambdaUpdateWrapper<ApsProcessOperationDo>()
                    .eq(ApsProcessOperationDo::getBlankid, blankId)
                    .eq(ApsProcessOperationDo::getProcessstatus, TO_BE_STARTED)
                    .set(ApsProcessOperationDo::getProcessstatus, TO_BE_PUBLISHED));
            // 工序作业信息
            List<ApsProcessOperationDo> processOperationDos = apsProcessOperationService.list(new LambdaQueryWrapper<ApsProcessOperationDo>()
                    .eq(ApsProcessOperationDo::getBlankid, blankId).orderByDesc(ApsProcessOperationDo::getProcessstatus));
            // 坯料计划排程状态
            String schedulingstatus;
            // 坯料计划生产状态
            String producttStatus;
            if (processOperationDos.get(0).getProcessstatus().equals(TO_BE_PUBLISHED)) {
                schedulingstatus = SCHEDULSTATUS_TORELEASE;
                producttStatus = TO_BE_PUBLISHED;
            } else if (processOperationDos.get(0).getProcessstatus().equals(TO_BE_SCHEDULED)) {
                schedulingstatus = SCHEDULSTATUS_TOSCHEDULE;
                producttStatus = TO_BE_SCHEDULED;
            } else {
                schedulingstatus = SCHEDULSTATUS_HASRELEASE;
                List<String> processStatusList = processOperationDos.stream()
                        .map(ApsProcessOperationDo::getProcessstatus)
                        .distinct()
                        .collect(Collectors.toList());
                if (processOperationDos.get(0).getProcessstatus().equals(TO_BE_STARTED)) {
                    producttStatus = TO_BE_STARTED;
                } else if (processStatusList.size() == 1 && processStatusList.get(0).equals(COMPLETED)) {
                    producttStatus = COMPLETED;
                } else {
                    producttStatus = PROCESSING;
                }
            }
            ApsBlankOrderDo blankOrderDo = this.getById(blankId);
            productionOrderIds.add(blankOrderDo.getProductionorderid());
            this.update(new LambdaUpdateWrapper<ApsBlankOrderDo>().eq(ApsBlankOrderDo::getId, blankId)
                    .set(ApsBlankOrderDo::getProductstatus, producttStatus)
                    .set(ApsBlankOrderDo::getSchedulingstatus, schedulingstatus));
        });
//        // 更新产品明细的生产状态
//        apsProductDetailDao.publishCancel(blankIds);
        // 更新订单的生产状态
        apsProcessOperationDao.publishCancel(productionOrderIds);
        for (String productionOrderId : productionOrderIds) {
            apsProcessOperationProcessEquServiceImpl.updateProductDetail(productionOrderId);
        }
//        //退料
//        for (String blankId : blankIds) {
//            removeBatchNumberByBlankId(blankId);
//        }
        // 根据坯料ID 删除 备份数据
        ApsUtils.clearBackup(null);
        return R.ok();
    }

    private R removeBatchNumberByBlankId(String blankId) {
        ApsBlankOrderDo apsBlankOrderDo = apsBlankOrderService.getById(blankId);
        //如果是否备料=是，备料对应在制品的备料计划ID=(空)，在制品状态=备料中
        if (ObjectUtil.isNotEmpty(apsBlankOrderDo.getPreparematerialbatchno()) && apsBlankOrderDo.getPreparematerial().equals("否")) {
            List<String> batchNumberList = Arrays.asList(apsBlankOrderDo.getPreparematerialbatchno().split(","));
            apsWorkInProgressInventoryService.update(new UpdateWrapper<ApsWorkInProgressInventoryDo>().lambda()
                    .set(ApsWorkInProgressInventoryDo::getPlanmaterialid, null)
                    .setSql("WORKINPROCESSSTATUS = IF(PREVIOUSWORKINPROCESSSTATUS = '待加工','备料中',PREVIOUSWORKINPROCESSSTATUS)")
                    .eq(ApsWorkInProgressInventoryDo::getDeleted, "0")
                    .in(ApsWorkInProgressInventoryDo::getBatchnumber, batchNumberList));

            //更新坯料计划该字段为空
            apsBlankOrderService.update(new UpdateWrapper<ApsBlankOrderDo>().lambda()
                    .set(ApsBlankOrderDo::getPreparematerialbatchno, null)
                    .eq(ApsBlankOrderDo::getId, apsBlankOrderDo.getId()));
        }
        return R.ok();
    }

    /*
    插单
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R insertOrder(Map<String, Object> params) throws Exception {
        String blankOrderId = params.get("BLANKORDERID").toString();
        String expecteddays = params.get("EXPECTEDDAYS").toString();

        //撤回发布该作业的坯料计划的排程结果
        publishCancel(Arrays.asList(blankOrderId));

        //重新自动备份，以便插单重排后可以恢复之前的排程结果 备份工序作业及作业明细
        List<ApsProcessOperationDo> apsProcessOperationList = apsProcessOperationService.getBaseMapper().selectList(new QueryWrapper<ApsProcessOperationDo>().lambda().eq(ApsProcessOperationDo::getBlankid, blankOrderId));
        List<String> processIds = apsProcessOperationList.stream().map(ApsProcessOperationDo::getId).collect(Collectors.toList());
        SysUserVo currentUser = CXCommonUtils.getCurrentUser();
        apsProcessOperationBackupDao.processOperationBackup(processIds,
                currentUser == null ? "" : currentUser.getId(),
                currentUser == null ? "" : currentUser.getName());
        apsProcessOperationProcessEquBackupDao.processOperationEquCopy(processIds,
                currentUser == null ? "" : currentUser.getId(),
                currentUser == null ? "" : currentUser.getName());

        // 保存坯料需求的期望交货天数(天)
        boolean bool1 = this.update(new LambdaUpdateWrapper<ApsBlankOrderDo>().eq(ApsBlankOrderDo::getId, blankOrderId)
                .set(ApsBlankOrderDo::getExpecteddays, Integer.parseInt(expecteddays))
                .set(ApsBlankOrderDo::getLockmark, LOCKMARK_N));

        // 解锁该坯料计划所有作业明细，设置是否锁定=否
        boolean bool2 = apsProcessOperationService.update(new LambdaUpdateWrapper<ApsProcessOperationDo>()
                .eq(ApsProcessOperationDo::getBlankid, blankOrderId)
                .set(ApsProcessOperationDo::getLockmark, LOCKMARK_N));

        if (bool1 && bool2) {
            return R.ok();
        } else {
            return R.error();
        }
    }

    /**
     * 获取锁定和解锁的 搜索事件
     *
     * @param lockStartTime
     * @param startSchedulerun
     * @return
     */
    private Date orderLockGetSearChDate(Date lockStartTime, int startSchedulerun, String lockMark) {
        Calendar instance = Calendar.getInstance();
        instance.add(Calendar.HOUR_OF_DAY, startSchedulerun);
        Date time = instance.getTime();
        if (LOCKMARK_Y.equals(lockMark)) {
            return lockStartTime.compareTo(time) > 0 ? lockStartTime : time;
        } else {
            return lockStartTime.compareTo(time) > 0 ? lockStartTime : time;
        }
    }

    /**
     * 锁定、解锁
     *
     * @param orderLockVo
     * @throws Exception
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R orderLock(OrderLockVo orderLockVo) {
        ApsUtils.checkScheduling(null);
        String lockMark = orderLockVo.getLockMark();
        if (StringUtils.isBlank(lockMark)) {
            log.error("锁定状态必填");
            return R.error("锁定状态必填");
        }
        List<String> processIds = orderLockVo.getId();
        processIds = processIds.stream().filter(StringUtils::isNotBlank).distinct().collect(Collectors.toList());
        Date lockStartTime = orderLockVo.getLockStartTime();
        if (processIds.isEmpty() && lockStartTime == null) {
            log.error("工序作业明细ID和解锁时间都为空");
            return R.error("工序作业明细ID和解锁时间都为空");
        }
        // 直接解锁和锁定操作的工序作业信息
        List<ApsProcessOperationDo> apsProcessOperationDos = Collections.emptyList();
        if (!processIds.isEmpty()) {
            // 校验所选择的工序作业生产状态 是否在加工或已完工
            apsProcessOperationDos = new ArrayList<>(apsProcessOperationService.listByIds(processIds));
            //解锁的情况下校验 工序作业对应的生产状态
            if (LOCKMARK_N.equals(lockMark)) {
                boolean b = apsProcessOperationDos.stream().allMatch(info -> COMPLETED.equals(info.getProcessstatus()) ||
                        PROCESSING.equals(info.getProcessstatus()));
                if (b) {
                    return R.error("所选作业都已开工，不可以解锁，不能锁定");
                }
            }
        }

        if (lockStartTime != null) {
            // 获取排程参数设置
            List<ApsScheduleConfigDo> apsConfigs = apsScheduleConfigService.list();
            // 默认最小排程时间
            Integer defaultStartStartSchedulerun = 8;
            if (!apsConfigs.isEmpty()) {
                defaultStartStartSchedulerun = apsConfigs.get(0).getStartschedulerun();
            }
            if (defaultStartStartSchedulerun == null || defaultStartStartSchedulerun <= 0) {
                defaultStartStartSchedulerun = 8;
            }

            Date date = orderLockGetSearChDate(lockStartTime, defaultStartStartSchedulerun, lockMark);
            // 获取所有 待发布 待开工的 工序作业
            apsProcessOperationDos = this.baseMapper.getProcessInfoByLockTime(date, lockMark);
        }
        if (apsProcessOperationDos.isEmpty()) {
            log.debug("暂无需要锁定或者解锁的工序作业");
            return R.ok("暂无需要锁定或者解锁的工序作业");
        }
        if (LOCKMARK_N.equals(lockMark)) {
            return unlock(apsProcessOperationDos);
        }
        return lock(apsProcessOperationDos);
    }

    /**
     * 排程明细锁定、解锁
     *
     * @param orderLockVo
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public R processDetailLock(OrderLockVo orderLockVo) {
        ApsUtils.checkScheduling(null);
        String lockMark = orderLockVo.getLockMark();
        if (StringUtils.isBlank(lockMark)) {
            log.error("锁定状态必填");
            return R.error("锁定状态必填");
        }
        // 作业明细锁定
        if (orderLockVo.getId() != null && orderLockVo.getId().size() > 0) {
            // 查询作业明细的所属作业ID
            List<ApsProcessOperationProcessEquDo> apsProcessOperationProcessEquDos = (List<ApsProcessOperationProcessEquDo>) apsProcessOperationProcessEquService.listByIds(orderLockVo.getId());
            //校验工序作业明细状态
            if (!apsProcessOperationProcessEquDos.isEmpty()) {
                //解锁的情况下校验 工序作业对应的生产状态
                boolean b = apsProcessOperationProcessEquDos.stream().allMatch(info -> JOBSTATUS_TO_BE_STARTING.equals(info.getWorkstatus()) ||
                        JOBSTATUS_TO_BE_END.equals(info.getWorkstatus()));
                if (b) {
                    return R.error("所选作业明细都已开工，不可以解锁，不能锁定");
                }
            }
            apsProcessOperationProcessEquDos = apsProcessOperationProcessEquDos.stream().filter(detail -> JOBSTATUS_TO_BE_START.equals(detail.getWorkstatus())).collect(Collectors.toList());
            List<String> updateEquIds = apsProcessOperationProcessEquDos.stream().map(ApsProcessOperationProcessEquDo::getId).collect(Collectors.toList());
            //  所有作业ID
            List<String> proids = apsProcessOperationProcessEquDos.stream().map(ApsProcessOperationProcessEquDo::getProcessid).collect(Collectors.toList());
            proids = proids.stream().distinct().collect(Collectors.toList());
            apsProcessOperationProcessEquService.update(new LambdaUpdateWrapper<ApsProcessOperationProcessEquDo>()
                    .in(ApsProcessOperationProcessEquDo::getId, updateEquIds)
                    .set(ApsProcessOperationProcessEquDo::getLockmarkdetail, lockMark));
            // 查询存在未锁定作业明细的作业ID
            List<ApsProcessOperationProcessEquDo> popes = apsProcessOperationProcessEquService.list(new LambdaQueryWrapper<ApsProcessOperationProcessEquDo>().in(ApsProcessOperationProcessEquDo::getProcessid, proids)
                    .eq(ApsProcessOperationProcessEquDo::getLockmarkdetail, LOCKMARK_N));
            List<String> notlockdetailproids = popes.stream().map(ApsProcessOperationProcessEquDo::getProcessid).collect(Collectors.toList()).stream().distinct().collect(Collectors.toList());
            String retMsg = "";
            // 锁定
            if (LOCKMARK_Y.equals(lockMark)) {
                List<String> yproids = null;
                if (notlockdetailproids != null && notlockdetailproids.size() > 0) {
                    // 没有未锁定作业明细的作业ID
                    yproids = proids.stream().filter(v -> !notlockdetailproids.contains(v)).collect(Collectors.toList());
                } else {
                    yproids = proids;
                }
                if (yproids != null && yproids.size() > 0) {
                    // 所有作业明细都是锁定状态则锁定作业
                    apsProcessOperationService.update(new LambdaUpdateWrapper<ApsProcessOperationDo>()
                            .in(ApsProcessOperationDo::getId, yproids)
                            .set(ApsProcessOperationDo::getLockmark, LOCKMARK_Y));
                }
                retMsg = "锁定成功";
            }
            // 解锁
            else if (LOCKMARK_N.equals(lockMark)) {
                if (notlockdetailproids != null && notlockdetailproids.size() > 0) {
                    // 存在作业明细是未锁定状态则解锁作业
                    apsProcessOperationService.update(new LambdaUpdateWrapper<ApsProcessOperationDo>()
                            .in(ApsProcessOperationDo::getId, notlockdetailproids)
                            .set(ApsProcessOperationDo::getLockmark, LOCKMARK_N));
                }
                retMsg = "解锁成功";
            }
            return R.ok("200", retMsg, updateEquIds);
        } else {
            Date lockStartTime = orderLockVo.getLockStartTime();
            if (lockStartTime != null) {
                // 获取排程参数设置
                List<ApsScheduleConfigDo> apsConfigs = apsScheduleConfigService.list();
                // 默认最小排程时间
                Integer defaultStartStartSchedulerun = 8;
                if (!apsConfigs.isEmpty()) {
                    defaultStartStartSchedulerun = apsConfigs.get(0).getStartschedulerun();
                }
                if (defaultStartStartSchedulerun == null || defaultStartStartSchedulerun <= 0) {
                    defaultStartStartSchedulerun = 8;
                }
                Date date = orderLockGetSearChDate(lockStartTime, defaultStartStartSchedulerun, lockMark);
                List<ApsProcessOperationProcessEquDo> processDetailInfoByLockTime = this.baseMapper.getProcessDetailInfoByLockTime(date, lockMark);
                if (processDetailInfoByLockTime != null && processDetailInfoByLockTime.size() > 0) {
                    List<String> lpeus = processDetailInfoByLockTime.stream().map(ApsProcessOperationProcessEquDo::getId).collect(Collectors.toList());
                    List<ApsProcessOperationProcessEquDo> equDos = new ArrayList<>(apsProcessOperationProcessEquService.listByIds(lpeus));
                    //校验工序作业明细状态
                    if (!equDos.isEmpty()) {
                        //解锁的情况下校验 工序作业对应的生产状态
                        boolean b = equDos.stream().allMatch(info -> JOBSTATUS_TO_BE_STARTING.equals(info.getWorkstatus()) ||
                                JOBSTATUS_TO_BE_END.equals(info.getWorkstatus()));
                        if (b) {
                            return R.error("所选作业明细都已开工，不可以解锁，不能锁定");
                        }
                    }
                    equDos = equDos.stream().filter(detail -> JOBSTATUS_TO_BE_START.equals(detail.getWorkstatus())).collect(Collectors.toList());
                    List<String> updateEquIds = equDos.stream().map(ApsProcessOperationProcessEquDo::getId).collect(Collectors.toList());
                    apsProcessOperationProcessEquService.update(new LambdaUpdateWrapper<ApsProcessOperationProcessEquDo>()
                            .in(ApsProcessOperationProcessEquDo::getId, updateEquIds)
                            .set(ApsProcessOperationProcessEquDo::getLockmarkdetail, lockMark));
                    String retMsg = "";
                    // 锁定
                    if (LOCKMARK_Y.equals(lockMark)) {
                        retMsg = "锁定成功";
                    } else if (LOCKMARK_N.equals(lockMark)) {
                        retMsg = "解锁成功";
                    }
                    return R.ok("200", retMsg, updateEquIds);
                }
            }
        }
        return R.ok("200", "没有需要操作的数据", orderLockVo.getId());
    }

    /**
     * 解锁 锁定标识  n
     *
     * @param
     * @return
     */
    @Transactional
    public R unlock(List<ApsProcessOperationDo> apsProcessOperationDos) {
        // 获取待发布的工序作业
        List<ApsProcessOperationDo> unlockProcessInfo = apsProcessOperationDos.stream()
                .filter(info -> TO_BE_STARTED.equals(info.getProcessstatus()) || TO_BE_PUBLISHED.equals(info.getProcessstatus()))
                .collect(Collectors.toList());
        // 执行解锁作业的工序作业信息
        List<String> processIds = unlockProcessInfo.stream().map(ApsProcessOperationDo::getId).collect(Collectors.toList());
        // 获取工序作业对应的坯料计划信息(坯料计划生产状态=待开工)
        List<String> blankIds = apsProcessOperationDos.stream().map(ApsProcessOperationDo::getBlankid).collect(Collectors.toList());
        List<ApsBlankOrderDo> apsBlankOrderDos = this.list(new LambdaQueryWrapper<ApsBlankOrderDo>().in(ApsBlankOrderDo::getId, blankIds).eq(ApsBlankOrderDo::getProductstatus, TO_BE_STARTED));
        if (!apsBlankOrderDos.isEmpty()) {
            blankIds = apsBlankOrderDos.stream().map(ApsBlankOrderDo::getId).collect(Collectors.toList());
            // 待加工的工序作业执行撤回操作
            publishCancel(blankIds);
            List<ApsProcessOperationDo> list = apsProcessOperationService.list(new LambdaQueryWrapper<ApsProcessOperationDo>().eq(ApsProcessOperationDo::getBlankid, blankIds));
            processIds.addAll(list.stream().map(ApsProcessOperationDo::getId).collect(Collectors.toList()));
        }
        if (processIds.isEmpty()) {
            log.error("暂无需要执行解锁的工序作业");
            return R.error("暂无需要执行解锁的工序作业信息");
        }
        //待发布更新锁定状态
        // 更新坯料计划的锁定状态 和 生产状态
        apsProcessOperationService.update(new LambdaUpdateWrapper<ApsProcessOperationDo>()
                .in(ApsProcessOperationDo::getId, processIds)
                .set(ApsProcessOperationDo::getLockmark, LOCKMARK_N));
        // 作业明细解锁
        apsProcessOperationProcessEquService.update(new LambdaUpdateWrapper<ApsProcessOperationProcessEquDo>()
                .in(ApsProcessOperationProcessEquDo::getProcessid, processIds)
                .set(ApsProcessOperationProcessEquDo::getLockmarkdetail, LOCKMARK_N));

        // 判断当前工序作业是否存在加工中或已完工的数据
        boolean b = unlockProcessInfo.stream().anyMatch(info -> COMPLETED.equals(info.getProcessstatus()) || PROCESSING.equals(info.getProcessstatus()));
        if (b) {
            return R.ok("200", "解锁成功，部分已开工的作业不能解锁", processIds);
        }
        return R.ok("200", "解锁成功", processIds);
    }

    /**
     * 锁定
     *
     * @param
     * @return
     */
    @Transactional
    public R lock(List<ApsProcessOperationDo> apsProcessOperationDos) {

        List<String> unlockedIds = apsProcessOperationDos.stream()
                .filter(info -> LOCKMARK_N.equals(info.getLockmark()))
                .map(ApsProcessOperationDo::getId)
                .collect(Collectors.toList());
        if (unlockedIds.isEmpty()) {
            return R.ok("200", "锁定成功", unlockedIds);
        }
        // 更新坯料计划的锁定状态 和 生产状态
        apsProcessOperationService.update(new LambdaUpdateWrapper<ApsProcessOperationDo>()
                .in(ApsProcessOperationDo::getId, unlockedIds)
                .set(ApsProcessOperationDo::getLockmark, LOCKMARK_Y));
        // 作业明细锁定
        apsProcessOperationProcessEquService.update(new LambdaUpdateWrapper<ApsProcessOperationProcessEquDo>()
                .in(ApsProcessOperationProcessEquDo::getProcessid, unlockedIds)
                .set(ApsProcessOperationProcessEquDo::getLockmarkdetail, LOCKMARK_Y));
        return R.ok("200", "锁定成功", unlockedIds);
    }

    /**
     * 重新排程
     * 所有待发布的未锁定的订单重新排程
     */
    @Override
    public void apsReScheduling() throws Exception {
        //获取当前用户的所属工厂
        SysUserVo currentUser = CXCommonUtils.getCurrentUser();
        ApsUtils.checkScheduling(currentUser);
        String tenantId = CXCommonUtils.getCurrentUserFactoryId(currentUser);

//        List<ApsBlankOrderVo> notLockOrders = apsBlankOrderDao.getNotLockOrders(tenantId);
        List<ApsBlankOrderVo> notLockOrders = new ArrayList<>();
        blankOrderAps(notLockOrders);
    }

    /**
     * 订单批量排程
     *
     * @param apsPlanVo
     */
    public void apsScheduling(ApsPlanVo apsPlanVo) throws Exception {
        List<ApsBlankOrderVo> apsBlankOrders = new ArrayList<>();
        if (apsPlanVo.getApsProductionOrders() != null) {
            for (ApsProductionOrderDo apsProductionOrder : apsPlanVo.getApsProductionOrders()) {
                List<ApsBlankOrderVo> byOrderId = apsBlankOrderDao.getByOrderId(apsProductionOrder.getId());
                if (byOrderId != null && byOrderId.size() > 0) {
                    apsBlankOrders.addAll(byOrderId);
                }
            }
        }
        if (apsPlanVo.getApsBlankOrderDos() != null && apsPlanVo.getApsBlankOrderDos().size() > 0) {
            apsBlankOrders.addAll(apsPlanVo.getApsBlankOrderDos());
        }
        //去重
        Set<String> blankIds = new HashSet<>();
        //去重后的坯料计划集合
        List<ApsBlankOrderVo> resBlankList = new LinkedList<>();
        for (ApsBlankOrderVo apsBlankOrder : apsBlankOrders) {
            String id = apsBlankOrder.getId();
            if (blankIds.contains(id)) {
                continue;
            }
            blankIds.add(id);
            resBlankList.add(apsBlankOrder);
        }
        blankOrderAps(resBlankList);
        //blankOrderAps(apsBlankOrders);
    }

    /**
     * 生产订单排程
     *
     * @param apsBlankOrders
     */
    @Override
    public void blankOrderAps(List<ApsBlankOrderVo> apsBlankOrders) {
        apsService.apsSchedule(apsBlankOrders, null, null);
    }

    /**
     * 排程结果操作
     *
     * @param productionScheduleRetVo
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void apsAfter(ProductionScheduleRetVo productionScheduleRetVo, List<ApsBlankOrderVo> apsBlankOrders, SysUserVo currentUser) {
        // 先删除所有作业的明细
        List<String> blankids = CollUtil.distinct(productionScheduleRetVo.getProcesses().stream().map(v -> v.getProduceOrder().get(0).getId()).collect(Collectors.toList()));
//        List<String> blankids = apsBlankOrders.stream().map(v -> v.getId()).collect(Collectors.toList());
//        processOperationProcessEquService.remove(new LambdaUpdateWrapper<ApsProcessOperationProcessEquDo>()
//                .in(ApsProcessOperationProcessEquDo::getBlankid, blankids));
        // 查询坯料计划所有已存在的作业明细
        List<ApsProcessOperationProcessEquDo> equDos = processOperationProcessEquService.list(new LambdaQueryWrapper<ApsProcessOperationProcessEquDo>()
                .in(ApsProcessOperationProcessEquDo::getBlankid, blankids));
        // 物理删除作业明细
        String idStrs = CollUtil.join(blankids, "','");
        idStrs = "'" + idStrs + "'";
        apsBlankOrderDao.deleteBlankDetail(idStrs);
        logger.info("作业明细删除结束..: " + blankids);
        // 排程结果保存
        List<ApsProcessOperationProcessEquDo> apsProcessOperationProcessEquDos = new ArrayList<>();
        // 最后节点集合
        List<ProductionProcesses> lastProcess = new ArrayList<>();
        if (productionScheduleRetVo.getProcesses() != null && productionScheduleRetVo.getProcesses().size() > 0) {
            for (ProductionProcesses process : productionScheduleRetVo.getProcesses()) {
                ApsProcessOperationProcessEquDo hasEqu = null;
                if (equDos != null && equDos.size() > 0) {
                    List<ApsProcessOperationProcessEquDo> equid = equDos.stream().filter(v -> v.getId().equals(process.getId())).collect(Collectors.toList());
                    if (equid != null && equid.size() > 0) {
                        hasEqu = equid.get(0);
                    }
                }
                // 非合并生产工序
                if (process.getAluminumCoils() == null) {
                    ApsProcessOperationProcessEquDo apsProcessOperationProcessEqu = new ApsProcessOperationProcessEquDo();
                    apsProcessOperationProcessEqu.setId(process.getId());
//                    apsProcessOperationProcessEqu.setProcessid(process.getId());
                    apsProcessOperationProcessEqu.setProcessid(process.getBsProcessesId().get(0));
                    apsProcessOperationProcessEqu.setBlankid(process.getProduceOrder().get(0).getId());
                    apsProcessOperationProcessEqu.setProcessdeviceid(process.getEquipment().getId());
                    apsProcessOperationProcessEqu.setProcessdevice(process.getEquipment().getEquipmentName());
                    apsProcessOperationProcessEqu.setProcessworkshopid(process.getEquipment().getWorkshopid());
                    apsProcessOperationProcessEqu.setProcessworkshop(process.getEquipment().getWorkshopname());
                    apsProcessOperationProcessEqu.setTenantid(process.getEquipment().getFactoryid());
                    apsProcessOperationProcessEqu.setPlanstartdate(Date.from(process.getStartTime().atZone(ZoneId.systemDefault()).toInstant()));
                    apsProcessOperationProcessEqu.setPlanenddate(Date.from(process.getEndTime().atZone(ZoneId.systemDefault()).toInstant()));
                    if ("小卷成退".equals(process.getProcessType()) || "小卷包装".equals(process.getProcessType())) {
                        apsProcessOperationProcessEqu.setPlanprocessrall(process.getMinThPcNum());
                    } else {
                        apsProcessOperationProcessEqu.setPlanprocessrall(process.getOpeProducePcNum());
                    }
                    apsProcessOperationProcessEqu.setHasconflict(process.getHasConflict());
                    apsProcessOperationProcessEqu.setConflictdes(process.getConflictDes());
                    apsProcessOperationProcessEqu.setSoftconflictdes(process.getSoftconflictdes());
                    apsProcessOperationProcessEqu.setDeleted("0");
                    apsProcessOperationProcessEqu.setUnfinishroll(apsProcessOperationProcessEqu.getPlanprocessrall());
                    apsProcessOperationProcessEqu.setLeavewaitworkroll(apsProcessOperationProcessEqu.getPlanprocessrall());
                    apsProcessOperationProcessEqu.setLeavewaitjobroll(apsProcessOperationProcessEqu.getPlanprocessrall());
                    apsProcessOperationProcessEqu.setPreviousprocessesids(CollUtil.join(process.getPreviousProcessesIds(), ","));
                    apsProcessOperationProcessEqu.setNextprocessesids(CollUtil.join(process.getNextProcessesIds(), ","));
                    apsProcessOperationProcessEqu.setLockmark("y");
                    apsProcessOperationProcessEqu.setLockmarkdetail("y");
                    if (process.getOptionalEquipmentZg() != null && process.getOptionalEquipmentZg().size() > 0) {
                        apsProcessOperationProcessEqu.setRollerid(process.getOptionalEquipmentZg().get(process.getEquipment().getId()));
                    }
                    if (hasEqu != null) {
                        apsProcessOperationProcessEqu.setClosestatus(hasEqu.getClosestatus());
                        apsProcessOperationProcessEqu.setPlanprocessrall(hasEqu.getPlanprocessrall());
                        apsProcessOperationProcessEqu.setStartingroll(hasEqu.getStartingroll());
                        apsProcessOperationProcessEqu.setReportroll(hasEqu.getReportroll());
                        apsProcessOperationProcessEqu.setCheckoutroll(hasEqu.getCheckoutroll());
                        apsProcessOperationProcessEqu.setUnfinishroll(hasEqu.getUnfinishroll());
                        apsProcessOperationProcessEqu.setLeavewaitworkroll(hasEqu.getLeavewaitworkroll());
                        apsProcessOperationProcessEqu.setLeavewaitjobroll(hasEqu.getLeavewaitjobroll());
                        apsProcessOperationProcessEqu.setWorkstatus(hasEqu.getWorkstatus());
                        apsProcessOperationProcessEqu.setBachmaterialprocess(hasEqu.getBachmaterialprocess());
                        apsProcessOperationProcessEqu.setWaitreportid(hasEqu.getWaitreportid());
                        apsProcessOperationProcessEqu.setActualstartdate(hasEqu.getActualstartdate());
                        apsProcessOperationProcessEqu.setActualfinishdate(hasEqu.getActualfinishdate());
                        apsProcessOperationProcessEqu.setBlankbatchnumber(hasEqu.getBlankbatchnumber());
                        apsProcessOperationProcessEqu.setReportbatchnumber(hasEqu.getReportbatchnumber());
                        apsProcessOperationProcessEqu.setDoingbatchnumber(hasEqu.getDoingbatchnumber());
                        apsProcessOperationProcessEqu.setCancelroll(hasEqu.getCancelroll());
                        apsProcessOperationProcessEqu.setSecondroll(hasEqu.getSecondroll());
                        apsProcessOperationProcessEqu.setInputunqualifiednum(hasEqu.getInputunqualifiednum());
                        apsProcessOperationProcessEqu.setInputunqualifiedroll(hasEqu.getInputunqualifiedroll());
                        apsProcessOperationProcessEqu.setCancelrollno(hasEqu.getCancelrollno());
                    }
                    apsProcessOperationProcessEquDos.add(apsProcessOperationProcessEqu);
                }
                // 合并生产工序
                else {
                    // 保存到明细表
                    Map<String, List<AluminumCoil>> bsAl = new HashMap<>();
                    for (AluminumCoil aluminumCoil : process.getAluminumCoils()) {
                        if (bsAl.containsKey(aluminumCoil.getBsId())) {
                            bsAl.get(aluminumCoil.getBsId()).add(aluminumCoil);
                        } else {
                            List<AluminumCoil> coils = new ArrayList<>();
                            coils.add(aluminumCoil);
                            bsAl.put(aluminumCoil.getBsId(), coils);
                        }
                    }
                    bsAl.forEach((k, v) -> {
                        ApsProcessOperationProcessEquDo apsProcessOperationProcessEqu = new ApsProcessOperationProcessEquDo();
                        apsProcessOperationProcessEqu.setId(SecurityUtil.getUUID());
                        apsProcessOperationProcessEqu.setProcessid(k);
                        apsProcessOperationProcessEqu.setBlankid(v.get(0).getBlankid());
                        apsProcessOperationProcessEqu.setProcessdeviceid(process.getEquipment().getId());
                        apsProcessOperationProcessEqu.setProcessdevice(process.getEquipment().getEquipmentName());
                        apsProcessOperationProcessEqu.setPlanstartdate(Date.from(process.getStartTime().atZone(ZoneId.systemDefault()).toInstant()));
                        apsProcessOperationProcessEqu.setPlanenddate(Date.from(process.getEndTime().atZone(ZoneId.systemDefault()).toInstant()));
                        apsProcessOperationProcessEqu.setPlanprocessrall(v.size());
                        apsProcessOperationProcessEqu.setProcessworkshopid(process.getEquipment().getWorkshopid());
                        apsProcessOperationProcessEqu.setProcessworkshop(process.getEquipment().getWorkshopname());
                        apsProcessOperationProcessEqu.setTenantid(process.getEquipment().getFactoryid());
                        apsProcessOperationProcessEqu.setHasconflict(process.getHasConflict());
                        apsProcessOperationProcessEqu.setConflictdes(process.getConflictDes());
                        apsProcessOperationProcessEqu.setSoftconflictdes(process.getSoftconflictdes());
                        apsProcessOperationProcessEqu.setDeleted("0");
                        apsProcessOperationProcessEqu.setUnfinishroll(v.size());
                        apsProcessOperationProcessEquDos.add(apsProcessOperationProcessEqu);
                    });
                }

                // 是否为最后一个节点
                if (process.getNextProcessesIds() == null || process.getNextProcessesIds().size() == 0) {
                    lastProcess.add(process);
                }
            }
        }
        if (apsProcessOperationProcessEquDos != null && apsProcessOperationProcessEquDos.size() > 0) {
            // 再保存新数据
            processOperationProcessEquService.saveBatch(apsProcessOperationProcessEquDos);
            logger.info("作业明细保存结束..: " + blankids);
            // 回写订单排程交货期
            Map<String, Date> orderDeliveryTime = new HashMap<>();
            // 查询所有坯料计划信息
            List<ApsBlankOrderDo> apsBlankOrderDos = (List<ApsBlankOrderDo>) apsBlankOrderService.listByIds(blankids);
            // 坯料计划ID集合
            List<String> blankKeys = new ArrayList<>();
            List<ApsBlankOrderDo> blankIds = new ArrayList<>();
            for (ProductionProcesses process : lastProcess) {
                List<ProduceOrder> produceOrder = process.getProduceOrder();
                if (produceOrder != null && produceOrder.size() > 0) {
                    for (ProduceOrder order : produceOrder) {
                        // 坯料计划信息
                        List<ApsBlankOrderDo> orders = apsBlankOrderDos.stream().filter(v -> v.getId().equals(order.getId())).collect(Collectors.toList());
                        ApsBlankOrderDo nowOrder = null;
                        if (orders != null && orders.size() > 0) {
                            nowOrder = orders.get(0);
                        }

                        Date endDate = Date.from(process.getEndTime().atZone(ZoneId.systemDefault()).toInstant());
                        List<ApsBlankOrderDo> hasblanks = blankIds.stream().filter(v -> v.getId().equals(order.getId())).collect(Collectors.toList());
                        if (hasblanks != null && hasblanks.size() > 0) {
                            ApsBlankOrderDo blankOrderDo = hasblanks.get(0);
                            if (blankOrderDo.getScheduledatestart().compareTo(endDate) > 0) {
                                blankOrderDo.setScheduledatestart(endDate);
                                if (nowOrder != null && nowOrder.getPromisedatestart() == null) {
                                    blankOrderDo.setPromisedatestart(DateUtil.offsetDay(endDate, 2));
                                }
                            }
                            if (blankOrderDo.getScheduledateend().compareTo(endDate) < 0) {
                                blankOrderDo.setScheduledateend(endDate);
                                if (nowOrder != null && nowOrder.getPromisedateend() == null) {
                                    blankOrderDo.setPromisedateend(DateUtil.offsetDay(endDate, 2));
                                }
                            }
                        } else {
                            ApsBlankOrderDo blankOrder = this.getById(order.getId());
                            ApsBlankOrderDo updateBlank = new ApsBlankOrderDo();
                            updateBlank.setId(order.getId());
                            if (StringUtils.isBlank(nowOrder.getSchedulingstatus()) || nowOrder.getSchedulingstatus().equals(TO_BE_SCHEDULED)) {
                                updateBlank.setSchedulingstatus(TO_BE_PUBLISHED);
                            }
                            updateBlank.setScheduledatestart(endDate);
                            updateBlank.setScheduledateend(endDate);
                            if (nowOrder != null && nowOrder.getPromisedatestart() == null) {
                                updateBlank.setPromisedatestart(DateUtil.offsetDay(endDate, 2));
                            }
                            if (nowOrder != null && nowOrder.getPromisedateend() == null) {
                                updateBlank.setPromisedateend(DateUtil.offsetDay(endDate, 2));
                            }
                            // 自动加锁坯料计划
                            updateBlank.setLockmark("y");
                            blankIds.add(updateBlank);
                            blankKeys.add(updateBlank.getId());
                            if (orderDeliveryTime.containsKey(blankOrder.getProductionorderid())) {
                                // 对比取最大值
                                if (endDate.compareTo(orderDeliveryTime.get(blankOrder.getProductionorderid())) > 0) {
                                    orderDeliveryTime.put(blankOrder.getProductionorderid(), endDate);
                                }
                            } else {
                                orderDeliveryTime.put(blankOrder.getProductionorderid(), endDate);
                            }
                        }
                    }
                }
            }

            // 更新订单的排程交货期、排程状态
            if (orderDeliveryTime.size() > 0) {
                orderDeliveryTime.forEach((k, v) -> {
                    ApsProductionOrderDo productionOrderDo = apsProductionOrderService.getById(k);
                    ApsProductionOrderDo apsProductionOrderDo = new ApsProductionOrderDo();
                    apsProductionOrderDo.setId(k);
                    apsProductionOrderDo.setScheduledeliverydate(v);
                    if (productionOrderDo.getDeliverytime() != null && productionOrderDo.getDeliverytime() > 0 && v != null) {
                        apsProductionOrderDo.setScheduledeliverydate(DateUtil.offsetHour(v, productionOrderDo.getDeliverytime()));
                    }
                    // 排程状态更新为待发布
//                    apsProductionOrderDo.setSchedulstatus(SCHEDULSTATUS_TORELEASE);
                    apsProductionOrderService.updateById(apsProductionOrderDo);
                });
            }
            // 坯料计划状态更新
            if (blankIds != null && blankIds.size() > 0) {
                this.updateBatchById(blankIds);
            }

            //更新生产状态为“待发布”（20）
            List<ApsBlankOrderVo> toupstatus = apsBlankOrders.stream().filter(v ->
                            blankids.contains(v.getId()) && (StringUtils.isBlank(v.getIfcp()) || "N".equals(v.getIfcp())))
                    .collect(Collectors.toList());
            updateProductStatusByOrderIds(toupstatus);
            // 自动加锁坯料计划下的工序作业
            apsProcessOperationService.update(new LambdaUpdateWrapper<ApsProcessOperationDo>().in(ApsProcessOperationDo::getBlankid, blankKeys).set(ApsProcessOperationDo::getLockmark, "y"));
            // 自动加锁坯料计划下的排程作业明细
//            apsProcessOperationProcessEquService.update(new LambdaUpdateWrapper<ApsProcessOperationProcessEquDo>().in(ApsProcessOperationProcessEquDo::getBlankid, blankKeys).set(ApsProcessOperationProcessEquDo::getLockmark, "y"));
        }
        logger.info("作业加锁结束..: " + blankids);
        backupProcessOperation(apsProcessOperationProcessEquDos, productionScheduleRetVo.getProcesses(), currentUser);
    }

    /**
     * 工序作业备份
     *
     * @param apsProcessOperationProcessEquDos
     * @param processes
     */
    private void backupProcessOperation(List<ApsProcessOperationProcessEquDo> apsProcessOperationProcessEquDos,
                                        List<ProductionProcesses> processes, SysUserVo currentUser) {


        // 工序作业明细ID
        List<String> apsProcessOperationIds = processes.stream().map(ProductionProcesses::getBsProcessesId)
                .flatMap(List::stream)
                .distinct()
                .collect(Collectors.toList());
        if (currentUser == null) {
            currentUser = CXCommonUtils.getCurrentUser();
        }
        //删除备份
        ApsUtils.clearBackup(currentUser);

        /*****************************开始工序作业备份********************************/
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        String currentUserId = currentUser == null ? null : currentUser.getId();
        String currentUserName = currentUser == null ? null : currentUser.getName();
        List<ApsProcessOperationBackupDo> saveBackUpDo = new ArrayList<>();
        ApsProcessOperationBackupDo backupDo;
        Date currentDate = new Date();
        for (ApsProcessOperationDo apsProcessOperationDo : apsProcessOperationService.list(new LambdaQueryWrapper<ApsProcessOperationDo>()
                .in(ApsProcessOperationDo::getId, apsProcessOperationIds))) {
            backupDo = new ApsProcessOperationBackupDo();
            BeanUtils.copyProperties(apsProcessOperationDo, backupDo);
            backupDo.setBackupuser(currentUserName);
            backupDo.setBackupuserid(currentUserId);
            backupDo.setBackupdate(currentDate);
            saveBackUpDo.add(backupDo);
        }
        apsProcessOperationBackupService.saveBatch(saveBackUpDo, 500);
        stopWatch.stop();
        log.error("工序作业备份时长: " + stopWatch.getTotalTimeSeconds());

        /*****************************开始工序作业明细备份********************************/
        StopWatch stopWatch1 = new StopWatch();
        stopWatch1.start();

        List<ApsProcessOperationProcessEquBackupDo> equBackupDos = new ArrayList<>();
        ApsProcessOperationProcessEquBackupDo equBackupDo;

        for (ApsProcessOperationProcessEquDo apsProcessOperationProcessEquDo : apsProcessOperationProcessEquDos) {
            equBackupDo = new ApsProcessOperationProcessEquBackupDo();
            BeanUtils.copyProperties(apsProcessOperationProcessEquDo, equBackupDo);
            equBackupDo.setBackupuser(currentUserName);
            equBackupDo.setBackupuserid(currentUserId);
            equBackupDo.setBackupdate(currentDate);
            equBackupDos.add(equBackupDo);
        }
        apsProcessOperationProcessEquBackupService.saveBatch(equBackupDos, 600);
        stopWatch1.stop();
        log.error("工序作业明细备份时长: {}" + stopWatch1.getTotalTimeSeconds());
    }

    /**
     * 原始订单表数据转换APS平台模型数据
     *
     * @param apsBlankOrders
     * @return
     */
    @Override
    public ProductionScheduleVo apsPlanModelSet(List<ApsBlankOrderVo> apsBlankOrders) {
        // 待排程坯料计划ID集合
        List<String> blankids = apsBlankOrders.stream().map(v -> v.getId()).collect(Collectors.toList());
        ProductionScheduleVo productionScheduleVo = new ProductionScheduleVo();
        productionScheduleVo.setProductionScheduleId("批量排程");
        List<ApsScheduleConfigDo> apsConfigs = apsScheduleConfigService.list();
        ApsScheduleConfigDo apsScheduleConfig = null;
        // 松散度
        BigDecimal looseness = null;
        if (apsConfigs != null && apsConfigs.size() > 0) {
            apsScheduleConfig = apsConfigs.get(0);
            looseness = apsScheduleConfig.getLooseness();
            productionScheduleVo.setPlanSeconds(apsScheduleConfig.getScheduleruntime());
            Map<String, Integer> roamTime = new HashMap<>();
            Integer workshopin = new BigDecimal(apsScheduleConfig.getWorkshopin()).multiply((looseness.divide(new BigDecimal("100"))).add(new BigDecimal("1")))
                    .setScale(0, RoundingMode.CEILING).intValue();
            Integer workshopcross = new BigDecimal(apsScheduleConfig.getWorkshopcross()).multiply((looseness.divide(new BigDecimal("100"))).add(new BigDecimal("1")))
                    .setScale(0, RoundingMode.CEILING).intValue();
            roamTime.put("WORKSHOP_IN", workshopin);
            roamTime.put("WORKSHOP_CROSS", workshopcross);
            productionScheduleVo.setRoamTime(roamTime);
            // 排程计划开始时间
            int a = 8;
            if (apsScheduleConfig.getStartschedulerun() != null) {
                a = apsScheduleConfig.getStartschedulerun();
            }
            if (apsScheduleConfig.getApsplanstartdate() != null) {
                productionScheduleVo.setApsPlanStartDate(apsScheduleConfig.getApsplanstartdate());
            } else {
                productionScheduleVo.setApsPlanStartDate(DateUtil.date().offset(DateField.HOUR, a));
            }
            productionScheduleVo.setWashingtime(apsScheduleConfig.getWashingtime());
            productionScheduleVo.setStandingtime(apsScheduleConfig.getStandingtime());
        } else {
            throw new CustomException("排程参数缺失");
        }
        // 洗炉合金配置数据设置
        List<WashingMetal> washingMetalConfig = apsBlankOrderDao.getWashingMetalConfig();
        productionScheduleVo.setClosealloynames(washingMetalConfig);

        // 所有设备
        List<AspCheckItemsDo> eqs = aspCheckItemsService.list(new LambdaQueryWrapper<>());
        Map<String, AspCheckItemsDo> eqMaps = new HashMap<>();
        for (AspCheckItemsDo eq : eqs) {
            eqMaps.put(eq.getId(), eq);
        }
        // 排程计划设备集合
        List<Equipment> equipmentList = new ArrayList<>();
        // 排程计划工序任务集合
        List<ProductionProcesses> processesList = new ArrayList<>();
        // 坯料计划ID和所属工序作业ID对应关系
        Map<String, List<String>> blankProcessIds = new HashMap<>();
        // 待合并生产的订单工序
        List<ApsProcessOperationVo> processOperationMs = new ArrayList<>();
        if (apsBlankOrders != null && apsBlankOrders.size() > 0) {
            for (ApsBlankOrderVo apsBlankOrderVo : apsBlankOrders) {
                // 父子ID对应关系处理
                List<String> processIdss = new ArrayList<>();
                // 坯料计划订单
                ProduceOrder produceOrder = new ProduceOrder(apsBlankOrderVo.getId(), apsBlankOrderVo.getMaterialname(), DateUtil.offsetHour(apsBlankOrderVo.getDeliverydate(), -apsBlankOrderVo.getDeliverytime()));
                // 获取所有订单工序
                List<ApsProcessOperationDo> operationDos = apsProcessOperationService.list(new LambdaQueryWrapper<ApsProcessOperationDo>()
                        .eq(ApsProcessOperationDo::getBlankid, apsBlankOrderVo.getId()));
                // 锁定的合并工序缓存
                Map<String, ProductionProcesses> lockHbPp = new HashMap<>();
                // 转换工序模型数据
                for (ApsProcessOperationDo operationDo : operationDos) {
                    processIdss.add(operationDo.getId());
                    ProductionProcesses processes = new ProductionProcesses();
                    // 工序所属订单
                    processes.setProduceOrder(Arrays.asList(new ProduceOrder[]{produceOrder}));
                    // 判断是否合并生产工序,加工方式是“合并加工”
                    String productprocess = operationDo.getProcessway();
                    // 设备列表
                    List<String> optionalEquipments = new ArrayList<>();
                    if (StringUtils.isNotBlank(operationDo.getCanchoosedeviceid())) {
                        String[] sbIds = operationDo.getCanchoosedeviceid().split(",");
                        for (String equIds : sbIds) {
                            AspCheckItemsDo checkItemsD = eqMaps.get(equIds);
                            if (checkItemsD != null) {
                                optionalEquipments.add(equIds);
                                List<Equipment> eqpts = equipmentList.stream().filter(v -> v.getId().equals(equIds)).collect(Collectors.toList());
                                // 设备集合里不存在时新增
                                if (eqpts == null || eqpts.size() == 0) {
                                    Equipment equipment = new Equipment();
                                    equipment.setBsEquipmentId(equIds);
                                    equipment.setId(equIds);
                                    equipment.setEquipmentName(checkItemsD.getCheckitemname());
                                    equipment.setWorkshopid(checkItemsD.getUsedeptid());
                                    equipment.setWorkshopname(checkItemsD.getUsedeptname());
                                    equipment.setFactoryid(checkItemsD.getTenantid());
                                    equipment.setFactory(checkItemsD.getOwnedfactory());
                                    equipment.setEquassociated(checkItemsD.getEquassociated());
                                    // 查询设备锁定的已排程的时间段
                                    List<ApsProcessOperationProcessEquDo> processEqus = processOperationProcessEquService.list(new LambdaQueryWrapper<ApsProcessOperationProcessEquDo>()
                                            .eq(ApsProcessOperationProcessEquDo::getProcessdeviceid, equIds)
                                            .notIn(ApsProcessOperationProcessEquDo::getBlankid, blankids)
                                            .ge(ApsProcessOperationProcessEquDo::getPlanstartdate, productionScheduleVo.getApsPlanStartDate())
                                            .orderByAsc(ApsProcessOperationProcessEquDo::getPlanstartdate)
                                    );
                                    // 查询当前设备最后一个生产任务、连续生产需要考虑使用
                                    List<ApsProcessOperationProcessEquDo> lastpros = processOperationProcessEquService.list(new LambdaQueryWrapper<ApsProcessOperationProcessEquDo>()
                                            .eq(ApsProcessOperationProcessEquDo::getProcessdeviceid, equIds)
                                            .notIn(ApsProcessOperationProcessEquDo::getBlankid, blankids)
                                            .gt(ApsProcessOperationProcessEquDo::getPlanstartdate, productionScheduleVo.getApsPlanStartDate())
                                            .orderByDesc(ApsProcessOperationProcessEquDo::getPlanstartdate)
                                    );
                                    if (lastpros != null && lastpros.size() > 0) {
                                        ApsProcessOperationProcessEquDo lastpro = lastpros.get(0);
                                        ApsProcessOperationDo lastop = apsProcessOperationService.getById(lastpro.getProcessid());
                                        equipment.setLastProcessType(lastop.getProcess());
                                        equipment.setLastProcessEndTime(lastpro.getPlanenddate());
                                        equipment.setLastProcessCutfinishmin(lastop.getCutfinishmin() == null ? null : lastop.getCutfinishmin().intValue());
                                        // 合金+输入物料+宽度
                                        equipment.setLastSeriesProduceMark(lastop.getMetal() + "^_^" + lastop.getPlaninput() + "^_^" + lastop.getProwidth());
                                        equipment.setLastProcessCutfinishmin(lastop.getCutfinishmin() == null ? null : lastop.getCutfinishmin().intValue());
                                    }
                                    // 查询相关工序作业
                                    Set<String> processIds = new HashSet<>();
                                    if (processEqus != null && processEqus.size() > 0) {
                                        for (ApsProcessOperationProcessEquDo equs : processEqus) {
                                            processIds.add(equs.getProcessid());
                                        }
                                        List<ApsProcessOperationDo> processOperationDos = null;
                                        if (processIds.size() > 0) {
                                            processOperationDos = apsProcessOperationService.list(new LambdaQueryWrapper<ApsProcessOperationDo>()
                                                    .in(ApsProcessOperationDo::getId, processIds));
                                        }

                                        List<EquipmentRunTime> equipmentRunTimes = new ArrayList<>();
                                        for (ApsProcessOperationProcessEquDo equs : processEqus) {
                                            EquipmentRunTime er = new EquipmentRunTime();
                                            er.setStartRunTime(equs.getPlanstartdate().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime());
                                            er.setEndRunTime(equs.getPlanenddate().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime());
                                            // 设备占用时间设置连续生产标识，参与排程
                                            if (processOperationDos != null) {
                                                List<ApsProcessOperationDo> pos = processOperationDos.stream().filter(v -> v.getId().equals(equs.getProcessid())).collect(Collectors.toList());
                                                if (pos != null && pos.size() > 0) {
                                                    ApsProcessOperationDo processesDo = pos.get(0);
                                                    er.setProcessType(processesDo.getProcess());
                                                    // 合金+输入物料+宽度
                                                    er.setSeriesProduceMark(operationDo.getMetal() + "^_^" + operationDo.getPlaninput() + "^_^" + operationDo.getProwidth());
                                                    er.setPrepressworkmin(processesDo.getPrepressworkmin() == null ? null : processesDo.getPrepressworkmin().intValue());
                                                    er.setCutfinishmin(processesDo.getCutfinishmin() == null ? null : processesDo.getCutfinishmin().intValue());
                                                }
                                            }
                                            equipmentRunTimes.add(er);
                                        }
                                        // 设备日历不可用时间段
                                        List<ApsEquipmentCalendarDo> equipmentCalendarDos = apsEquipmentCalendarService.list(
                                                new LambdaQueryWrapper<ApsEquipmentCalendarDo>().ge(ApsEquipmentCalendarDo::getSdstarttime, productionScheduleVo.getApsPlanStartDate())
                                                        .eq(ApsEquipmentCalendarDo::getProcessdeviceid, equipment.getBsEquipmentId()));
                                        if (equipmentCalendarDos != null && equipmentCalendarDos.size() > 0) {
                                            for (ApsEquipmentCalendarDo equipmentCalendarDo : equipmentCalendarDos) {
                                                EquipmentRunTime er = new EquipmentRunTime();
                                                er.setStartRunTime(equipmentCalendarDo.getSdstarttime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime());
                                                er.setEndRunTime(equipmentCalendarDo.getSdendtime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime());
                                                equipmentRunTimes.add(er);
                                            }
                                        }

                                        equipment.setEquipmentRunTimes(equipmentRunTimes);
                                    }
                                    equipmentList.add(equipment);
                                }
                            }
                        }
                    }

                    if (SaveConstans.ProcessWay.PROCESS_WAY_MERGE.equals(productprocess)) {
                        // 锁定的作业
                        if (LOCKMARK_Y.equals(operationDo.getLockmark())) {
//                            List<ApsProcessOperationProcessEquDo> popes = processOperationProcessEquService.list(new LambdaQueryWrapper<ApsProcessOperationProcessEquDo>()
//                                    .eq(ApsProcessOperationProcessEquDo::getProcessid, operationDo.getId()));
                            List<ApsProcessOperationProcessEquDo> popes = apsBlankOrderDao.getGroupProcessOperationProcessEqus(operationDo.getId());
                            if (popes != null && popes.size() > 0) {
                                for (ApsProcessOperationProcessEquDo pope : popes) {
                                    // 已存在的合并工序则添加上下道工序
                                    if (lockHbPp.containsKey(pope.getProcessdeviceid())) {
                                        ProductionProcesses processes1 = lockHbPp.get(pope.getProcessdeviceid());
                                        // 上道工序
                                        if (StringUtils.isNotBlank(operationDo.getPreviousprocessid())) {
                                            List<String> pres = new ArrayList<>();
                                            pres.addAll(Arrays.asList(operationDo.getPreviousprocessid().split(",")));
                                            pres.addAll(processes1.getPreviousProcessesIds());
                                            processes1.setPreviousProcessesIds(pres);
                                        }
                                        // 下道工序
                                        if (StringUtils.isNotBlank(operationDo.getNextprocessid())) {
                                            List<String> nexts = new ArrayList<>();
                                            nexts.addAll(Arrays.asList(operationDo.getNextprocessid().split(",")));
                                            nexts.addAll(processes1.getNextProcessesIds());
                                            processes1.setNextProcessesIds(nexts);
                                        }
                                    }
                                    // 不存在此合并工序则新增一个合并作业
                                    else {
                                        ProductionProcesses processeshb = new ProductionProcesses();
                                        // 模型ID
                                        processeshb.setId(SecurityUtil.getUUID());
                                        // 业务表主键ID
                                        List<String> bss = new ArrayList<>();
                                        bss.add(operationDo.getId());
                                        processeshb.setBsProcessesId(bss);
                                        List<String> opeqs = new ArrayList<>();
                                        opeqs.add(pope.getProcessdeviceid());
                                        processeshb.setOptionalEquipments(opeqs);
                                        // 批次
                                        processeshb.setProducePcNum(operationDo.getPlanprocessrall());
                                        processeshb.setProcessType(operationDo.getProcess());
                                        // 单次加工时长
                                        processeshb.setUnitProduceTime(operationDo.getOnceprocessmin().intValue());
                                        processeshb.setProduceTime(operationDo.getOnceprocessmin().intValue());
                                        if (operationDo.getMinflowwaitmin() != null) {
                                            processeshb.setMinWaitTime(operationDo.getMinflowwaitmin().intValue());
                                        }
                                        if (operationDo.getMaxflowwaitmin() != null) {
                                            processeshb.setMaxWaitTime(operationDo.getMaxflowwaitmin().intValue());
                                        }
                                        processes.setIfLock(true);
                                        processes.setEquipmentId(pope.getProcessdeviceid());
                                        processes.setStartTime(pope.getPlanstartdate().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime());
                                        processes.setEndTime(pope.getPlanenddate().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime());
                                        // 上道工序
                                        if (StringUtils.isNotBlank(operationDo.getPreviousprocessid())) {
                                            List<String> pres = new ArrayList<>();
                                            pres.addAll(Arrays.asList(operationDo.getPreviousprocessid().split(",")));
                                            processes.setPreviousProcessesIds(pres);
                                        }
                                        // 下道工序
                                        if (StringUtils.isNotBlank(operationDo.getNextprocessid())) {
                                            List<String> nexts = new ArrayList<>();
                                            nexts.addAll(Arrays.asList(operationDo.getNextprocessid().split(",")));
                                            processes.setNextProcessesIds(nexts);
                                        }

                                        lockHbPp.put(pope.getProcessdeviceid(), processes);
                                        processesList.add(processes);
                                    }
                                }
                            }
                        } else {
                            // 合并生产工序
                            ApsProcessOperationVo processOperationVo = new ApsProcessOperationVo();
                            BeanUtil.copyProperties(operationDo, processOperationVo);
                            processOperationVo.setProduceOrder(produceOrder);
                            processOperationVo.setDeliverydate(produceOrder.getDeliveryDate());
                            processOperationVo.setApsBlankOrderVo(apsBlankOrderVo);
                            processOperationMs.add(processOperationVo);
                        }
                    } else {
                        // 非合并生产工序

                        // 模型ID
                        processes.setId(operationDo.getId());
                        // 业务表主键ID
                        List<String> bss = new ArrayList<>();
                        bss.add(operationDo.getId());
                        processes.setBsProcessesId(bss);
                        // 工序任务可选设备
                        processes.setOptionalEquipments(optionalEquipments);
                        // 批次
                        processes.setProducePcNum(operationDo.getPlanprocessrall());
                        processes.setProcessType(operationDo.getProcess());
                        // 单次加工时长
                        processes.setUnitProduceTime(operationDo.getOnceprocessmin().intValue());
//                        processes.setProduceTime(processes.getUnitProduceTime()*processes.getProducePcNum());
                        processes.setProduceTime(operationDo.getProcesstotaltime().intValue());
                        if (operationDo.getMinflowwaitmin() != null) {
                            processes.setMinWaitTime(operationDo.getMinflowwaitmin().intValue());
                        }
                        if (operationDo.getMaxflowwaitmin() != null) {
                            processes.setMaxWaitTime(operationDo.getMaxflowwaitmin().intValue());
                        }
                        // 合金
                        processes.setVolumeMetal(operationDo.getMetal());
                        // 上道工序
                        if (StringUtils.isNotBlank(operationDo.getPreviousprocessid())) {
                            processes.setPreviousProcessesIds(Arrays.asList(operationDo.getPreviousprocessid().split(",")));
                        }
                        // 下道工序
                        if (StringUtils.isNotBlank(operationDo.getNextprocessid())) {
                            processes.setNextProcessesIds(Arrays.asList(operationDo.getNextprocessid().split(",")));
                        }
                        // 连续生产标识设置
                        // 合金+输入物料+宽度
                        processes.setSeriesProduceMark(operationDo.getMetal() + "^_^" + operationDo.getPlaninput() + "^_^" + operationDo.getProwidth());
                        processes.setPrepressworkmin(operationDo.getPrepressworkmin() == null ? null : operationDo.getPrepressworkmin().intValue());
                        processes.setCutfinishmin(operationDo.getCutfinishmin() == null ? null : operationDo.getCutfinishmin().intValue());
                        // 锁定的作业加上锁定标识
                        if (LOCKMARK_Y.equals(operationDo.getLockmark())) {
                            ApsProcessOperationProcessEquDo processEquServiceOne = processOperationProcessEquService.getOne(new LambdaQueryWrapper<ApsProcessOperationProcessEquDo>()
                                    .eq(ApsProcessOperationProcessEquDo::getProcessid, operationDo.getId()));
                            if (processEquServiceOne != null) {
                                processes.setIfLock(true);
                                processes.setEquipmentId(processEquServiceOne.getProcessdeviceid());
                                List<String> opeqs = new ArrayList<>();
                                opeqs.add(processEquServiceOne.getProcessdeviceid());
                                processes.setOptionalEquipments(opeqs);
                                processes.setStartTime(processEquServiceOne.getPlanstartdate().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime());
                                processes.setEndTime(processEquServiceOne.getPlanenddate().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime());
                            }
                        }
                        processesList.add(processes);
                    }
                }

                blankProcessIds.put(apsBlankOrderVo.getId(), processIdss);
            }
        }
        // 合并生产工序
        List<ProductionProcesses> productionProcesses = mergeProcesses(processOperationMs, eqMaps, apsScheduleConfig);
        processesList.addAll(productionProcesses);
        // 加工时间松散度设置
        /*if(looseness != null){
            for (ProductionProcesses process : processesList) {
                // 加工时间乘以松散度并向上取整
                process.setProduceTime(new BigDecimal(process.getProduceTime()).multiply((looseness.divide(new BigDecimal("100"))).add(new BigDecimal("1"))).setScale(0, RoundingMode.CEILING).intValue());
            }
        }*/
        productionScheduleVo.setProcesses(processesList);
        productionScheduleVo.setEquipmentList(equipmentList);
        productionScheduleVo.setBlankProcessIds(blankProcessIds);
        return productionScheduleVo;
    }

    /**
     * 合并生产工序
     *
     * @param processOperationMs
     * @return
     */
    private List<ProductionProcesses> mergeProcesses(List<ApsProcessOperationVo> processOperationMs, Map<String, AspCheckItemsDo> eqMaps, ApsScheduleConfigDo apsScheduleConfig) {
        // 最终的合并工序
        List<ProductionProcesses> processesList = new ArrayList<>();
        if (processOperationMs != null && processOperationMs.size() > 0) {
            // 按照期望交货期排序
            processOperationMs.sort(Comparator.comparing(ApsProcessOperationVo::getDeliverydate));
            // 获取所有设备，并按已存在最晚完工时间排序
            String allSelEqs = null;
            for (ApsProcessOperationVo processOperationM : processOperationMs) {
                if (StringUtils.isNotBlank(processOperationM.getCanchoosedeviceid())) {
                    String[] sbIds = processOperationM.getCanchoosedeviceid().split(",");
                    for (String equIds : sbIds) {
                        if (allSelEqs == null) {
                            allSelEqs = "'" + equIds + "'";
                        } else {
                            if (!allSelEqs.contains(equIds)) {
                                allSelEqs = allSelEqs + ",'" + equIds + "'";
                            }
                        }
                    }
                }
            }
            // 设备排序
            List<AspCheckItemsDo> aspCheckItemsDos = apsBlankOrderDao.eqAscs(allSelEqs);
            // 记录当前设备已排程个数
            Map<String, Integer> aspCheckItemUse = new HashMap<>();
            Map<String, AspCheckItemsDo> aspCheckItemMap = new HashMap<>();
            for (AspCheckItemsDo aspCheckItemsDo : aspCheckItemsDos) {
                aspCheckItemMap.put(aspCheckItemsDo.getId(), aspCheckItemsDo);
                aspCheckItemUse.put(aspCheckItemsDo.getId(), 0);
                // 退火炉套筒间隙扣除
                if (apsScheduleConfig != null) {
                    if (aspCheckItemsDo.getEquipmentlength() == null) {
                        throw new CustomException("退火炉的长度没有维护");
                    }
                    aspCheckItemsDo.setEquipmentlength(aspCheckItemsDo.getEquipmentlength().subtract(apsScheduleConfig.getLooseness()));
                }
            }
            for (ApsProcessOperationVo processOperationM : processOperationMs) {
                // 首先查看是否有可以合并的已存在的工序
                hasProcessmerge(processesList, processOperationM, aspCheckItemMap);
                // 剩下作业加工卷数,启用新的作业
                int planprocessrall = processOperationM.getPlanprocessrall();
                if (planprocessrall <= 0) {
                    continue;
                }
                Boolean a = true;
                while (a) {
                    int lastnum = processOperationM.getPlanprocessrall();
                    // 剩余的料卷
                    if (lastnum > 0) {
                        Integer minUse = null;
                        int haslast = 0;
                        AspCheckItemsDo aspCheckItem = null;
                        for (int i = 0; i < aspCheckItemsDos.size(); i++) {
                            // 选择的设备要在可选设备的范围内
                            if (processOperationM.getCanchoosedeviceid().contains(aspCheckItemsDos.get(i).getId())) {
                                // 单卷宽度不能超过设备宽度并且单卷重不能超过设备的承重
                                if (processOperationM.getProwidth().compareTo(aspCheckItemsDos.get(i).getEquipmentlength()) < 0
                                        && processOperationM.getSinglerollweight().compareTo(aspCheckItemsDos.get(i).getEquipmentbearing()) < 0) {
                                    // 获取使用次数最小的可选设备
                                    if (aspCheckItem == null) {
                                        minUse = aspCheckItemUse.get(aspCheckItemsDos.get(i).getId());
                                        aspCheckItem = aspCheckItemsDos.get(i);
                                    } else {
                                        if (minUse > aspCheckItemUse.get(aspCheckItemsDos.get(i).getId())) {
                                            minUse = aspCheckItemUse.get(aspCheckItemsDos.get(i).getId());
                                            aspCheckItem = aspCheckItemsDos.get(i);
                                        }
                                    }
                                }
                            }
                        }
                        // 新增工序作业
                        if (aspCheckItem != null) {
                            // 选择的设备要在可选设备的范围内
                            if (processOperationM.getCanchoosedeviceid().contains(aspCheckItem.getId())) {
                                // 单卷宽度不能超过设备宽度并且单卷重不能超过设备的承重
                                if (processOperationM.getProwidth().compareTo(aspCheckItem.getEquipmentlength()) < 0
                                        && processOperationM.getSinglerollweight().compareTo(aspCheckItem.getEquipmentbearing()) < 0) {
                                    for (int n = 1; n <= lastnum; n++) {
                                        // 宽度余量
                                        if (aspCheckItem.getEquipmentlength().compareTo(processOperationM.getProwidth().multiply(new BigDecimal(n))) < 0) {
                                            break;
                                        }
                                        // 重量余量
                                        if (aspCheckItem.getEquipmentbearing().compareTo(processOperationM.getSinglerollweight().multiply(new BigDecimal(n))) < 0) {
                                            break;
                                        }
                                        haslast = n;
                                    }
                                    if (haslast > 0) {
                                        ProductionProcesses processes = new ProductionProcesses();
                                        processes.setId(SecurityUtil.getUUID());
                                        List<String> bss = new ArrayList<>();
                                        bss.add(processOperationM.getId());
                                        processes.setBsProcessesId(bss);
                                        processes.setProcessType(processOperationM.getProcess());
                                        processes.setOptionalEquipments(Arrays.asList(new String[]{aspCheckItem.getId()}));
                                        // 坯料计划订单
                                        ProduceOrder produceOrder = new ProduceOrder(processOperationM.getApsBlankOrderVo().getId(), processOperationM.getApsBlankOrderVo().getMaterialname(), processOperationM.getApsBlankOrderVo().getDeliverydate());
                                        List<ProduceOrder> produceOrders = new ArrayList<>();
                                        produceOrders.add(produceOrder);
                                        processes.setProduceOrder(produceOrders);

                                        processes.setAluminumCoils(new ArrayList<>());
                                        for (int j = 1; j <= haslast; j++) {
                                            AluminumCoil aluminumCoil = new AluminumCoil();
                                            aluminumCoil.setBsId(processOperationM.getId());
                                            aluminumCoil.setBlankid(processOperationM.getBlankid());
                                            aluminumCoil.setVolumeWidth(processOperationM.getProwidth());
                                            aluminumCoil.setVolumeThickness(processOperationM.getThickness());
                                            aluminumCoil.setVolumeWeight(processOperationM.getSinglerollweight());
                                            aluminumCoil.setVolumeMetal(processOperationM.getMetal());
                                            aluminumCoil.setVolumeMetalstate(processOperationM.getMetalstate());
                                            processes.getAluminumCoils().add(aluminumCoil);
                                        }
                                        // 批次
                                        processes.setProducePcNum(1);
                                        // 最小等待时长
                                        if (processOperationM.getMinflowwaitmin() != null) {
                                            processes.setMinWaitTime(processOperationM.getMinflowwaitmin().intValue());
                                        }
                                        // 最大等待时长
                                        if (processOperationM.getMaxflowwaitmin() != null) {
                                            processes.setMaxWaitTime(processOperationM.getMaxflowwaitmin().intValue());
                                        }
                                        // 合并加工的为瓶颈工序
                                        processes.setBottleneck(true);
                                        // 单次加工时长
                                        processes.setUnitProduceTime(processOperationM.getOnceprocessmin().intValue());
                                        processes.setProduceTime(processOperationM.getOnceprocessmin().intValue());
                                        // 上道工序
                                        if (StringUtils.isNotBlank(processOperationM.getPreviousprocessid())) {
                                            processes.setPreviousProcessesIds(new ArrayList<>(Arrays.asList(processOperationM.getPreviousprocessid().split(","))));
                                        }
                                        // 下道工序
                                        if (StringUtils.isNotBlank(processOperationM.getNextprocessid())) {
                                            processes.setNextProcessesIds(new ArrayList<>(Arrays.asList(processOperationM.getNextprocessid().split(","))));
                                        }
                                        processesList.add(processes);

                                        lastnum = lastnum - haslast;
                                        // 更新设备使用次数
                                        aspCheckItemUse.put(aspCheckItem.getId(), aspCheckItemUse.get(aspCheckItem.getId()) + 1);
                                    }
                                }
                            }
                        }
                    }
                    // 重新设置计划加工卷数
                    processOperationM.setPlanprocessrall(lastnum);
                    if (lastnum <= 0) {
                        a = false;
                    }
                }
            }
        }
        return processesList;
    }

    /**
     * 查看是否有可以合并的已存在的工序
     *
     * @param processesList
     * @param processOperationM
     * @param aspCheckItemMap
     */
    private void hasProcessmerge(List<ProductionProcesses> processesList, ApsProcessOperationVo processOperationM, Map<String, AspCheckItemsDo> aspCheckItemMap) {
        // 首先看已合并生产的设备是否还有余量继续排产
        for (ProductionProcesses processes : processesList) {
            // 加工设备
            String eq = processes.getOptionalEquipments().get(0);
            AspCheckItemsDo aspCheckItem = aspCheckItemMap.get(eq);
            // 工序类型一致，合金和合金状态要一致，并且同一坯料计划两个不同合并工序不能合并
            List<String> blankids = processes.getProduceOrder().stream().map(v -> v.getId()).collect(Collectors.toList());
            if (processes.getProcessType().equals(processOperationM.getProcess()) && processes.getAluminumCoils().get(0).getVolumeMetal().equals(processOperationM.getMetal())
                    && processes.getAluminumCoils().get(0).getVolumeMetalstate().equals(processOperationM.getMetalstate()) && !blankids.contains(processOperationM.getBlankid())) {
                // 宽度不超过50mm，厚度不超过0.05mm，重量不超过1吨
                boolean bol = true;
                BigDecimal totalWidth = null;
                BigDecimal totalWeight = null;
                // 宽度、厚度、重量绝对值差限制
                BigDecimal knum = null;
                BigDecimal hnum = null;
                BigDecimal znum = null;
                if (SaveConstans.ProcessType.PROCESS_TYPE_CHENTUI.equals(processes.getProcessType())) {
                    knum = new BigDecimal("50");
                    hnum = new BigDecimal("0.05");
                    znum = new BigDecimal("1");
                } else if (SaveConstans.ProcessType.PROCESS_TYPE_ZHONGTUI.equals(processes.getProcessType())) {
                    knum = new BigDecimal("100");
                    hnum = new BigDecimal("0.08");
                    znum = new BigDecimal("1");
                }
                if (knum == null || hnum == null || znum == null) {
                    continue;
                }

                for (AluminumCoil aluminumCoil : processes.getAluminumCoils()) {
                    // 宽度不超过50mm
                    if (aluminumCoil.getVolumeWidth().subtract(processOperationM.getProwidth()).abs().compareTo(knum) > 0) {
                        bol = false;
                    }
                    // 厚度不超过0.05mm
                    if (aluminumCoil.getVolumeThickness().subtract(processOperationM.getThickness()).abs().compareTo(hnum) > 0) {
                        bol = false;
                    }
                    // 重量不超过1吨
                    if (aluminumCoil.getVolumeWeight().subtract(processOperationM.getSinglerollweight()).abs().compareTo(znum) > 0) {
                        bol = false;
                    }
                    if (totalWidth == null) {
                        totalWidth = aluminumCoil.getVolumeWidth();
                    } else {
                        totalWidth = totalWidth.add(aluminumCoil.getVolumeWidth());
                    }
                    if (totalWeight == null) {
                        totalWeight = aluminumCoil.getVolumeWeight();
                    } else {
                        totalWeight = totalWeight.add(aluminumCoil.getVolumeWeight());
                    }
                }
                if (bol) {
                    Integer sy = 0;
                    for (int j = 1; j <= processOperationM.getPlanprocessrall(); j++) {
                        // 宽度余量
                        if (aspCheckItem.getEquipmentlength().subtract(totalWidth).compareTo(processOperationM.getProwidth().multiply(new BigDecimal(j))) < 0) {
                            break;
                        }
                        // 重量余量
                        if (aspCheckItem.getEquipmentbearing().subtract(totalWeight).compareTo(processOperationM.getSinglerollweight().multiply(new BigDecimal(j))) < 0) {
                            break;
                        }
                        sy = j;
                    }
                    if (sy > 0) {
                        // 更新计划加工卷数
                        processOperationM.setPlanprocessrall(processOperationM.getPlanprocessrall() - sy);
                        for (int j = 1; j <= sy; j++) {
                            AluminumCoil aluminumCoil = new AluminumCoil();
                            aluminumCoil.setBsId(processOperationM.getId());
                            aluminumCoil.setBlankid(processOperationM.getBlankid());
                            aluminumCoil.setVolumeWidth(processOperationM.getProwidth());
                            aluminumCoil.setVolumeThickness(processOperationM.getThickness());
                            aluminumCoil.setVolumeWeight(processOperationM.getSinglerollweight());
                            aluminumCoil.setVolumeMetal(processOperationM.getMetal());
                            aluminumCoil.setVolumeMetalstate(processOperationM.getMetalstate());
                            processes.getAluminumCoils().add(aluminumCoil);
                        }

                        // 上道工序
                        if (StringUtils.isNotBlank(processOperationM.getPreviousprocessid())) {
                            if (processes.getPreviousProcessesIds() == null) {
                                List<String> list = new ArrayList<>();
                                list.addAll(Arrays.asList(processOperationM.getPreviousprocessid().split(",")));
                                processes.setPreviousProcessesIds(list);
                            } else {
                                processes.getPreviousProcessesIds().addAll(new ArrayList<>(Arrays.asList(processOperationM.getPreviousprocessid().split(","))));
                            }
                        }
                        // 下道工序
                        if (StringUtils.isNotBlank(processOperationM.getNextprocessid())) {
                            if (processes.getNextProcessesIds() == null) {
                                List<String> list = new ArrayList<>();
                                list.addAll(Arrays.asList(processOperationM.getNextprocessid().split(",")));
                                processes.setNextProcessesIds(list);
                            } else {
                                processes.getNextProcessesIds().addAll(new ArrayList<>(Arrays.asList(processOperationM.getNextprocessid().split(","))));
                            }
                        }
                        // 所属订单
                        List<ProduceOrder> produceOrders = processes.getProduceOrder().stream().filter(v ->
                                v.getId().equals(processOperationM.getApsBlankOrderVo().getId())).collect(Collectors.toList());
                        if (produceOrders == null || produceOrders.size() == 0) {
                            ProduceOrder produceOrder = new ProduceOrder(processOperationM.getApsBlankOrderVo().getId(), processOperationM.getApsBlankOrderVo().getMaterialname(), processOperationM.getApsBlankOrderVo().getDeliverydate());
                            processes.getProduceOrder().add(produceOrder);
                        }
                        // 单次加工时长
                        if (processes.getUnitProduceTime() == null) {
                            processes.setUnitProduceTime(processOperationM.getOnceprocessmin().intValue());
                            processes.setProduceTime(processOperationM.getOnceprocessmin().intValue());
                        }
                        // 加工时长取最大值
                        else {
                            if (processes.getUnitProduceTime() < processOperationM.getOnceprocessmin().intValue()) {
                                processes.setUnitProduceTime(processOperationM.getOnceprocessmin().intValue());
                                processes.setProduceTime(processOperationM.getOnceprocessmin().intValue());
                            }
                        }
                        processes.getBsProcessesId().add(processOperationM.getId());
                    }
                }
            }
        }
    }

    private void checkIfCanMerge() {

    }

    /**
     * 排程结束之后根据坯料计划IDs修改生产状态
     */
    private void updateProductStatusByOrderIds(List<ApsBlankOrderVo> blankOrderVoList) {
        if (blankOrderVoList.isEmpty()) {
            log.debug("无需修改坯料计划的生产状态");
            return;
        }

        //每个需要排程的订单挑选一个坯料计划，用于更新订单的生产状态
        Set<String> orderIds = new HashSet<>();
        //最终需要更新生产状态的坯料计划Id集合
        List<String> needUpdateProductStatusBlankIdList = new LinkedList<>();
        for (ApsBlankOrderVo blankOrderVo : blankOrderVoList) {
            if (!orderIds.contains(blankOrderVo.getProductionorderid())) {
                orderIds.add(blankOrderVo.getProductionorderid());
                needUpdateProductStatusBlankIdList.add(blankOrderVo.getId());
            }
        }
        //需要修改自身状态的坯料计划ID集合
        Set<String> blankIds = blankOrderVoList.stream().map(ApsBlankOrderVo::getId).collect(Collectors.toSet());

        String idStrs = CollUtil.join(blankIds, "','");
        idStrs = "'" + idStrs + "'";
        //更新工序作业的生产状态为‘待发布’(20)
        this.baseMapper.updateProcessOperationByOrderIds(idStrs);
        //更新所有坯料计划的生产状态
        ApsBlankOrderDo apsBlankOrderDo = new ApsBlankOrderDo();
        apsBlankOrderDo.setProductstatus(TO_BE_PUBLISHED);
        apsBlankOrderDo.setSchedulingstatus(SaveConstans.SchedulingStatus.TO_BE_PUBLISHED);
        this.update(apsBlankOrderDo, new QueryWrapper<ApsBlankOrderDo>().lambda().in(ApsBlankOrderDo::getId, blankIds).eq(ApsBlankOrderDo::getProductstatus, SaveConstans.SchedulingStatus.TO_BE_SCHEDULED));
        //公共方法挨个更新
        for (String blankId : needUpdateProductStatusBlankIdList) {
            CommonUpdateProductionStatusReq commonUpdateProductionStatusReq = new CommonUpdateProductionStatusReq();
            commonUpdateProductionStatusReq.setBlankId(blankId);
            apsProcessOperationProcessEquService.updateProductionStatus(commonUpdateProductionStatusReq);
        }

//        //更新所有产品明细的生产状态
//        ApsProductDetailDo apsProductDetailDo = new ApsProductDetailDo();
//        apsProductDetailDo.setProductstatus(TO_BE_PUBLISHED);
//        apsProductDetailService.update(apsProductDetailDo, new QueryWrapper<ApsProductDetailDo>().lambda().in(ApsProductDetailDo::getMainid, blankIds).eq(ApsProductDetailDo::getProductstatus, SaveConstans.SchedulingStatus.TO_BE_SCHEDULED));
//        //更新订单的生产状态
//        ApsProductionOrderDo apsProductionOrderDo = new ApsProductionOrderDo();
//        apsProductionOrderDo.setProductionstatus(TO_BE_PUBLISHED);
//        apsProductionOrderService.update(apsProductionOrderDo, new QueryWrapper<ApsProductionOrderDo>().lambda().in(ApsProductionOrderDo::getId, orderIds).eq(ApsProductionOrderDo::getProductionstatus, SaveConstans.SchedulingStatus.TO_BE_SCHEDULED));
    }

    /**
     * 物理删除排程备份（工序作业备份【aps_process_operation_backup】、加工设备备份【aps_process_operation_process_equ_backup】）
     * 更新状态
     */
    public void deleteBackup(List<String> blankids, List<String> apsProductionOrderIds) {
        // 删除工序作业和工序作业明备份数据
        ApsUtils.clearBackup(null);
        // 更工序作业生产状态=待排程，是否锁定=否，
        apsProcessOperationService.update(new LambdaUpdateWrapper<ApsProcessOperationDo>()
                .in(ApsProcessOperationDo::getBlankid, blankids)
                .set(ApsProcessOperationDo::getProcessstatus, TO_BE_SCHEDULED)
                .set(ApsProcessOperationDo::getLockmark, LOCKMARK_N)
        );
        //存储日志
        apsConflictLogService.recordLog("", "更新工序作业的生产状态为待排程，坯料计划ID为：" + blankids, "", 2, null);

        // 清空作业明细
        processOperationProcessEquService.remove(new LambdaUpdateWrapper<ApsProcessOperationProcessEquDo>()
                .in(ApsProcessOperationProcessEquDo::getBlankid, blankids));
        //存储日志
        apsConflictLogService.recordLog("", "逻辑删除作业明细，坯料计划ID为：" + blankids, "", 2, null);

        // 更新生产订单的状态
        apsProductionOrderService.update(new LambdaUpdateWrapper<ApsProductionOrderDo>()
                .in(ApsProductionOrderDo::getId, apsProductionOrderIds)
                .set(ApsProductionOrderDo::getScheduledeliverydate, null)
                .set(ApsProductionOrderDo::getProductionstatus, TO_BE_SCHEDULED));
        // 更新坯料计划的状态
        this.baseMapper.cancelSchedulingUpdate(blankids);
        //存储日志
        apsConflictLogService.recordLog("", "更新坯料计划状态为待排程，坯料计划ID为：" + blankids, "", 1, null);
        //获取坯料计划用到的产品明细ID
        String idStrs = CollUtil.join(blankids, "','");
        idStrs = "'" + idStrs + "'";
        List<String> productIdList = this.baseMapper.selectOutputProductIdList(idStrs);
        List<ApsProductDetailDo> productDetailDoList = new LinkedList<>();
        for (String productId : productIdList) {
            ApsProductDetailDo apsProductDetailDo = new ApsProductDetailDo();
            apsProductDetailDo.setId(productId);
            apsProductDetailDo.setProductstatus(TO_BE_SCHEDULED);
            productDetailDoList.add(apsProductDetailDo);
        }
        // 更新坯料计划产品明细的的生产状态
        if (productDetailDoList.size() > 0) {
            apsProductDetailService.updateBatchById(productDetailDoList);
        }
//        apsProductDetailDao.updateProductstatus(apsProductionOrderIds);
    }

    @Override
    @Transactional
    public R checkAndSaveOrUpdateBlank(CheckAndSaveOrUpdateBlankReq req) {
        //获取校验级别
        Integer checkLevel = req.getCheckLevel();

        //获取坯料计划内容
        ApsBlankOrderDo apsBlankOrderDo = req.getApsBlankOrderDo();

        //如果数据库存在此坯料计划 并且 坯料计划的生产状态和数据库的生产状态不一致，提示
        ApsBlankOrderDo newBlankOrder = this.getById(apsBlankOrderDo.getId());
        if (ObjectUtil.isNotEmpty(newBlankOrder) && !apsBlankOrderDo.getProductstatus().equals(newBlankOrder.getProductstatus())) {
            return R.error("该坯料计划状态已被其他人修改，请刷新页面后重新进入");
        }

        //获取当前用户信息
        //获取当前用户的所属工厂
        SysUserVo currentUser = CXCommonUtils.getCurrentUser();
        String tenantId = CXCommonUtils.getCurrentUserFactoryId(currentUser);


        //=============校验坯料长度=================
        //如果坯料需求的外观类型是板锭或铝板，并且坯料长度为空，则提示：坯料是板锭和铝板时长度必填
        if (ObjectUtil.isNotEmpty(apsBlankOrderDo.getBlankappearance())
                && Arrays.asList(SaveConstans.BlankAppearanceType.ALUMINUM_PLATE, SaveConstans.BlankAppearanceType.ALUMINUM_INGOT).contains(apsBlankOrderDo.getBlankappearance())
                && ObjectUtil.isEmpty(apsBlankOrderDo.getProlength())) {
            return R.error("坯料是铝板和铝锭时长度必填");
        }

//        //如果坯料产品类型对应产品外观类型是铝板或板锭，并且坯料长度为空，则提示：坯料是铝板和板锭时长度必填
//        if (ObjectUtil.isNotEmpty(apsBlankOrderDo.getProducttypeid())) {
//            //目前数据字典存在3层关系，选择只能选择最后一层，二开查询最后一层的父节点对应数据的父节点，判断是否为铝板或板锭的主键ID
//            String pid = this.baseMapper.selectDictAboutProductType(apsBlankOrderDo.getProducttypeid());
//            //如果是铝板或板锭
//            if (ObjectUtil.isNotEmpty(pid) && (pid.equals(SaveConstans.ProductionType.BANDING_ID) || pid.equals(SaveConstans.ProductionType.LVBAN_ID))) {
//                //坯料长度为空
//                if (ObjectUtil.isEmpty(apsBlankOrderDo.getProlength())) {
//                    return R.error("坯料是铝板和板锭时长度必填");
//                }
//            }
//        }
        Collator collator = Collator.getInstance(Locale.US);
        if (checkLevel == 2 || checkLevel == 3) {
            //==============拼接工艺路线输出成品和工艺输出物料规格==============
            //查询坯料计划对应工艺路线的输出成品
            List<String> processOutputList = this.baseMapper.selectProcessByBlankId(apsBlankOrderDo.getId());
            if (processOutputList != null && processOutputList.size() > 0) {
                //工艺路线输出成品
                String outputProcessMaterial = "";
                //排序
                processOutputList.sort(collator);
                for (int i = 0; i < processOutputList.size(); i++) {
                    outputProcessMaterial += "[" + (i + 1) + "]" + processOutputList.get(i);
                    if (i < processOutputList.size() - 1) {
                        outputProcessMaterial += ",\r\n";
                    }
                }
                apsBlankOrderDo.setOutputmaterial(outputProcessMaterial);

                //工艺输出物料规格
                String processOutputMaterialSpecification = "";
                List<String> processOutputMaterialSpecificationList = processOutputList.stream().map(item -> {
                    String[] s = item.split(" ");
                    return s[0] + " " + s[1];
                }).sorted(collator).collect(Collectors.toList());
                for (int i = 0; i < processOutputMaterialSpecificationList.size(); i++) {
                    processOutputMaterialSpecification += "[" + (i + 1) + "]" + processOutputMaterialSpecificationList.get(i);
                    if (i < processOutputMaterialSpecificationList.size() - 1) {
                        processOutputMaterialSpecification += ",\r\n";
                    }
                }
                apsBlankOrderDo.setProcessoutmaterspecification(processOutputMaterialSpecification);


            } else {
                return R.error("工艺路线没有输出成品");
            }
        }

        //=============校验输出成品==============
        //产品明细
        List<ApsProductDetailDo> apsProductDetailDoList = req.getApsProductDetailDoList();
        //输出成品
        List<ApsProcessOutputProductDo> apsProcessOutputProductDoList = req.getApsProcessOutputProductDoList().stream().filter(item -> ObjectUtil.isEmpty(item.getDeleted()) || item.getDeleted().equals("0")).collect(Collectors.toList());

        //如果坯料计划未开工(生产状态<=30)
        if (apsBlankOrderDo.getProductstatus().compareTo(TO_BE_STARTED) <= 0) {
            for (ApsProcessOutputProductDo apsProcessOutputProductDo : apsProcessOutputProductDoList) {
                boolean haveProductDetail = false;
                for (ApsProductDetailDo apsProductDetailDo : apsProductDetailDoList) {
                    //判断是否存在对应的产品明细
                    if (apsProcessOutputProductDo.getProductid().equals(apsProductDetailDo.getId())) {
                        haveProductDetail = true;

                        if (apsProductDetailDo.getInputmaterialdescription().equals(apsProcessOutputProductDo.getProductname())) {
//                            //如果输出单卷重<单卷重（起）或 输出单卷重>单卷重（止），错误提示：输出成品：{订单产品} 输出单卷重不符合订单要求
////                            if (apsProcessOutputProductDo.getOutputonerollweigth().compareTo(apsProductDetailDo.getSinglerollweightstart()) < 0
////                                    || apsProcessOutputProductDo.getOutputonerollweigth().compareTo(apsProductDetailDo.getSinglerollweightend()) > 0) {
////                                return R.error("输出成品：" + apsProcessOutputProductDo.getProductname() + " 输出单卷重不符合订单要求");
////                            }
                        } else {//如果对应订单产品明细的订单产品字段值不等于输出成品的订单产品字段值，错误提示：输出成品：{订单产品} 对应订单产品已修改或已删除，请重新选择订单产品
                            return R.error("输出成品：" + apsProcessOutputProductDo.getProductname() + " 对应订单产品已修改或已删除，请重新选择订单产品");
                        }
                        break;
                    }
                }
                //存在对应的产品明细
                if (haveProductDetail) {
                    //如果输出单卷重>坯料单卷重，错误提示：输出单卷重不能超过坯料单卷重
                    //（注意：成品重量和坯料重量单位吨和公斤需要转换后比较）
                    //转化单卷重 单位统一为 公斤（kg）

                    //输出的单卷重
                    BigDecimal outputWeight = new BigDecimal(0);
                    //坯料的单卷重
                    BigDecimal blankWeight = new BigDecimal(0);
                    outputWeight = apsProcessOutputProductDo.getOutputonerollweigth();
                    blankWeight = apsBlankOrderDo.getSinglerollweight();
                    //统一输出的重量 现在都是吨（固定的）
//                    //公斤/千克
//                    if (apsProcessOutputProductDo.getCountunit().equals(SaveConstans.CountUnit.KG)) {
//                        outputWeight = apsProcessOutputProductDo.getOutputonerollweigth();
//                    }
//                    //吨
//                    if (apsProcessOutputProductDo.getCountunit().equals(SaveConstans.CountUnit.TON)) {
//                        outputWeight = apsProcessOutputProductDo.getOutputonerollweigth().multiply(new BigDecimal(1000));
//                    }
//
//                    //统一坯料的重量
//                    if (apsBlankOrderDo.getNumberunit().equals(SaveConstans.CountUnit.KG)) {
//                        blankWeight = apsBlankOrderDo.getSinglerollweight();
//                    }
//                    if (apsBlankOrderDo.getNumberunit().equals(SaveConstans.CountUnit.TON)) {
//                        BigDecimal singlerollweight = apsBlankOrderDo.getSinglerollweight();
//                        blankWeight = singlerollweight.multiply(new BigDecimal(1000));
//                    }
                    //校验 输出单卷重 必须小于等于坯料单卷重，必须大于等于单卷重（起），必须小于等于单卷重（止）
                    //输出单卷重>坯料单卷重
                    if (outputWeight.compareTo(blankWeight) > 0) {
                        return R.error("输出成品：" + apsProcessOutputProductDo.getProductname() + " 输出单卷重不能超过坯料单卷重");
                    }
//                    //输出单卷重<单卷重（起）
//                    if (apsProcessOutputProductDo.getOutputonerollweigth().compareTo(apsProcessOutputProductDo.getOnerollweigthstart()) < 0) {
//                        return R.error("输出成品：" + apsProcessOutputProductDo.getProductname() + " 输出单卷重必须大于等于单卷重（起）");
//                    }
//                    //输出单卷重>单卷重（止）
//                    if (apsProcessOutputProductDo.getOutputonerollweigth().compareTo(apsProcessOutputProductDo.getOnerollweigthend()) > 0) {
//                        return R.error("输出成品：" + apsProcessOutputProductDo.getProductname() + " 输出单卷重必须小于等于单卷重（止）");
//                    }
                } else {//不存在对应的产品明细
                    return R.error("输出成品：" + apsProcessOutputProductDo.getProductname() + " 对应订单产品已修改或已删除，请重新选择订单产品");
                }
            }
        }

        //不论坯料计划的状态，需要增加的判断
        for (ApsProcessOutputProductDo apsProcessOutputProductDo : apsProcessOutputProductDoList) {
            boolean haveProductDetail = false;
            for (ApsProductDetailDo apsProductDetailDo : apsProductDetailDoList) {
                //判断是否存在对应的产品明细
                if (apsProcessOutputProductDo.getProductid().equals(apsProductDetailDo.getId())) {
                    haveProductDetail = true;
//                    //计划输出订单重量 > 对应订单产品订单总数量；错误提示：输出成品 {订单产品} 计划输出订单重量 不能大于 订单总数量
//                    if (apsProcessOutputProductDo.getPlanoutputonerollweight().compareTo(apsProductDetailDo.getTotalordercount()) > 0) {
//                        return R.error("输出成品：" + apsProcessOutputProductDo.getProductname() + " 计划输出订单重量 不能大于 订单总数量");
//                    }
                }
            }
            if (!haveProductDetail && apsProcessOutputProductDo.getPlanoutputonerollweight().compareTo(new BigDecimal(0)) > 0) {
                return R.error("输出成品：" + apsProcessOutputProductDo.getProductname() + " 已修改或已删除，并已开工，计划输出订单重量 不能大于 0");
            }
            //如果输出卷数不是坯料卷数的倍数，错误提示:“输出成品:{订单产品}输出卷数必须是坏料卷数的倍数”
            if (ObjectUtil.isNotEmpty(apsProcessOutputProductDo.getPlanoutputorderroll()) && ObjectUtil.isNotEmpty(apsBlankOrderDo.getRollnum())) {
                if (apsProcessOutputProductDo.getPlanoutputorderroll() % apsBlankOrderDo.getRollnum() != 0) {
                    return R.error("输出成品：" + apsProcessOutputProductDo.getProductname() + " 输出卷数必须是坯料卷数的倍数");
                }
            }

            //如果输出成品技术要求的坯料外观类型是铝板或铝锭，并且长度为空，则提示：输出成品是板锭和铝板时长度必填
            if (ObjectUtil.isNotEmpty(apsProcessOutputProductDo.getBlankappearance())
                    && Arrays.asList(SaveConstans.BlankAppearanceType.ALUMINUM_PLATE, SaveConstans.BlankAppearanceType.ALUMINUM_INGOT).contains(apsProcessOutputProductDo.getBlankappearance())
                    && ObjectUtil.isEmpty(apsProcessOutputProductDo.getProlength())) {
                return R.error("输出成品：" + apsProcessOutputProductDo.getProductname() + " 是板锭和铝板时长度必填");
            }
        }

        /**
         * •如果坯料输出成品技术要求的坯料类型与当前坯料需求的不同，
         * 错误提示：坯料类型{坯料类型}与订单产品的技术要求的坯料类型{坯料类型}不同，不可作为该坯料的输出成品
         * •如果坯料输出成品技术要求的坯料产品类型与当前坯料需求的不同，
         * 错误提示：坯料需求的产品类型{产品类型}与订单产品的技术要求的坯料产品类型{坯料产品类型}不同，不可作为该坯料的输出成品
         */
        for (ApsProcessOutputProductDo apsProcessOutputProductDo : apsProcessOutputProductDoList) {
            if (!apsProcessOutputProductDo.getBlanktype().equals(apsBlankOrderDo.getBlanktype())) {
                return R.error("输出成品：" + apsProcessOutputProductDo.getProductname() + " 坯料类型" + apsBlankOrderDo.getBlanktype() + "与订单产品的技术要求的坯料类型" + apsProcessOutputProductDo.getBlanktype() + "不同，不可作为该坯料的输出成品");
            }
            if (!apsProcessOutputProductDo.getBlankproducttype().equals(apsBlankOrderDo.getProducttype())) {
                return R.error("输出成品：" + apsProcessOutputProductDo.getProductname() + " 坯料需求的产品类型" + apsBlankOrderDo.getProducttype() + "与订单产品的技术要求的坯料产品类型" + apsProcessOutputProductDo.getBlankproducttype() + "不同，不可作为该坯料的输出成品");
            }
        }


        //如果存在订单产品和输出单卷重都相同坯料输出成品，错误提示：以下坯料输出成品重复：{订单产品1}{输出单卷重}{数量单位}，{订单产品2}{输出单卷重}{数量单位}
        //新需求：如果存在关联相同的订单产品，并且输出单卷重都相同坯料输出成品，错误提示：以下坯料输出成品重复：{订单产品1}{输出单卷重}{数量单位}，{订单产品2}{输出单卷重}{数量单位}
        //用于判断是否重复
        Map<String, String> checkOutputDesMap = new HashMap<>();
        //有重复数据订单产品的数组
        List<String> endCheckOutputDesList = new LinkedList<>();
        for (ApsProcessOutputProductDo apsProcessOutputProductDo : apsProcessOutputProductDoList) {
            //产品明细的ID
            String productid = apsProcessOutputProductDo.getProductid();

            String unit = "吨";
            if (apsProcessOutputProductDo.getCountunit().equals(SaveConstans.CountUnit.KG)) {
                unit = "千克";
            }
            //拼接字段
            String s = apsProcessOutputProductDo.getProductname() + apsProcessOutputProductDo.getOutputonerollweigth() + unit;

            if (checkOutputDesMap.containsKey(productid) && checkOutputDesMap.get(productid).equals(s)) {
                endCheckOutputDesList.add(s);
            } else {
                checkOutputDesMap.put(productid, s);
            }
        }
        List<String> collect = endCheckOutputDesList.stream().distinct().collect(Collectors.toList());
        if (!collect.isEmpty()) {
            String join = String.join(",", collect);
            return R.error("以下坯料输出成品重复：" + join);
        }

        //如果坯料输出成品的计划输出订单重量合计>坯料的总重量，错误提示：坯料输出成品的计划输出订单重量合计不能大于坯料的总重量
        //计算输出成品的总计划输出重量
        BigDecimal planAll = new BigDecimal("0");
        for (ApsProcessOutputProductDo apsProcessOutputProductDo : apsProcessOutputProductDoList) {
            BigDecimal planoutputonerollweight = apsProcessOutputProductDo.getPlanoutputonerollweight();
            planAll = planAll.add(planoutputonerollweight);
        }
        //坯料的总重量
        BigDecimal totalweight = apsBlankOrderDo.getTotalweight();
        if (planAll.compareTo(totalweight) > 0) {//如果坯料输出成品的计划输出订单重量合计>坯料的总重量
            return R.error("坯料输出成品的计划输出订单重量合计不能大于坯料的总重量");
        }

        //===========坯料计划保存之前的校验=============
        //校验坯料需求
        //后台校验备料:如果是备料计划，
        //并且保存前备料已经被其他计划员新增的备料计划选择并保存了
        //（即：坯料的是否备料=是，并且所选在制品的备料计划ID不为空，并且不是该备料计划ID），
        //则错误提示：所选在制品已被其他备料计划使用
        ApsWorkInProgressInventoryDo progressWork = null;
        if (apsBlankOrderDo.getPreparematerial().equals("是") && ObjectUtil.isNotEmpty(req.getProgressId())) {
            progressWork = apsWorkInProgressInventoryService.getById(req.getProgressId());
            if (ObjectUtil.isNotEmpty(progressWork.getPlanmaterialid())) {
                return R.error("所选在制品已被其他备料计划使用");
            }
        }

        //提交工艺路线的校验
        if (checkLevel == 2) {
            if (!apsBlankOrderDo.getBlankoutmaterspecification().equals(apsBlankOrderDo.getProcessoutmaterspecification())) {
                return R.error("工艺路线输出物料规格" + apsBlankOrderDo.getProcessoutmaterspecification() + "与坯料输出物料规格" + apsBlankOrderDo.getBlankoutmaterspecification() + "不一致");
            }
        }

        if (checkLevel == 3) {
            //============校验工艺路线=============
            //如果坯料需求的坯料输出成品字段值不等于坯料需求的工艺输出成品字段值，错误提示：工艺路线输出成品与坯料输出成品不一致
            if (!apsBlankOrderDo.getOutputfinishproduct().equals(apsBlankOrderDo.getOutputmaterial())) {
                return R.error("工艺路线输出成品" + apsBlankOrderDo.getOutputmaterial() + "与坯料输出成品" + apsBlankOrderDo.getOutputfinishproduct() + "不一致");
            }
        }

        if (checkLevel == 2 || checkLevel == 3) {
            //新需求：如果坯料需求的坯料来源不是新料，并且首道工序作业的是否坯料工序=是，错误提示：坯料不是新料，不可以包含坯料工序：{首道工序作业的工序}
            //旧需求：如果坯料需求的坯料来源=备料，并且首道工序作业的是否坯料工序=是，错误提示：备料工艺路线首道工序作业不应该包含坯料工序：{首道工序作业的工序}
            //是否备料
            if (!apsBlankOrderDo.getBlankcomefrom().equals("新料")) {
                //查询坯料计划的工序作业
                List<ApsProcessOperationDo> processOperationList = apsProcessOperationService.list(new QueryWrapper<ApsProcessOperationDo>().lambda().eq(ApsProcessOperationDo::getBlankid, apsBlankOrderDo.getId()));
                for (ApsProcessOperationDo processOperationDo : processOperationList) {
                    //没有前道工序作业ID
                    if (ObjectUtil.isEmpty(processOperationDo.getPreviousprocessid())) {
                        if (processOperationDo.getIfblankprocess().equals("是")) {
                            return R.error("坯料不是新料，不可以包含坯料工序：" + processOperationDo.getProcess());
                        }
                        break;
                    }
                }
            }
        }

        //校验是否有工序作业
        if (checkLevel == 2) {
            List<ApsProcessOperationDo> processOperationDos = apsProcessOperationService.list(new QueryWrapper<ApsProcessOperationDo>().lambda().eq(ApsProcessOperationDo::getBlankid, apsBlankOrderDo.getId()));
            if (processOperationDos.size() <= 0) {
                return R.error("请选择工艺路线");
            }
            //============校验工序作业=============
            Map<String, Object> params = new HashMap<>();
            params.put("BLANKID", apsBlankOrderDo.getId());
            R r = apsProcessOperationService.checkOperation(params);
            if (!(r.getCode().equals("200"))) {
                return R.error(r.getMsg());
            }
        }

        if (checkLevel == 3) {
            //============校验工序作业=============
            Map<String, Object> params = new HashMap<>();
            params.put("BLANKID", apsBlankOrderDo.getId());
            R r = apsProcessOperationService.checkOperation(params);
            if (!(r.getCode().equals("200"))) {
                return R.error(r.getMsg());
            }
            //赋值坯料计划冗余字段 是否待领料
            String isWaitGetMaterial = this.baseMapper.selectIsWaitGetMaterialById(apsBlankOrderDo.getId());
            apsBlankOrderDo.setIswaitgetmaterial(isWaitGetMaterial);
        }

        //更新或保存坯料计划和输出成品
        apsBlankOrderDo.setTenantid(tenantId);

        //判断是否是新增
        int count = this.count(new QueryWrapper<ApsBlankOrderDo>().lambda().eq(ApsBlankOrderDo::getId, apsBlankOrderDo.getId()));
        if (count != 0) {
            LambdaUpdateWrapper<ApsBlankOrderDo> blankUpdateWrapper = new UpdateWrapper<ApsBlankOrderDo>().lambda();
            blankUpdateWrapper.eq(ApsBlankOrderDo::getId, apsBlankOrderDo.getId());

            blankUpdateWrapper.set(ApsBlankOrderDo::getProductionorderid, apsBlankOrderDo.getProductionorderid());
            blankUpdateWrapper.set(ApsBlankOrderDo::getBlanknumber, apsBlankOrderDo.getBlanknumber());
            blankUpdateWrapper.set(ApsBlankOrderDo::getMaterialname, apsBlankOrderDo.getMaterialname());
            blankUpdateWrapper.set(ApsBlankOrderDo::getMaterialcode, apsBlankOrderDo.getMaterialcode());
            blankUpdateWrapper.set(ApsBlankOrderDo::getBlanktype, apsBlankOrderDo.getBlanktype());
            blankUpdateWrapper.set(ApsBlankOrderDo::getProducttype, apsBlankOrderDo.getProducttype());
            blankUpdateWrapper.set(ApsBlankOrderDo::getAlloy, apsBlankOrderDo.getAlloy());
            blankUpdateWrapper.set(ApsBlankOrderDo::getAlloystatus, apsBlankOrderDo.getAlloystatus());
            blankUpdateWrapper.set(ApsBlankOrderDo::getThickness, apsBlankOrderDo.getThickness());
            blankUpdateWrapper.set(ApsBlankOrderDo::getProwidth, apsBlankOrderDo.getProwidth());
            blankUpdateWrapper.set(ApsBlankOrderDo::getProlength, apsBlankOrderDo.getProlength());
            blankUpdateWrapper.set(ApsBlankOrderDo::getSinglerollweight, apsBlankOrderDo.getSinglerollweight());
            blankUpdateWrapper.set(ApsBlankOrderDo::getNumberunit, apsBlankOrderDo.getNumberunit());
            blankUpdateWrapper.set(ApsBlankOrderDo::getRollnum, apsBlankOrderDo.getRollnum());
            blankUpdateWrapper.set(ApsBlankOrderDo::getTotalweight, apsBlankOrderDo.getTotalweight());
            blankUpdateWrapper.set(ApsBlankOrderDo::getPreparematerial, apsBlankOrderDo.getPreparematerial());
            blankUpdateWrapper.set(ApsBlankOrderDo::getDefectiveproduct, apsBlankOrderDo.getDefectiveproduct());
            blankUpdateWrapper.set(ApsBlankOrderDo::getPreparematerialbatchno, apsBlankOrderDo.getPreparematerialbatchno());
            blankUpdateWrapper.set(ApsBlankOrderDo::getOutputorderproduct, apsBlankOrderDo.getOutputorderproduct());
            blankUpdateWrapper.set(ApsBlankOrderDo::getOutputfinishproduct, apsBlankOrderDo.getOutputfinishproduct());
            blankUpdateWrapper.set(ApsBlankOrderDo::getOutputmaterial, apsBlankOrderDo.getOutputmaterial());
            blankUpdateWrapper.set(ApsBlankOrderDo::getProductstatus, apsBlankOrderDo.getProductstatus());
            blankUpdateWrapper.set(ApsBlankOrderDo::getTenantid, apsBlankOrderDo.getTenantid());
            blankUpdateWrapper.set(ApsBlankOrderDo::getRoption, apsBlankOrderDo.getRoption());
            blankUpdateWrapper.set(ApsBlankOrderDo::getDeleted, apsBlankOrderDo.getDeleted());
            blankUpdateWrapper.set(ApsBlankOrderDo::getRemark, apsBlankOrderDo.getRemark());
            blankUpdateWrapper.set(ApsBlankOrderDo::getCreatedate, apsBlankOrderDo.getCreatedate());
            blankUpdateWrapper.set(ApsBlankOrderDo::getCreateuserid, apsBlankOrderDo.getCreateuserid());
            blankUpdateWrapper.set(ApsBlankOrderDo::getModifydate, apsBlankOrderDo.getModifydate());
            blankUpdateWrapper.set(ApsBlankOrderDo::getModifyuserid, apsBlankOrderDo.getModifyuserid());
            blankUpdateWrapper.set(ApsBlankOrderDo::getCreateusername, apsBlankOrderDo.getCreateusername());
            blankUpdateWrapper.set(ApsBlankOrderDo::getModifyusername, apsBlankOrderDo.getModifyusername());
            blankUpdateWrapper.set(ApsBlankOrderDo::getProducttypeid, apsBlankOrderDo.getProducttypeid());
            blankUpdateWrapper.set(ApsBlankOrderDo::getInputreportdescribe, apsBlankOrderDo.getInputreportdescribe());
            blankUpdateWrapper.set(ApsBlankOrderDo::getCraftrouteid, apsBlankOrderDo.getCraftrouteid());
            blankUpdateWrapper.set(ApsBlankOrderDo::getLockmark, apsBlankOrderDo.getLockmark());
            blankUpdateWrapper.set(ApsBlankOrderDo::getIsstag, apsBlankOrderDo.getIsstag());
            blankUpdateWrapper.set(ApsBlankOrderDo::getSchedulingstatus, apsBlankOrderDo.getSchedulingstatus());
            blankUpdateWrapper.set(ApsBlankOrderDo::getPlanhavematerialdate, apsBlankOrderDo.getPlanhavematerialdate());
            blankUpdateWrapper.set(ApsBlankOrderDo::getDeliverydate, apsBlankOrderDo.getDeliverydate());
            blankUpdateWrapper.set(ApsBlankOrderDo::getScheduledatestart, apsBlankOrderDo.getScheduledatestart());
            blankUpdateWrapper.set(ApsBlankOrderDo::getScheduledateend, apsBlankOrderDo.getScheduledateend());
            blankUpdateWrapper.set(ApsBlankOrderDo::getPromisedatestart, apsBlankOrderDo.getPromisedatestart());
            blankUpdateWrapper.set(ApsBlankOrderDo::getPromisedateend, apsBlankOrderDo.getPromisedateend());
            blankUpdateWrapper.set(ApsBlankOrderDo::getPlannedproductionworkshop, apsBlankOrderDo.getPlannedproductionworkshop());
            blankUpdateWrapper.set(ApsBlankOrderDo::getBlankoutmatertype, apsBlankOrderDo.getBlankoutmatertype());
            blankUpdateWrapper.set(ApsBlankOrderDo::getBlankoutmaterspecification, apsBlankOrderDo.getBlankoutmaterspecification());
            blankUpdateWrapper.set(ApsBlankOrderDo::getProcessoutmaterspecification, apsBlankOrderDo.getProcessoutmaterspecification());
            blankUpdateWrapper.set(ApsBlankOrderDo::getRouteproducttypeid, apsBlankOrderDo.getRouteproducttypeid());
            blankUpdateWrapper.set(ApsBlankOrderDo::getRoutemetal, apsBlankOrderDo.getRoutemetal());
            blankUpdateWrapper.set(ApsBlankOrderDo::getRoutemetalstate, apsBlankOrderDo.getRoutemetalstate());
            blankUpdateWrapper.set(ApsBlankOrderDo::getRoutethickstr, apsBlankOrderDo.getRoutethickstr());
            blankUpdateWrapper.set(ApsBlankOrderDo::getRoutethickend, apsBlankOrderDo.getRoutethickend());
            blankUpdateWrapper.set(ApsBlankOrderDo::getRoutewidthstr, apsBlankOrderDo.getRoutewidthstr());
            blankUpdateWrapper.set(ApsBlankOrderDo::getRoutewidthend, apsBlankOrderDo.getRoutewidthend());
            blankUpdateWrapper.set(ApsBlankOrderDo::getRoutelengthstr, apsBlankOrderDo.getRoutelengthstr());
            blankUpdateWrapper.set(ApsBlankOrderDo::getRoutelengthend, apsBlankOrderDo.getRoutelengthend());
            blankUpdateWrapper.set(ApsBlankOrderDo::getBlankappearance, apsBlankOrderDo.getBlankappearance());
            blankUpdateWrapper.set(ApsBlankOrderDo::getMaxheatroll, apsBlankOrderDo.getMaxheatroll());
            blankUpdateWrapper.set(ApsBlankOrderDo::getBlankcomefrom, apsBlankOrderDo.getBlankcomefrom());
            blankUpdateWrapper.set(ApsBlankOrderDo::getUpdateprocess, apsBlankOrderDo.getUpdateprocess());
            blankUpdateWrapper.set(ApsBlankOrderDo::getExpecteddays, apsBlankOrderDo.getExpecteddays());
            blankUpdateWrapper.set(ApsBlankOrderDo::getIssubmitprocessroute, apsBlankOrderDo.getIssubmitprocessroute());
            blankUpdateWrapper.set(ApsBlankOrderDo::getIswaitgetmaterial, apsBlankOrderDo.getIswaitgetmaterial());

            this.update(blankUpdateWrapper);
        } else {
            this.save(apsBlankOrderDo);
        }
        //根据坯料计划更新订单的 计划排产车间字段
        String planEndProductionWorkshops = this.baseMapper.selectPlanEndProductionWorkshopsById(apsBlankOrderDo.getProductionorderid());
        if (ObjectUtil.isNotEmpty(planEndProductionWorkshops)) {
            HashSet<String> hashSet = new HashSet<>(Arrays.asList(planEndProductionWorkshops.split(",")));
            ApsProductionOrderDo apsProductionOrderDo = new ApsProductionOrderDo();
            apsProductionOrderDo.setId(apsBlankOrderDo.getProductionorderid());
            apsProductionOrderDo.setPlannedproductionworkshop(String.join(",", hashSet));
            apsProductionOrderService.updateById(apsProductionOrderDo);
        }

        for (ApsProcessOutputProductDo apsProcessOutputProductDo : apsProcessOutputProductDoList) {
            apsProcessOutputProductDo.setBlankid(apsBlankOrderDo.getId());
            apsProcessOutputProductDo.setTenantid(tenantId);
            if (ObjectUtil.isEmpty(apsProcessOutputProductDo.getChangeproducttype())) {
                apsProcessOutputProductDo.setChangeproducttype("");
            }
            if (ObjectUtil.isEmpty(apsProcessOutputProductDo.getChangeproducttypeid())) {
                apsProcessOutputProductDo.setChangeproducttypeid("");
            }
            if (ObjectUtil.isEmpty(apsProcessOutputProductDo.getChangealloy())) {
                apsProcessOutputProductDo.setChangealloy("");
            }
            if (ObjectUtil.isEmpty(apsProcessOutputProductDo.getChangealloystatus())) {
                apsProcessOutputProductDo.setChangealloystatus("");
            }
        }
        if (ObjectUtil.isNotEmpty(apsProcessOutputProductDoList)) {
            apsProcessOutputProductService.saveOrUpdateBatch(apsProcessOutputProductDoList);
        }

        //如果订单的评审状态为 待排程、待交期评审、待回复交期或已发布计划，将对应的工序作业的输出物料的产品明细ID赋值
        ApsProductionOrderDo apsProductionOrderDo = apsProductionOrderService.getById(apsBlankOrderDo.getProductionorderid());
        if (ObjectUtil.isNotEmpty(apsProductionOrderDo)
                && ObjectUtil.isNotEmpty(apsProductionOrderDo.getAuditstatus())
                && Arrays.asList("待排程", "待交期评审", "待回复交期", "已发布计划").contains(apsProductionOrderDo.getAuditstatus())) {
            List<ApsProcessOperationOutMaterDo> apsProcessOperationOutMaterDoList = apsProcessOperationOutMaterService.list(new LambdaQueryWrapper<ApsProcessOperationOutMaterDo>()
                    .eq(ApsProcessOperationOutMaterDo::getBlankid, apsBlankOrderDo.getId())
                    .ne(ApsProcessOperationOutMaterDo::getOutputfinishproduct, "")
                    .isNotNull(ApsProcessOperationOutMaterDo::getOutputfinishproduct));
            List<ApsProcessOperationOutMaterDo> needUpdateProcessOperationOutMaterList = new LinkedList<>();
            for (ApsProcessOperationOutMaterDo apsProcessOperationOutMaterDo : apsProcessOperationOutMaterDoList) {
                for (ApsProcessOutputProductDo apsProcessOutputProductDo : apsProcessOutputProductDoList) {
                    if (apsProcessOperationOutMaterDo.getOutputfinishproduct().equals(apsProcessOutputProductDo.getOutputresult())) {
                        ApsProcessOperationOutMaterDo needUpdate = new ApsProcessOperationOutMaterDo();
                        needUpdate.setId(apsProcessOperationOutMaterDo.getId());
                        needUpdate.setOrderdetailid(apsProcessOutputProductDo.getProductid());
                        needUpdateProcessOperationOutMaterList.add(needUpdate);
                        break;
                    }
                }
            }
            if (!needUpdateProcessOperationOutMaterList.isEmpty()) {
                apsProcessOperationOutMaterService.updateBatchById(needUpdateProcessOperationOutMaterList);
            }
        }

        //如果在制品不为空，并且为新增备料
        if (ObjectUtil.isNotEmpty(req.getProgressId()) && apsBlankOrderDo.getPreparematerial().equals("是")) {
            //将对应备料库（在制品库存表【aps_work_in_progress_inventory表】）中的备料计划ID（PLANMATERIALID）字段设置为坯料计划的ID
//            ApsWorkInProgressInventoryDo apsWorkInProgressInventoryDo = new ApsWorkInProgressInventoryDo();
//            apsWorkInProgressInventoryDo.setId(req.getProgressId());
//            apsWorkInProgressInventoryDo.setPlanmaterialid(apsBlankOrderDo.getId());
//            apsWorkInProgressInventoryDo.setWorkinprocessstatus(SaveConstans.WorkInProgressStatus.WAIT_WORK);
//            apsWorkInProgressInventoryService.updateById(apsWorkInProgressInventoryDo);
            apsWorkInProgressInventoryService.update(new UpdateWrapper<ApsWorkInProgressInventoryDo>().lambda()
                    .set(ApsWorkInProgressInventoryDo::getPlanmaterialid, apsBlankOrderDo.getId())
                    .setSql("PREVIOUSWORKINPROCESSSTATUS = WORKINPROCESSSTATUS")
                    .set(ApsWorkInProgressInventoryDo::getWorkinprocessstatus, SaveConstans.WorkInProgressStatus.WAIT_WORK)
                    .eq(ApsWorkInProgressInventoryDo::getDeleted, "0")
                    .eq(ApsWorkInProgressInventoryDo::getId, req.getProgressId()));

            //借调逻辑
            borrowBlank(progressWork);
        }

        return R.ok();
    }

    public R checkBlankOrder(CheckAndSaveOrUpdateBlankReq req, int j) {
        //获取坯料计划内容
        ApsBlankOrderDo apsBlankOrderDo = req.getApsBlankOrderDo();

        //校验坯料计划的必填项
        R checkRes = checkBlankMustWrite(req);
        if (!checkRes.getCode().equals("200")) {
            return R.error("坯料计划" + apsBlankOrderDo.getBlanknumber() + " " + checkRes.getMsg());
        }

        //获取校验等级
        Integer checkLevel = req.getCheckLevel();

        //获取当前用户信息
        //获取当前用户的所属工厂
        SysUserVo currentUser = CXCommonUtils.getCurrentUser();
        String tenantId = CXCommonUtils.getCurrentUserFactoryId(currentUser);


        //=============校验坯料长度=================
        //如果坯料需求的外观类型是板锭或铝板，并且坯料长度为空，则提示：坯料是板锭和铝板时长度必填
        if (ObjectUtil.isNotEmpty(apsBlankOrderDo.getBlankappearance())
                && Arrays.asList(SaveConstans.BlankAppearanceType.ALUMINUM_PLATE, SaveConstans.BlankAppearanceType.ALUMINUM_INGOT).contains(apsBlankOrderDo.getBlankappearance())
                && ObjectUtil.isEmpty(apsBlankOrderDo.getProlength())) {
            return R.error("坯料计划" + apsBlankOrderDo.getBlanknumber() + " 坯料是铝板和铝锭时长度必填");
        }
//        //如果坯料产品类型对应产品外观类型是铝板或板锭，并且坯料长度为空，则提示：坯料是铝板和板锭时长度必填
//        if (ObjectUtil.isNotEmpty(apsBlankOrderDo.getProducttypeid())) {
//            //目前数据字典存在3层关系，选择只能选择最后一层，二开查询最后一层的父节点对应数据的父节点，判断是否为铝板或板锭的主键ID
//            String pid = this.baseMapper.selectDictAboutProductType(apsBlankOrderDo.getProducttypeid());
//            //如果是铝板或板锭
//            if (ObjectUtil.isNotEmpty(pid) && (pid.equals(SaveConstans.ProductionType.BANDING_ID) || pid.equals(SaveConstans.ProductionType.LVBAN_ID))) {
//                //坯料长度为空
//                if (ObjectUtil.isEmpty(apsBlankOrderDo.getProlength())) {
//                    return R.error("坯料计划" + apsBlankOrderDo.getBlanknumber() + " 坯料是铝板和板锭时长度必填");
//                }
//            }
//        }
        if (checkLevel == 3) {
            //==============校验交期==================
            /**
             * 如果有评审日志，并且变更内容为空，则错误提示：请填写变更内容(暂未实现)
             * 如果有任一坯料计划的承诺交货期-起或止为空，错误提示：请填写所有坯料计划的承诺交货期
             * 如果有坯料计划的承诺交货期-起<排程交货期-起，错误提示：第{几}个坯料计划的承诺交货期-起不能早于排程交货期-起{排程交货期}
             * 如果有坯料计划的承诺交货期-止 < 排程交货期，错误提示：第{几}个坯料计划的承诺交货期-止不能早于排程交货期{排程交货期}
             */
            if (req.getCheckDevice()) {
                if (ObjectUtil.isEmpty(apsBlankOrderDo.getPromisedatestart()) || ObjectUtil.isEmpty(apsBlankOrderDo.getPromisedateend())) {
                    return R.error("请填写所有坯料计划的承诺交货期");
                }
                if (ObjectUtil.isEmpty(apsBlankOrderDo.getScheduledateend())) {
                    return R.error("坯料计划" + apsBlankOrderDo.getBlanknumber() + " 排程交货期-止不能为空");
                }
                if (apsBlankOrderDo.getPromisedatestart().compareTo(apsBlankOrderDo.getScheduledatestart()) < 0) {
                    return R.error("坯料计划" + apsBlankOrderDo.getBlanknumber() + "的承诺交货期-起不能早于排程交货期-起" + DateUtil.format(apsBlankOrderDo.getScheduledatestart(), "yyyy-MM-dd"));
                }
                if (apsBlankOrderDo.getPromisedateend().compareTo(apsBlankOrderDo.getScheduledateend()) < 0) {
                    return R.error("坯料计划" + apsBlankOrderDo.getBlanknumber() + "的承诺交货期-止不能早于排程交货期-止" + DateUtil.format(apsBlankOrderDo.getScheduledateend(), "yyyy-MM-dd"));
                }
            }
        }
        Collator collator = Collator.getInstance(Locale.US);
        if (checkLevel == 2 || checkLevel == 3) {
            //==============拼接工艺路线输出成品==============
            //查询坯料计划对应工艺路线的输出成品
            List<String> processOutputList = this.baseMapper.selectProcessByBlankId(apsBlankOrderDo.getId());
            if (processOutputList != null && processOutputList.size() > 0) {
                //工艺路线输出成品
                String outputProcessMaterial = "";
                //排序
                processOutputList.sort(collator);
                for (int i = 0; i < processOutputList.size(); i++) {
                    outputProcessMaterial += "[" + (i + 1) + "]" + processOutputList.get(i);
                    if (i < processOutputList.size() - 1) {
                        outputProcessMaterial += ",\r\n";
                    }
                }
                apsBlankOrderDo.setOutputmaterial(outputProcessMaterial);

                //工艺输出物料规格
                String processOutputMaterialSpecification = "";
                List<String> processOutputMaterialSpecificationList = processOutputList.stream().map(item -> {
                    String[] s = item.split(" ");
                    return s[0] + " " + s[1];
                }).sorted(collator).collect(Collectors.toList());
                for (int i = 0; i < processOutputMaterialSpecificationList.size(); i++) {
                    processOutputMaterialSpecification += "[" + (i + 1) + "]" + processOutputMaterialSpecificationList.get(i);
                    if (i < processOutputMaterialSpecificationList.size() - 1) {
                        processOutputMaterialSpecification += ",\r\n";
                    }
                }
                apsBlankOrderDo.setProcessoutmaterspecification(processOutputMaterialSpecification);


            } else {
                return R.error("坯料计划" + apsBlankOrderDo.getBlanknumber() + " 工艺路线没有输出成品");
            }
        }

        //=============校验输出成品==============
        //产品明细
        List<ApsProductDetailDo> apsProductDetailDoList = req.getApsProductDetailDoList();
        //输出成品
        List<ApsProcessOutputProductDo> apsProcessOutputProductDoList = req.getApsProcessOutputProductDoList();

        //如果坯料计划未开工(生产状态<=30)
        if (apsBlankOrderDo.getProductstatus().compareTo(TO_BE_STARTED) <= 0) {
            for (ApsProcessOutputProductDo apsProcessOutputProductDo : apsProcessOutputProductDoList) {
                boolean haveProductDetail = false;
                for (ApsProductDetailDo apsProductDetailDo : apsProductDetailDoList) {
                    //判断是否存在对应的产品明细
                    if (apsProcessOutputProductDo.getProductid().equals(apsProductDetailDo.getId())) {
                        haveProductDetail = true;

                        if (apsProductDetailDo.getInputmaterialdescription().equals(apsProcessOutputProductDo.getProductname())) {
//                            //如果输出单卷重<单卷重（起）或 输出单卷重>单卷重（止），错误提示：输出成品：{订单产品} 输出单卷重不符合订单要求
//                            if (apsProcessOutputProductDo.getOutputonerollweigth().compareTo(apsProductDetailDo.getSinglerollweightstart()) < 0
//                                    || apsProcessOutputProductDo.getOutputonerollweigth().compareTo(apsProductDetailDo.getSinglerollweightend()) > 0) {
//                                return R.error("坯料计划" + apsBlankOrderDo.getBlanknumber() + " 输出成品：" + apsProcessOutputProductDo.getProductname() + " 输出单卷重不符合订单要求");
//                            }
                        } else {//如果对应订单产品明细的订单产品字段值不等于输出成品的订单产品字段值，错误提示：输出成品：{订单产品} 对应订单产品已修改或已删除，请重新选择订单产品
                            return R.error("坯料计划" + apsBlankOrderDo.getBlanknumber() + " 输出成品：" + apsProcessOutputProductDo.getProductname() + " 对应订单产品已修改或已删除，请重新选择订单产品");
                        }
                        break;
                    }
                }
                //存在对应的产品明细
                if (haveProductDetail) {
                    //如果输出单卷重>坯料单卷重，错误提示：输出单卷重不能超过坯料单卷重
                    //（注意：成品重量和坯料重量单位吨和公斤需要转换后比较）
                    //转化单卷重 单位统一为 公斤（kg）

                    //输出的单卷重
                    BigDecimal outputWeight = new BigDecimal(0);
                    //坯料的单卷重
                    BigDecimal blankWeight = new BigDecimal(0);
                    outputWeight = apsProcessOutputProductDo.getOutputonerollweigth();
                    blankWeight = apsBlankOrderDo.getSinglerollweight();
                    //统一输出的重量
//                    //公斤/千克
//                    if (apsProcessOutputProductDo.getCountunit().equals(SaveConstans.CountUnit.KG)) {
//                        outputWeight = apsProcessOutputProductDo.getOutputonerollweigth();
//                    }
//                    //吨
//                    if (apsProcessOutputProductDo.getCountunit().equals(SaveConstans.CountUnit.TON)) {
//                        outputWeight = apsProcessOutputProductDo.getOutputonerollweigth().multiply(new BigDecimal(1000));
//                    }
//
//                    //统一坯料的重量
//                    if (apsBlankOrderDo.getNumberunit().equals(SaveConstans.CountUnit.KG)) {
//                        blankWeight = apsBlankOrderDo.getSinglerollweight();
//                    }
//                    if (apsBlankOrderDo.getNumberunit().equals(SaveConstans.CountUnit.TON)) {
//                        blankWeight = apsBlankOrderDo.getSinglerollweight().multiply(new BigDecimal(1000));
//                    }
                    //输出单卷重>坯料单卷重
                    if (outputWeight.compareTo(blankWeight) > 0) {
                        return R.error("坯料计划" + apsBlankOrderDo.getBlanknumber() + " 输出成品：" + apsProcessOutputProductDo.getProductname() + " 输出单卷重不能超过坯料单卷重");
                    }
                } else {//不存在对应的产品明细
                    return R.error("坯料计划" + apsBlankOrderDo.getBlanknumber() + " 输出成品：" + apsProcessOutputProductDo.getProductname() + " 对应订单产品已修改或已删除，请重新选择订单产品");
                }
            }
        }

        //不论坯料计划的状态，需要增加的判断
        for (ApsProcessOutputProductDo apsProcessOutputProductDo : apsProcessOutputProductDoList) {
            boolean haveProductDetail = false;
            for (ApsProductDetailDo apsProductDetailDo : apsProductDetailDoList) {
                //判断是否存在对应的产品明细
                if (apsProcessOutputProductDo.getProductid().equals(apsProductDetailDo.getId())) {
                    haveProductDetail = true;
                    //计划输出订单重量 > 对应订单产品订单总数量；错误提示：输出成品 {订单产品} 计划输出订单重量 不能大于 订单总数量
//                    if (apsProcessOutputProductDo.getPlanoutputonerollweight().compareTo(apsProductDetailDo.getTotalordercount()) > 0) {
//                        return R.error("坯料计划" + apsBlankOrderDo.getBlanknumber() + " 输出成品：" + apsProcessOutputProductDo.getProductname() + " 计划输出订单重量 不能大于 订单总数量");
//                    }
                }
            }
            if (!haveProductDetail && apsProcessOutputProductDo.getPlanoutputonerollweight().compareTo(new BigDecimal(0)) > 0) {
                return R.error("坯料计划" + apsBlankOrderDo.getBlanknumber() + " 输出成品：" + apsProcessOutputProductDo.getProductname() + " 已修改或已删除，并已开工，计划输出订单重量 不能大于 0");
            }

            //如果输出卷数不是坯料卷数的倍数，错误提示:“输出成品:{订单产品}输出卷数必须是坏料卷数的倍数”
            if (ObjectUtil.isNotEmpty(apsProcessOutputProductDo.getPlanoutputorderroll()) && ObjectUtil.isNotEmpty(apsBlankOrderDo.getRollnum())) {
                if (apsProcessOutputProductDo.getPlanoutputorderroll() % apsBlankOrderDo.getRollnum() != 0) {
                    return R.error("坯料计划" + apsBlankOrderDo.getBlanknumber() + " 输出成品：" + apsProcessOutputProductDo.getProductname() + " 输出卷数必须是坯料卷数的倍数");
                }
            }

            //如果输出成品技术要求的坯料外观类型是铝板或铝锭，并且长度为空，则提示：输出成品是板锭和铝板时长度必填
            if (ObjectUtil.isNotEmpty(apsProcessOutputProductDo.getBlankappearance())
                    && Arrays.asList(SaveConstans.BlankAppearanceType.ALUMINUM_PLATE, SaveConstans.BlankAppearanceType.ALUMINUM_INGOT).contains(apsProcessOutputProductDo.getBlankappearance())
                    && ObjectUtil.isEmpty(apsProcessOutputProductDo.getProlength())) {
                return R.error("坯料计划" + apsBlankOrderDo.getBlanknumber() + " 输出成品：" + apsProcessOutputProductDo.getProductname() + " 是板锭和铝板时长度必填");
            }
        }

        /**
         * •如果坯料输出成品技术要求的坯料类型与当前坯料需求的不同，
         * 错误提示：坯料类型{坯料类型}与订单产品的技术要求的坯料类型{坯料类型}不同，不可作为该坯料的输出成品
         * •如果坯料输出成品技术要求的坯料产品类型与当前坯料需求的不同，
         * 错误提示：坯料需求的产品类型{产品类型}与订单产品的技术要求的坯料产品类型{坯料产品类型}不同，不可作为该坯料的输出成品
         */
        for (ApsProcessOutputProductDo apsProcessOutputProductDo : apsProcessOutputProductDoList) {
            if (!apsProcessOutputProductDo.getBlanktype().equals(apsBlankOrderDo.getBlanktype())) {
                return R.error("坯料计划" + apsBlankOrderDo.getBlanknumber() + " 输出成品：" + apsProcessOutputProductDo.getProductname() + " 坯料类型" + apsBlankOrderDo.getBlanktype() + "与订单产品的技术要求的坯料类型" + apsProcessOutputProductDo.getBlanktype() + "不同，不可作为该坯料的输出成品");
            }
            if (!apsProcessOutputProductDo.getBlankproducttype().equals(apsBlankOrderDo.getProducttype())) {
                return R.error("坯料计划" + apsBlankOrderDo.getBlanknumber() + " 输出成品：" + apsProcessOutputProductDo.getProductname() + " 坯料需求的产品类型" + apsBlankOrderDo.getProducttype() + "与订单产品的技术要求的坯料产品类型" + apsProcessOutputProductDo.getBlankproducttype() + "不同，不可作为该坯料的输出成品");
            }
        }

        //如果存在订单产品和输出单卷重都相同坯料输出成品，错误提示：以下坯料输出成品重复：{订单产品1}{输出单卷重}{数量单位}，{订单产品2}{输出单卷重}{数量单位}
        //新需求：如果存在关联相同的订单产品，并且输出单卷重都相同坯料输出成品，错误提示：以下坯料输出成品重复：{订单产品1}{输出单卷重}{数量单位}，{订单产品2}{输出单卷重}{数量单位}
        //用于判断是否重复
        Map<String, String> checkOutputDesMap = new HashMap<>();
        //有重复数据订单产品的数组
        List<String> endCheckOutputDesList = new LinkedList<>();
        for (ApsProcessOutputProductDo apsProcessOutputProductDo : apsProcessOutputProductDoList) {
            //产品明细的ID
            String productid = apsProcessOutputProductDo.getProductid();

            String unit = "吨";
            if (apsProcessOutputProductDo.getCountunit().equals(SaveConstans.CountUnit.KG)) {
                unit = "千克";
            }
            //拼接字段
            String s = apsProcessOutputProductDo.getProductname() + apsProcessOutputProductDo.getOutputonerollweigth() + unit;

            if (checkOutputDesMap.containsKey(productid) && checkOutputDesMap.get(productid).equals(s)) {
                endCheckOutputDesList.add(s);
            } else {
                checkOutputDesMap.put(productid, s);
            }
        }
        List<String> collect = endCheckOutputDesList.stream().distinct().collect(Collectors.toList());
        if (!collect.isEmpty()) {
            String join = String.join(",", collect);
            return R.error("坯料计划" + apsBlankOrderDo.getBlanknumber() + " 以下坯料输出成品重复：" + join);
        }

        //如果坯料输出成品的计划输出订单重量合计>坯料的总重量，错误提示：坯料输出成品的计划输出订单重量合计不能大于坯料的总重量
        //计算输出成品的总计划输出重量
        BigDecimal planAll = new BigDecimal("0");
        for (ApsProcessOutputProductDo apsProcessOutputProductDo : apsProcessOutputProductDoList) {
            BigDecimal planoutputonerollweight = apsProcessOutputProductDo.getPlanoutputonerollweight();
            planAll = planAll.add(planoutputonerollweight);
        }
        //坯料的总重量
        BigDecimal totalweight = apsBlankOrderDo.getTotalweight();
        if (planAll.compareTo(totalweight) > 0) {//如果坯料输出成品的计划输出订单重量合计>坯料的总重量
            return R.error("坯料计划" + apsBlankOrderDo.getBlanknumber() + " 坯料输出成品的计划输出订单重量合计不能大于坯料的总重量");
        }


        //提交工艺路线的校验
        if (checkLevel == 2) {
            if (!apsBlankOrderDo.getBlankoutmaterspecification().equals(apsBlankOrderDo.getProcessoutmaterspecification())) {
                return R.error("坯料计划" + apsBlankOrderDo.getBlanknumber() + " 工艺路线输出物料规格" + apsBlankOrderDo.getProcessoutmaterspecification() + "与坯料输出物料规格" + apsBlankOrderDo.getBlankoutmaterspecification() + "不一致");
            }
        }


        if (checkLevel == 3) {
            //============校验工艺路线=============
            //如果坯料需求的坯料输出成品字段值不等于坯料需求的工艺输出成品字段值，错误提示：工艺路线输出成品与坯料输出成品不一致
            if (!apsBlankOrderDo.getOutputfinishproduct().equals(apsBlankOrderDo.getOutputmaterial())) {
                return R.error("坯料计划" + apsBlankOrderDo.getBlanknumber() + " 工艺路线输出成品" + apsBlankOrderDo.getOutputmaterial() + "与坯料输出成品" + apsBlankOrderDo.getOutputfinishproduct() + "不一致");
            }
        }
        if (checkLevel == 2 || checkLevel == 3) {
            //新需求：如果坯料需求的坯料来源不是新料，并且首道工序作业的是否坯料工序=是，错误提示：坯料不是新料，不可以包含坯料工序：{首道工序作业的工序}
            //旧需求：如果坯料需求的坯料来源=备料，并且首道工序作业的是否坯料工序=是，错误提示：备料工艺路线首道工序作业不应该包含坯料工序：{首道工序作业的工序}
            //是否备料
            if (!apsBlankOrderDo.getBlankcomefrom().equals("新料")) {
                //查询坯料计划的工序作业
                List<ApsProcessOperationDo> processOperationList = apsProcessOperationService.list(new QueryWrapper<ApsProcessOperationDo>().lambda().eq(ApsProcessOperationDo::getBlankid, apsBlankOrderDo.getId()));
                for (ApsProcessOperationDo processOperationDo : processOperationList) {
                    //没有前道工序作业ID
                    if (ObjectUtil.isEmpty(processOperationDo.getPreviousprocessid())) {
                        if (processOperationDo.getIfblankprocess().equals("是")) {
                            return R.error("坯料计划" + apsBlankOrderDo.getBlanknumber() + " 坯料不是新料，不可以包含坯料工序：" + processOperationDo.getProcess());
                        }
                        break;
                    }
                }
            }
        }

        //校验是否有工序作业
        if (checkLevel == 2) {
            List<ApsProcessOperationDo> processOperationDos = apsProcessOperationService.list(new QueryWrapper<ApsProcessOperationDo>().lambda().eq(ApsProcessOperationDo::getBlankid, apsBlankOrderDo.getId()));
            if (processOperationDos.size() <= 0) {
                return R.error("坯料计划" + apsBlankOrderDo.getBlanknumber() + " 请选择工艺路线");
            }
            //============校验工序作业=============
            Map<String, Object> params = new HashMap<>();
            params.put("BLANKID", apsBlankOrderDo.getId());
            R r = apsProcessOperationService.checkOperation(params);
            if (!(r.getCode().equals("200"))) {
                return R.error(r.getMsg());
            }
        }

        if (checkLevel == 3) {
            //============校验工序作业=============
            Map<String, Object> params = new HashMap<>();
            params.put("BLANKID", apsBlankOrderDo.getId());
            if (req.getCheckDevice()) {
                params.put("checkDeviceAndOrgCode", true);
            }
            R r = apsProcessOperationService.checkOperation(params);
            if (!(r.getCode().equals("200"))) {
                return R.error("坯料计划" + apsBlankOrderDo.getBlanknumber() + " " + r.getMsg());
            }
        }
        return R.ok();
    }

    private R checkBlankMustWrite(CheckAndSaveOrUpdateBlankReq req) {
        ApsBlankOrderDo apsBlankOrderDo = req.getApsBlankOrderDo();
        List<ApsProcessOutputProductDo> apsProcessOutputProductDoList = req.getApsProcessOutputProductDoList();

        if (ObjectUtil.isEmpty(apsBlankOrderDo.getBlanknumber())) {
            return R.error("坯料计划编号不能为空");
        }
        if (ObjectUtil.isEmpty(apsBlankOrderDo.getMaterialname())) {
            return R.error("物料名称不能为空");
        }
        if (ObjectUtil.isEmpty(apsBlankOrderDo.getBlanktype())) {
            return R.error("坯料类型不能为空");
        }
        if (ObjectUtil.isEmpty(apsBlankOrderDo.getProducttype())) {
            return R.error("产品类型不能为空");
        }
        if (ObjectUtil.isEmpty(apsBlankOrderDo.getAlloy())) {
            return R.error("合金不能为空");
        }
        if (ObjectUtil.isEmpty(apsBlankOrderDo.getThickness())) {
            return R.error("厚度不能为空");
        }
        if (ObjectUtil.isEmpty(apsBlankOrderDo.getProwidth())) {
            return R.error("宽度不能为空");
        }
        if (ObjectUtil.isEmpty(apsBlankOrderDo.getSinglerollweight())) {
            return R.error("单卷重不能为空");
        }
        if (ObjectUtil.isEmpty(apsBlankOrderDo.getNumberunit())) {
            return R.error("数量单位不能为空");
        }
        if (ObjectUtil.isEmpty(apsBlankOrderDo.getRollnum())) {
            return R.error("卷数不能为空");
        }
        if (ObjectUtil.isEmpty(apsBlankOrderDo.getBlankappearance())) {
            return R.error("坯料外观类型不能为空");
        }
        if (ObjectUtil.isNotEmpty(apsProcessOutputProductDoList)) {
            for (ApsProcessOutputProductDo apsProcessOutputProductDo : apsProcessOutputProductDoList) {
                if (ObjectUtil.isEmpty(apsProcessOutputProductDo.getProducttype())
                        || ObjectUtil.isEmpty(apsProcessOutputProductDo.getOutputonerollweigth())
                        || ObjectUtil.isEmpty(apsProcessOutputProductDo.getPlanoutputorderroll())
                        || ObjectUtil.isEmpty(apsProcessOutputProductDo.getHadrollcount())) {
                    return R.error("输出成品的必填项未填");
                }
            }
        } else {
            return R.error("输出成品必填");
        }
        return R.ok();
    }

    @Override
    @Transactional
    public R removeBlankOrder(ApsBlankOrderDo req) {
        if (ObjectUtil.isEmpty(req.getId())) {
            return R.error("坯料Id不能为空");
        }
        ApsBlankOrderDo apsBlankOrderDo = this.getById(req.getId());

        //如果是否备料=是，备料对应在制品的备料计划ID=(空)，在制品状态=备料中
        if (ObjectUtil.isNotEmpty(apsBlankOrderDo.getPreparematerialbatchno())) {
//            apsWorkInProgressInventoryService.update(new UpdateWrapper<ApsWorkInProgressInventoryDo>().lambda()
//                    .set(ApsWorkInProgressInventoryDo::getPlanmaterialid, "")
//                    .set(ApsWorkInProgressInventoryDo::getWorkinprocessstatus, SaveConstans.WorkInProgressStatus.WAITING)
//                    .eq(ApsWorkInProgressInventoryDo::getBlankid, req.getId()));
            List<String> batchNumberList = Arrays.asList(apsBlankOrderDo.getPreparematerialbatchno().split(","));
            apsWorkInProgressInventoryService.update(new UpdateWrapper<ApsWorkInProgressInventoryDo>().lambda()
                    .set(ApsWorkInProgressInventoryDo::getPlanmaterialid, null)
                    .setSql("WORKINPROCESSSTATUS = IF(PREVIOUSWORKINPROCESSSTATUS = '待加工','备料中',PREVIOUSWORKINPROCESSSTATUS)")
                    .eq(ApsWorkInProgressInventoryDo::getDeleted, "0")
                    .in(ApsWorkInProgressInventoryDo::getBatchnumber, batchNumberList));
            //更新坯料计划该字段为空
            apsBlankOrderService.update(new UpdateWrapper<ApsBlankOrderDo>().lambda()
                    .set(ApsBlankOrderDo::getPreparematerialbatchno, null)
                    .eq(ApsBlankOrderDo::getId, apsBlankOrderDo.getId()));
        }
        removeBlankOrderAndChild(apsBlankOrderDo);

        //物理删除相关的工序作业备份
        this.baseMapper.deleteProcessBackupByBlankId(req.getId());
        //物理删除相关的作业明细备份
        this.baseMapper.deleteProcessEquBackupByBlankId(req.getId());

        //查询此订单的其他任一一个坯料计划的Id，重新更新订单的状态
        CommonUpdateProductionStatusReq commonUpdateProductionStatusReq = new CommonUpdateProductionStatusReq();
        List<ApsBlankOrderDo> list = this.list(new QueryWrapper<ApsBlankOrderDo>().lambda().eq(ApsBlankOrderDo::getProductionorderid, apsBlankOrderDo.getProductionorderid()));
        if (ObjectUtil.isNotEmpty(list)) {
            commonUpdateProductionStatusReq.setBlankId(list.get(0).getId());
            apsProcessOperationProcessEquService.updateProductionStatus(commonUpdateProductionStatusReq);
        } else {
            //更新订单生产状态为“待排程10”
            apsProductionOrderService.update(new LambdaUpdateWrapper<ApsProductionOrderDo>()
                    .eq(ApsProductionOrderDo::getId, apsBlankOrderDo.getProductionorderid())
                    .set(ApsProductionOrderDo::getProductionstatus, TO_BE_SCHEDULED));
            //更新订单的产品明细的生产状态为“待排程10”
            apsProductDetailService.update(new LambdaUpdateWrapper<ApsProductDetailDo>()
                    .eq(ApsProductDetailDo::getMainid, apsBlankOrderDo.getProductionorderid())
                    .set(ApsProductDetailDo::getProductstatus, TO_BE_SCHEDULED));
        }


        return R.ok();
    }

    public void removeBlankOrderAndChild(ApsBlankOrderDo req) {
        //删除坯料计划
        this.removeById(req.getId());

        //如果是备料，删除计划后，备料的计划ID设置为空
        if (req.getPreparematerial().equals("是")) {
            ApsWorkInProgressInventoryDo needUpdate = new ApsWorkInProgressInventoryDo();
            needUpdate.setPlanmaterialid("");
            apsWorkInProgressInventoryService.update(needUpdate, new QueryWrapper<ApsWorkInProgressInventoryDo>().lambda().eq(ApsWorkInProgressInventoryDo::getPlanmaterialid, req.getId()));
        }

        //逻辑删除坯料计划输出成品
        apsProcessOutputProductService.remove(new QueryWrapper<ApsProcessOutputProductDo>().lambda().eq(ApsProcessOutputProductDo::getBlankid, req.getId()));

        //逻辑删除坯料计划工序作业
        apsProcessOperationService.remove(new QueryWrapper<ApsProcessOperationDo>().lambda().eq(ApsProcessOperationDo::getBlankid, req.getId()));

        //逻辑删除坯料计划工序作业-输出物料
        apsProcessOperationOutMaterService.remove(new QueryWrapper<ApsProcessOperationOutMaterDo>().lambda().eq(ApsProcessOperationOutMaterDo::getBlankid, req.getId()));

        //逻辑删除坯料计划工序作业-可选设备
        apsProcessOperationEquService.remove(new QueryWrapper<ApsProcessOperationEquDo>().lambda().eq(ApsProcessOperationEquDo::getBlankid, req.getId()));

        //逻辑删除坯料计划工序作业-作业明细
        apsProcessOperationProcessEquService.remove(new QueryWrapper<ApsProcessOperationProcessEquDo>().lambda().eq(ApsProcessOperationProcessEquDo::getBlankid, req.getId()));
        //存储日志
        apsConflictLogService.recordLog("", "逻辑删除作业明细，坯料计划ID为：" + req.getId(), "", 2, null);

        //逻辑删除坯料计划的工艺路线
        this.baseMapper.removeMindMappingByBlankId(req.getId());
    }

    @Override
    @Transactional
    public R checkAndUpdateBlankAndOutput(ApsProductionTechnicalRequirementDo req) {
        String id = req.getId();
        //查询技术要求
        ApsProductionTechnicalRequirementDo apsProductionTechnicalRequirementDo = apsProductionTechnicalRequirementService.getById(id);
        //获取对应的产品明细IDs
        String orderdetailids = apsProductionTechnicalRequirementDo.getOrderdetailids();
        if (ObjectUtil.isEmpty(orderdetailids)) {
            return R.error("技术要求对应的产品明细不能为空");
        }
        //查询产品明细对应的输出成品
        List<String> orderDetailIdList = Arrays.asList(orderdetailids.split(","));
        List<ApsProcessOutputProductDo> apsProcessOutputProductDoList = apsProcessOutputProductService.list(new QueryWrapper<ApsProcessOutputProductDo>().lambda().in(ApsProcessOutputProductDo::getProductid, orderDetailIdList));
        //查询涉及到的坯料计划ID
        List<String> blankIdlist = apsProcessOutputProductDoList.stream().map(ApsProcessOutputProductDo::getBlankid).distinct().collect(Collectors.toList());
        if (ObjectUtil.isEmpty(blankIdlist)) {
            return R.ok();
        }
        //查询坯料计划详情
        List<ApsBlankOrderDo> apsBlankOrderDoList = apsBlankOrderService.list(new QueryWrapper<ApsBlankOrderDo>().lambda().in(ApsBlankOrderDo::getId, blankIdlist));

        //如果坯料计划的生产状态为：待排程（10），待发布（20），待开工（30）
        //筛选出需要更新的坯料计划
        List<ApsBlankOrderDo> needUpdateBlankList = apsBlankOrderDoList.stream().filter(item -> Arrays.asList(TO_BE_SCHEDULED, TO_BE_PUBLISHED, TO_BE_STARTED).contains(item.getProductstatus())).collect(Collectors.toList());

//        //最终需要更新的数组集合
//        List<ApsBlankOrderDo> endNeedUpdateBlankList = new LinkedList<>();

        for (ApsBlankOrderDo apsBlankOrderDo : needUpdateBlankList) {
            //是否需要更新坯料计划的 输出产品订单字段
            boolean isNeedUpdateBlank = false;

            //筛选出需要更新的输出成品
            List<ApsProcessOutputProductDo> needUpdateProductOutputList = apsProcessOutputProductDoList.stream().filter(item -> item.getBlankid().equals(apsBlankOrderDo.getId())).collect(Collectors.toList());
            for (ApsProcessOutputProductDo apsProcessOutputProductDo : needUpdateProductOutputList) {
                //获取当前组装输出成品 使用的 字段，（包含替换字段）
                String alloy = ObjectUtil.isNotEmpty(apsProcessOutputProductDo.getAlloy()) ? apsProcessOutputProductDo.getAlloy() : "";
                String alloystatus = ObjectUtil.isNotEmpty(apsProcessOutputProductDo.getAlloystatus()) ? apsProcessOutputProductDo.getAlloystatus() : "";
                String producttype = ObjectUtil.isNotEmpty(apsProcessOutputProductDo.getProducttype()) ? apsProcessOutputProductDo.getProducttype() : "";
                if (ObjectUtil.isNotEmpty(apsProcessOutputProductDo.getChangealloy())) {
                    alloy = apsProcessOutputProductDo.getChangealloy();
                }
                if (ObjectUtil.isNotEmpty(apsProcessOutputProductDo.getChangealloystatus())) {
                    alloystatus = apsProcessOutputProductDo.getChangealloystatus();
                }
                if (ObjectUtil.isNotEmpty(apsProcessOutputProductDo.getChangeproducttype())) {
                    producttype = apsProcessOutputProductDo.getChangeproducttype();
                }

                ApsProcessOutputProductDo endUpdateOutput = BeanUtil.copyProperties(apsProductionTechnicalRequirementDo, ApsProcessOutputProductDo.class);
                endUpdateOutput.setId(apsProcessOutputProductDo.getId());
                endUpdateOutput.setProducttype(apsProductionTechnicalRequirementDo.getOrderproducttype());
                endUpdateOutput.setProducttypeid(apsProductionTechnicalRequirementDo.getOrderproducttypeid());
                //新的产品类型
                String newProductType = endUpdateOutput.getProducttype();
                if (ObjectUtil.isNotEmpty(apsProcessOutputProductDo.getChangeproducttype())) {
                    newProductType = apsProcessOutputProductDo.getChangeproducttype();
                }
                //如果技术要求中的 订单产品类型 不等于 输出成品的 产品类型 ， 需要重新组装输出成品
                if (!producttype.equals(newProductType)) {
                    String outputResult = "";
                    outputResult += (alloy + alloystatus + newProductType + " ");
                    //获取长宽高
                    String thickness = ObjectUtil.isNotEmpty(apsProcessOutputProductDo.getThickness()) ? CXCommonUtils.removeExtraZerosAndDecimal(apsProcessOutputProductDo.getThickness()) : "";
                    String prowidth = ObjectUtil.isNotEmpty(apsProcessOutputProductDo.getProwidth()) ? CXCommonUtils.removeExtraZerosAndDecimal(apsProcessOutputProductDo.getProwidth()) : "";
                    String prolength = ObjectUtil.isNotEmpty(apsProcessOutputProductDo.getProlength()) ? CXCommonUtils.removeExtraZerosAndDecimal(apsProcessOutputProductDo.getProlength()) : "";
                    outputResult += ("厚" + thickness + "*宽" + prowidth);
                    if (ObjectUtil.isNotEmpty(prolength)) {
                        outputResult += ("*长" + prolength);
                    }
                    outputResult += "mm";
                    if (ObjectUtil.isNotEmpty(apsProcessOutputProductDo.getOutputonerollweigth()) && ObjectUtil.isNotEmpty(apsProcessOutputProductDo.getPlanoutputorderroll())) {
                        outputResult += (" " + apsProcessOutputProductDo.getOutputonerollweigth() + "吨 * " + apsProcessOutputProductDo.getPlanoutputorderroll() + "卷");
                    }


                    endUpdateOutput.setOutputresult(outputResult);
                    isNeedUpdateBlank = true;
                }
                apsProcessOutputProductService.updateById(endUpdateOutput);
            }
            if (isNeedUpdateBlank) {
//                //如果坯料计划是暂存，不做处理
////                if (apsBlankOrderDo.getIsstag().equals("是")) {
////                    continue;
////                }
                this.updateBlankSplicingColumn(apsBlankOrderDo.getId());
//                endNeedUpdateBlankList.add(endUpdateBlank);
            }
        }
//        if (endNeedUpdateBlankList.size() > 0) {
//            apsBlankOrderService.updateBatchById(endNeedUpdateBlankList);
//        }

        return R.ok();
    }

    /**
     * 根据坯料计划ID，更新 拼接字段（输出订单产品、输出成品、坯料输出物料类型、坯料输出物料规格）
     *
     * @param blankId 坯料计划ID
     */
    public void updateBlankSplicingColumn(String blankId) {
        ApsBlankOrderDo endUpdateBlank = new ApsBlankOrderDo();
        endUpdateBlank.setId(blankId);
        //=============更新坯料计划的 输出订单产品、输出成品、坯料输出物料类型、坯料输出物料规格===========
        //查询坯料计划对应的输出成品
        List<ApsProcessOutputProductDo> processOutputProductDoList = apsProcessOutputProductService.list(new QueryWrapper<ApsProcessOutputProductDo>().lambda().eq(ApsProcessOutputProductDo::getBlankid, blankId));
        if (processOutputProductDoList != null && processOutputProductDoList.size() > 0) {
            //获取所有输出成品的输出成品
            List<String> outputFinishList = processOutputProductDoList.stream().filter(item -> ObjectUtil.isNotEmpty(item.getOutputresult())).map(ApsProcessOutputProductDo::getOutputresult).sorted(String::compareTo).collect(Collectors.toList());
            //================输出成品===============
            String outputFinishProduct = "";

            for (int i = 0; i < outputFinishList.size(); i++) {
                outputFinishProduct += "[" + (i + 1) + "]" + outputFinishList.get(i);
                if (i < outputFinishList.size() - 1) {
                    outputFinishProduct += ",\r\n";
                }
            }
            endUpdateBlank.setOutputfinishproduct(outputFinishProduct);

            //================输出订单产品=================
            String outputOrderProduct = "";
            //获取所有输出成品的输出订单产品
            List<String> outputOrderProductList = processOutputProductDoList.stream()
                    .filter(item -> ObjectUtil.isNotEmpty(item.getOutputresult()))
                    .map(item -> {
                        String outputresult = item.getOutputresult();
                        String[] s = outputresult.split(" ");
                        String res = s[0] + " " + s[1];
                        if (ObjectUtil.isNotEmpty(item.getOutputonerollweigth()) && ObjectUtil.isNotEmpty(item.getPlanoutputrollcount())) {
                            res = res + " " + CXCommonUtils.removeExtraZerosAndDecimal(item.getOutputonerollweigth()) + "吨 * " + item.getPlanoutputrollcount() + "卷 = " + CXCommonUtils.removeExtraZerosAndDecimal(item.getPlanoutputonerollweight());
                        }
                        return res;
                    }).sorted(String::compareTo).collect(Collectors.toList());

            for (int i = 0; i < outputOrderProductList.size(); i++) {
                outputOrderProduct += "[" + (i + 1) + "]" + outputOrderProductList.get(i);
                if (i < outputOrderProductList.size() - 1) {
                    outputOrderProduct += ",\r\n";
                }
            }
            endUpdateBlank.setOutputorderproduct(outputOrderProduct);

            //==============坯料输出物料类型===============
            String outMaterType = "";
            List<String> outMaterTypeList = processOutputProductDoList.stream().filter(item -> ObjectUtil.isNotEmpty(item.getOutmatertype())).map(ApsProcessOutputProductDo::getOutmatertype).sorted(String::compareTo).collect(Collectors.toList());
            for (int i = 0; i < outMaterTypeList.size(); i++) {
                outMaterType += "[" + (i + 1) + "]" + outMaterTypeList.get(i);
                if (i < outMaterTypeList.size() - 1) {
                    outMaterType += ",\r\n";
                }
            }
            endUpdateBlank.setBlankoutmatertype(outMaterType);

            //==============坯料输出物料规格===============
            String outMaterSpecification = "";
            List<String> outMaterSpecificationList = processOutputProductDoList.stream().filter(item -> ObjectUtil.isNotEmpty(item.getOutmaterspecification())).map(ApsProcessOutputProductDo::getOutmaterspecification).sorted(String::compareTo).collect(Collectors.toList());
            for (int i = 0; i < outMaterSpecificationList.size(); i++) {
                outMaterSpecification += "[" + (i + 1) + "]" + outMaterSpecificationList.get(i);
                if (i < outMaterSpecificationList.size() - 1) {
                    outMaterSpecification += ",\r\n";
                }
            }
            endUpdateBlank.setBlankoutmaterspecification(outMaterSpecification);

            //更新坯料计划
            this.updateById(endUpdateBlank);
        } else {
            throw new CustomException("没有找到该坯料计划的输出成品");
        }
    }

    @Override
    public R checkSchedule(CheckScheduleReq req) {
        //是否正在排程
        ApsUtils.checkScheduling(null);
        CheckScheduleVo checkScheduleVo = null;
        //根据订单ID查询
        if (ObjectUtil.isNotEmpty(req.getOrderId())) {
            checkScheduleVo = apsProcessOperationBackupDao.selectBackByOrderId(req.getOrderId());
        }
        if (ObjectUtil.isNotEmpty(req.getBlankIdList())) {
            checkScheduleVo = apsProcessOperationBackupDao.selectBackByBlankIdList(req.getBlankIdList());
        }
        CheckScheduleResponse checkScheduleResponse = new CheckScheduleResponse();
        if (ObjectUtil.isEmpty(checkScheduleVo)) {
            checkScheduleResponse.setNeedWarning(true);
            checkScheduleResponse.setWarningMessage("修改坯料计划后需要重新排程，确认修改吗？");
            return R.ok(checkScheduleResponse);
        }
        //获取当前用户
        SysUserVo currentUser = CXCommonUtils.getCurrentUser();

        if (ObjectUtil.isNotEmpty(checkScheduleVo.getCount()) && checkScheduleVo.getCount().equals(0)) {
            checkScheduleResponse.setNeedWarning(true);
            checkScheduleResponse.setWarningMessage("修改坯料计划后需要重新排程，确认修改吗？");
            return R.ok(checkScheduleResponse);
        }
        if (ObjectUtil.isNotEmpty(checkScheduleVo.getBackUpUserId()) && checkScheduleVo.getBackUpUserId().equals(currentUser.getId())) {
            checkScheduleResponse.setNeedWarning(true);
            checkScheduleResponse.setWarningMessage("该操作会导致您的排程结果备份不可恢复，确认继续吗？");
            return R.ok(checkScheduleResponse);
        } else {
            checkScheduleResponse.setNeedWarning(true);
            checkScheduleResponse.setWarningMessage(checkScheduleVo.getBackUpUserName() + "正在排程，该操作会导致其排程结果备份不可恢复，确认继续吗？");
            return R.ok(checkScheduleResponse);
        }

    }

    /**
     * 更新坯料计划交货期并增加响应冲突描述
     *
     * @return
     */
    public List<String> updateBlankDeliveryDate(ApsBlankOrderDo apsBlankOrderDo, String id) {
        if (apsBlankOrderDo == null) {
            apsBlankOrderDo = this.getById(id);
        }
        if (apsBlankOrderDo == null) {
            logger.error("无法获取到坯料计划信息");
            throw new CustomException("无法获取到坯料计划信息");
        }
        logger.debug("开始更新坯料计划：{}对应的交货期没", apsBlankOrderDo.getId());
        List<UpdateBlankDeliveryDateVo> blankDeliveryDate = this.getBaseMapper().getBlankDeliveryDate(apsBlankOrderDo.getId());
        logger.debug("获取当前坯料计划的相关的计划完工时间:{}", blankDeliveryDate);
        List<String> aboutIds = removeOtherConflictdes(apsBlankOrderDo.getId(), blankDeliveryDate.stream().map(UpdateBlankDeliveryDateVo::getId).collect(Collectors.toList()));
        if (blankDeliveryDate.isEmpty()) {
            return aboutIds;
        }
        if (blankDeliveryDate.size() == 1) {
            blankDeliveryDate.add(blankDeliveryDate.get(0));
        }
        // 排程交货期-起
        Date startDate = timeAddHour(blankDeliveryDate.get(0).getPlanenddate(), blankDeliveryDate.get(0).getDeliverytime());
        //排程交货期-止
        Date endDate = timeAddHour(blankDeliveryDate.get(1).getPlanenddate(), blankDeliveryDate.get(0).getDeliverytime());
        logger.debug("坯料计划新的排程交货期为：{}-{},旧的为{}-{}", startDate, endDate, apsBlankOrderDo.getScheduledatestart(),
                apsBlankOrderDo.getScheduledateend());
        Date newStartDate = apsBlankOrderDo.getPromisedatestart();
        Date newEndDate = apsBlankOrderDo.getPromisedateend();
        if (newStartDate == null) {
            newStartDate = timeAddHour(startDate, 48);
        }
        if (newEndDate == null) {
            newEndDate = timeAddHour(endDate, 48);
        }
        // 更新数据
        this.update(new LambdaUpdateWrapper<ApsBlankOrderDo>().eq(ApsBlankOrderDo::getId, apsBlankOrderDo.getId())
                .set(BaseDo::getDeleted, 0)
                .set(ApsBlankOrderDo::getScheduledatestart, startDate)
                .set(ApsBlankOrderDo::getScheduledateend, endDate)
                .set(ApsBlankOrderDo::getPromisedatestart, newStartDate)
                .set(ApsBlankOrderDo::getPromisedateend, newEndDate));
        logger.debug("坯料计划新的承诺交货日期：{}-{},旧的为{}-{}", newStartDate, newEndDate,
                apsBlankOrderDo.getPromisedatestart(), apsBlankOrderDo.getPromisedateend());
        List<String> updateIds = new ArrayList<>();
        List<String> removeIds = new ArrayList<>();
        // modify at [ 2024-10-14 11:51:18 ] by xh: 按照项目经理要求只比较日期的日期部分

        //如果排程交货期-起>承诺交货期-起
        if (DateCompare(startDate, newStartDate) > 0) {
            updateIds.add(blankDeliveryDate.get(0).getId());
        } else {
            removeIds.add(blankDeliveryDate.get(0).getId());
        }
        // 如果排程交货期-止>承诺交货期-止
        if (DateCompare(endDate, newEndDate) > 0) {
            updateIds.add(blankDeliveryDate.get(1).getId());
        } else {
            removeIds.add(blankDeliveryDate.get(1).getId());
        }
        String format = String.format(LOG_MSG, startDate, startDate, endDate, newEndDate);
        if (!updateIds.isEmpty()) {
            List<ApsProcessOperationProcessEquDo> equDos = apsProcessOperationProcessEquService.getBaseMapper().selectBatchIds(updateIds);
            logger.debug("记录冲突信息超出承诺交货期:{}", equDos);
            equDos.forEach(data -> {
                apsConflictLogService.recordLog(data.getId(),
                        format, EXCEEDING_THE_PROMISED_DELIVERY_DEADLINE, 1, null);
                data.setConflictdes(addNewConflictsDesc(data.getConflictdes(), EXCEEDING_THE_PROMISED_DELIVERY_DEADLINE));
                if (StringUtils.isBlank(data.getConflictdes()) && StringUtils.isBlank(data.getSoftconflictdes())) {
                    data.setHasconflict("n");
                } else {
                    data.setHasconflict("y");
                }
                data.setModifydate(new Date());
            });
            if (!equDos.isEmpty()) {
                apsProcessOperationProcessEquService.updateBatchById(equDos);
            }
        }
        if (!removeIds.isEmpty()) {
            List<ApsProcessOperationProcessEquDo> equDos = apsProcessOperationProcessEquService.getBaseMapper().selectBatchIds(removeIds);
            logger.debug("删除超出承诺交货期冲突:{}", equDos);
            equDos.forEach(data -> {
                apsConflictLogService.recordLog(data.getId(),
                        format, EXCEEDING_THE_PROMISED_DELIVERY_DEADLINE, 2, null);
                data.setConflictdes(removeConflictsDesc(data.getConflictdes(), EXCEEDING_THE_PROMISED_DELIVERY_DEADLINE));
                if (StringUtils.isBlank(data.getConflictdes()) && StringUtils.isBlank(data.getSoftconflictdes())) {
                    data.setHasconflict("n");
                } else {
                    data.setHasconflict("y");
                }
                data.setModifydate(new Date());
            });
            if (!equDos.isEmpty()) {
                apsProcessOperationProcessEquService.updateBatchById(equDos);
            }
        }
        return aboutIds;

    }

    /**
     * 根據坯料計劃ID 刪除工序作業明細的 超出承諾交貨期衝突
     *
     * @param blankId
     * @param operationId
     */
    public List<String> removeOtherConflictdes(String blankId, List<String> operationId) {
        if (StringUtils.isBlank(blankId)) {
            return new ArrayList<>();
        }
        List<ApsProcessOperationProcessEquDo> list = apsProcessOperationProcessEquService.list(new LambdaQueryWrapper<ApsProcessOperationProcessEquDo>()
                .eq(ApsProcessOperationProcessEquDo::getBlankid, blankId)
                .notIn(operationId == null && operationId.size() > 0, ApsProcessOperationProcessEquDo::getId, operationId)
                .like(ApsProcessOperationProcessEquDo::getConflictdes, EXCEEDING_THE_PROMISED_DELIVERY_DEADLINE));

        List<ApsProcessOperationProcessEquDo> updates = new ArrayList<>();
        ApsProcessOperationProcessEquDo updateDo;
        for (ApsProcessOperationProcessEquDo apsProcessOperationProcessEquDo : list) {
            updateDo = new ApsProcessOperationProcessEquDo();
            apsConflictLogService.recordLog(apsProcessOperationProcessEquDo.getId(),
                    "", EXCEEDING_THE_PROMISED_DELIVERY_DEADLINE, 2, null);

            updateDo.setConflictdes(removeConflictsDesc(apsProcessOperationProcessEquDo.getConflictdes(), EXCEEDING_THE_PROMISED_DELIVERY_DEADLINE));
            if (StringUtils.isBlank(updateDo.getConflictdes()) && StringUtils.isBlank(apsProcessOperationProcessEquDo.getSoftconflictdes())) {
                updateDo.setHasconflict("n");
            } else {
                updateDo.setHasconflict("y");
            }
            updateDo.setId(apsProcessOperationProcessEquDo.getId());
            updateDo.setModifydate(new Date());
            updates.add(updateDo);
        }
        if (!updates.isEmpty()) {
            apsProcessOperationProcessEquService.updateBatchById(updates);
        }
        return updates.stream().map(ApsProcessOperationProcessEquDo::getId).collect(Collectors.toList());
    }

    public Date timeAddHour(Date date, int hour) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.HOUR_OF_DAY, hour);
        return calendar.getTime();
    }

    public int DateCompare(Date date1, Date date2) {
        Calendar calendar1 = Calendar.getInstance();
        calendar1.setTime(date1);
        calendar1.set(Calendar.HOUR_OF_DAY, 0);
        calendar1.set(Calendar.MINUTE, 0);
        calendar1.set(Calendar.SECOND, 0);
        calendar1.set(Calendar.MILLISECOND, 0);

        Calendar calendar2 = Calendar.getInstance();
        calendar2.setTime(date2);
        calendar2.set(Calendar.HOUR_OF_DAY, 0);
        calendar2.set(Calendar.MINUTE, 0);
        calendar2.set(Calendar.SECOND, 0);
        calendar2.set(Calendar.MILLISECOND, 0);
        return calendar1.compareTo(calendar2);
    }

    @Override
    public R productPlanReport(CriteriaQuery req) {
        //解析入参
        //每页数量
        Integer size = req.getSize();
        //页数
        Integer current = req.getCurrent();
        //查询条件
        /**
         * 客户名称：模糊查询；
         * 客户订单号：模糊查询；
         * 承诺交货日期：日历选择日期范围
         * 坯料计划编号：模糊查询；
         */
        //页签查询：订单产品生产状态

        ProductPlanReportParamVo param = new ProductPlanReportParamVo();

        List<ColumnQueryType> columnQueryTypes = req.getColumnQueryTypes();
        for (ColumnQueryType columnQueryType : columnQueryTypes) {
            if (columnQueryType.getColumnInnerRelation().equals("AND")) {
                for (SearchColumnVo column : columnQueryType.getColumns()) {
                    if (column.getField().equals("customname")) {
                        param.setCustomName(column.getValue());
                    }
                    if (column.getField().equals("customorderno")) {
                        param.setCustomOrderNo(column.getValue());
                    }
                    if (column.getField().equals("promiseDateStart")) {
                        param.setStartDate(column.getStartDate());
                        param.setEndDate(column.getEndDate());
                    }
                    if (column.getField().equals("blankNumber")) {
                        param.setBlankNumber(column.getValue());
                    }
                    if (column.getField().equals("productionOrderStatus")) {
                        param.setProductionOrderStatus(column.getValue());
                    }
                }
            }
            if (columnQueryType.getColumnInnerRelation().equals("OR")) {
                for (SearchColumnVo column : columnQueryType.getColumns()) {
                    if (column.getField().equals("customname")) {
                        param.setLikeSearchAll(column.getValue());
                    }
                }
            }
        }

        //排序规则
        String order = "";
        List<SearchOrderVo> orders = req.getOrders();
        if (ObjectUtil.isNotEmpty(orders)) {
            for (SearchOrderVo searchOrderVo : orders) {
                if (searchOrderVo.getOrderName().equals("auditdate")) {
                    order = "a.auditdate " + searchOrderVo.getOrderType() + ",";
                }
                if (searchOrderVo.getOrderName().equals("customname")) {
                    order = "a.customname " + searchOrderVo.getOrderType() + ",";
                }
            }
        }
        param.setOrders(order);

        //当前用户所属工厂
        SysUserVo currentUser = CXCommonUtils.getCurrentUser();
        String tenantId = CXCommonUtils.getCurrentUserFactoryId(currentUser);
        param.setTenantId(tenantId);

        Page<ProductPlanReportVo> queryPage = new Page<>(current, size);


        IPage<ProductPlanReportVo> res = this.baseMapper.selectPageReport(queryPage, param);

        //分页查询结束，拼接结果字段信息
        List<ProductPlanReportVo> records = res.getRecords();
        if (ObjectUtil.isEmpty(records)) {
            return R.ok(res);
        }
        //当前页面的坯料计划的ID集合
        List<String> blankIdList = records.stream().map(ProductPlanReportVo::getBlankId).distinct().collect(Collectors.toList());
        List<BlankIdAndWorkShopVo> blankIdAndWorkShopList = new LinkedList<>();
        if (ObjectUtil.isNotEmpty(blankIdList)) {
            //查询坯料计划对应的 坯料 加工车间
            blankIdAndWorkShopList = apsProcessOperationDao.getWorkShopByBlankId(blankIdList);
        }

        //查询坯料计划对应的工序作业信息
//        List<ApsProcessOperationDo> processOperationDoList = apsProcessOperationService.list(new QueryWrapper<ApsProcessOperationDo>().lambda().in(ApsProcessOperationDo::getBlankid, blankIdList));
        List<ApsProcessOperationDo> processOperationDoList = apsProcessOperationDao.selectListAndPlanDate(blankIdList);

        //构造生产进度
        //获取所有的最终工序ID集合
        List<String> lastProcessIdList = records.stream().filter(item -> ObjectUtil.isNotEmpty(item.getLastProcessId())).map(ProductPlanReportVo::getLastProcessId).distinct().collect(Collectors.toList());
        //组装map《最终工序ID，生产进度》
        Map<String, String> processLoadingMap = new HashMap<>();
        doSetMapLoading(processLoadingMap, lastProcessIdList, processOperationDoList);

        //获取对应输出成品的产品明细
        List<String> detailIdList = records.stream().map(ProductPlanReportVo::getOutputOrderDetailId).distinct().collect(Collectors.toList());
        List<ApsProductDetailDo> apsProductDetailDoList = new ArrayList<>(apsProductDetailService.listByIds(detailIdList));

        for (ProductPlanReportVo record : records) {
            //显示订单号
            record.setOrderno(record.getNewOrderNo());
            //显示客户订单号
            record.setCustomorderno(record.getNewCustomOrderNo());
            //显示客户名称
            record.setCustomname(record.getNewCustomName());
            //加工车间
            if (ObjectUtil.isNotEmpty(blankIdAndWorkShopList)) {
                BlankIdAndWorkShopVo blankIdAndWorkShopVo = blankIdAndWorkShopList.stream().filter(item -> item.getBlankId().equals(record.getBlankId())).findFirst().orElse(null);
                if (ObjectUtil.isNotEmpty(blankIdAndWorkShopVo)) {
                    record.setProcessWorkShop(blankIdAndWorkShopVo.getPrcessWorkShop());
                }
            }

            //计划转料日期
            //实际转料日期
            //已投料数量
            List<ApsProcessOperationDo> operationDoList = processOperationDoList.stream().filter(item -> item.getBlankid().equals(record.getBlankId()) && item.getIfblankprocess().equals("否")).collect(Collectors.toList());
            if (ObjectUtil.isNotEmpty(operationDoList)) {
                ApsProcessOperationDo processOperationDo1 = operationDoList.stream().filter(item -> ObjectUtil.isNotEmpty(item.getPlanstartdate())).min(Comparator.comparing(ApsProcessOperationDo::getPlanstartdate)).orElse(null);
                if (ObjectUtil.isNotEmpty(processOperationDo1)) {
                    record.setPlanChangeBlankDate(processOperationDo1.getPlanstartdate());
                    record.setHadStartRoll(processOperationDo1.getStartingroll());
                }
                ApsProcessOperationDo processOperationDo2 = operationDoList.stream().filter(item -> ObjectUtil.isNotEmpty(item.getActualstartdate())).min(Comparator.comparing(ApsProcessOperationDo::getActualstartdate)).orElse(null);
                if (ObjectUtil.isNotEmpty(processOperationDo2)) {
                    record.setActualChangeBlankDate(processOperationDo2.getActualstartdate());
                }
            }


            //生产进度
            if (ObjectUtil.isNotEmpty(record.getLastProcessId()) && processLoadingMap.containsKey(record.getLastProcessId())) {
                record.setProductLoading(processLoadingMap.get(record.getLastProcessId()));
            }

            //计划开工时间
            //计划完工时间
            //实际开工时间
            //实际完工时间
            List<ApsProcessOperationDo> operationDoList2 = processOperationDoList.stream().filter(item -> item.getBlankid().equals(record.getBlankId())).collect(Collectors.toList());
            if (ObjectUtil.isNotEmpty(operationDoList2)) {
                ApsProcessOperationDo processOperationDo1 = operationDoList.stream().filter(item -> ObjectUtil.isNotEmpty(item.getPlanstartdate())).min(Comparator.comparing(ApsProcessOperationDo::getPlanstartdate)).orElse(null);
                if (ObjectUtil.isNotEmpty(processOperationDo1)) {
                    record.setPlanStartDate(processOperationDo1.getPlanstartdate());
                }

                ApsProcessOperationDo processOperationDo2 = operationDoList.stream().filter(item -> ObjectUtil.isNotEmpty(item.getPlanenddate())).max(Comparator.comparing(ApsProcessOperationDo::getPlanenddate)).orElse(null);
                if (ObjectUtil.isNotEmpty(processOperationDo2)) {
                    record.setPlanEndDate(processOperationDo2.getPlanstartdate());
                }

                ApsProcessOperationDo processOperationDo3 = operationDoList.stream().filter(item -> ObjectUtil.isNotEmpty(item.getActualstartdate())).min(Comparator.comparing(ApsProcessOperationDo::getActualstartdate)).orElse(null);
                if (ObjectUtil.isNotEmpty(processOperationDo3)) {
                    record.setActualStartDate(processOperationDo3.getActualstartdate());
                }
                if (record.getProductionOrderStatus().equals("已完工")) {
                    ApsProcessOperationDo processOperationDo4 = operationDoList.stream().filter(item -> ObjectUtil.isNotEmpty(item.getActualfinishdate())).max(Comparator.comparing(ApsProcessOperationDo::getActualfinishdate)).orElse(null);
                    if (ObjectUtil.isNotEmpty(processOperationDo4)) {
                        record.setActualEndDate(processOperationDo4.getActualfinishdate());
                    }
                }

            }

            //分配客户订单号
            ApsProductDetailDo apsProductDetailDo = apsProductDetailDoList.stream().filter(item -> item.getId().equals(record.getOutputOrderDetailId())).findFirst().orElse(null);
            if (ObjectUtil.isNotEmpty(apsProductDetailDo) && ObjectUtil.isNotEmpty(apsProductDetailDo.getCustomorderno())) {
                record.setCustomorderno(apsProductDetailDo.getCustomorderno());
            }

        }


        return R.ok(res);
    }

    //插单重新排程
    @Override
    public void insertSchedule(Map<String, Object> params) {
        List<ApsBlankOrderVo> apsBlankOrders = apsBlankOrderDao.getByOrderBlanckId(params.get("PRODUCTIONORDERID").toString(), params.get("BLANKORDERID").toString());
        if (apsBlankOrders != null && apsBlankOrders.size() > 0) {
            apsService.apsSchedule(apsBlankOrders, new ApsTypeVo(), null);
        }
    }

    private void doSetMapLoading(Map<String, String> processLoadingMap, List<String> lastProcessIdList, List<ApsProcessOperationDo> processOperationDoList) {
        for (String s : lastProcessIdList) {

            LinkedList<String> stack = new LinkedList<>();
            //迭代处理
            setStack(processLoadingMap, processOperationDoList, s, stack, s);
        }
    }

    //迭代处理，组装生产进度，赋值stack
    private void setStack(Map<String, String> processLoadingMap, List<ApsProcessOperationDo> processOperationDoList, String s, LinkedList<String> stack, String lastProcessId) {
        //获取该工序详情
        ApsProcessOperationDo processOperationDo = processOperationDoList.stream().filter(item -> item.getId().equals(s)).findFirst().orElse(null);

        if (ObjectUtil.isEmpty(processOperationDo)) {
            setMap(s, processLoadingMap, stack, lastProcessId);
        }

        //组装赋值，先进后出
        String processname = processOperationDo.getProcessname();
        Integer reportroll = processOperationDo.getReportroll();
        Integer planprocessrall = processOperationDo.getPlanprocessrall();
        stack.push(processname + "（" + reportroll + "/" + planprocessrall + "）");

        //如果前道工序为空，表示结束
        if (ObjectUtil.isEmpty(processOperationDo.getPreviousprocessid())) {
            setMap(s, processLoadingMap, stack, lastProcessId);
        } else {//不为空，继续迭代
            setStack(processLoadingMap, processOperationDoList, processOperationDo.getPreviousprocessid(), stack, lastProcessId);
        }
    }


    //赋值map
    private void setMap(String s, Map<String, String> processLoadingMap, LinkedList<String> stack, String lastProcessId) {
        //判断栈中 是否为空，如果为空，则赋值空字符串
        if (stack.size() <= 0) {
            processLoadingMap.put(s, "");
        } else {//否则，顺序取出并拼接字符串
            String processLoading = String.join("-", stack);
            processLoadingMap.put(lastProcessId, processLoading);
        }
    }

    //领料锁
    private final ArrayList<String> addMaterialList = new ArrayList<String>();
    private final List<String> addMaterialLock = Collections.synchronizedList(addMaterialList);

    @Override
    @Transactional
    public R addMaterial(AddMaterialReq req) {
        //分割批次号
        List<String> batchNumberList = new LinkedList<>();
//        if (ObjectUtil.isEmpty(req.getBatchNumbers())) {
//            return R.error("批次号不可为空");
//        }
        if (ObjectUtil.isEmpty(req.getBlankId())) {
            return R.error("坯料计划ID为空");
        }
        if (ObjectUtil.isEmpty(req.getProcessId())) {
            return R.error("坯料计划第一道工序作业的ID为空");
        }
        if (ObjectUtil.isNotEmpty(req.getBatchNumbers())) {
            batchNumberList = new LinkedList<>(Arrays.asList(req.getBatchNumbers().split(",")));
        }

        for (String s : batchNumberList) {
            if (addMaterialLock.contains(s)) {
                log.error("批次号" + s + "正被操作，请稍后重试");
                return R.error("批次号" + s + "正被操作，请稍后重试");
            }
        }
        addMaterialLock.addAll(batchNumberList);
        List<String> inputNumberList = new ArrayList<>(batchNumberList);
        try {
            //查询坯料计划第一道工序作业的详情
            ApsProcessOperationDo apsProcessOperationDo = apsProcessOperationService.getById(req.getProcessId());
            int needStartRoll = apsProcessOperationDo.getLeavewaitworkroll();
            if (batchNumberList.size() > needStartRoll) {
                return R.error("领料数量不能大于剩余待开工卷数=" + needStartRoll);
            }

            //查询目前的作业明细包含的批次号
            String nowNumber = this.baseMapper.selectBatchNumbersByProcessId(apsProcessOperationDo.getId());

            //原来的批次号
            String oldBatchNumbers = req.getOldBatchNumbers();
            LinkedList<String> oldBatchNumberList = new LinkedList<>();
            if (ObjectUtil.isNotEmpty(oldBatchNumbers)) {
                oldBatchNumberList = new LinkedList<>(Arrays.asList(oldBatchNumbers.split(",")));
            }

            //判断旧的批次号有没有变化
            List<String> nowNumberList = new LinkedList<>();
            if (ObjectUtil.isNotEmpty(nowNumber)) {
                nowNumberList = new LinkedList<>(Arrays.asList(nowNumber.split(",")));
            }
            if (nowNumberList.size() != oldBatchNumberList.size()) {
                return R.error("领料批次号可能已经被使用，请刷新后重新领料");
            }
            for (String s : nowNumberList) {
                if (!oldBatchNumberList.contains(s)) {
                    return R.error("领料批次号可能已经被使用，请刷新后重新领料");
                }
            }
            for (String s : oldBatchNumberList) {
                if (!nowNumberList.contains(s)) {
                    return R.error("领料批次号可能已经被使用，请刷新后重新领料");
                }
            }

            //新的批次号集合
            List<String> addBatchNumberList = new LinkedList<>();
            //删除的批次号集合
            List<String> delBatchNumberList = new LinkedList<>();

            for (String s : oldBatchNumberList) {
                if (!batchNumberList.contains(s)) {
                    delBatchNumberList.add(s);
                }
            }
            for (String s : batchNumberList) {
                if (!oldBatchNumberList.contains(s)) {
                    addBatchNumberList.add(s);
                }
            }

            List<ApsWorkInProgressInventoryDo> workInProgressInventoryDos = new LinkedList<>();
            if (ObjectUtil.isNotEmpty(addBatchNumberList)) {

                //查询新增的批次号
                workInProgressInventoryDos =
                        apsWorkInProgressInventoryService.list(new QueryWrapper<ApsWorkInProgressInventoryDo>().lambda()
                                .eq(ApsWorkInProgressInventoryDo::getDeleted, "0")
                                .in(ApsWorkInProgressInventoryDo::getBatchnumber, addBatchNumberList));
            }
            for (int i = 0; i < addBatchNumberList.size(); i++) {
                String s = addBatchNumberList.get(i);
                String addBatchNumber = s;
                //如果该批次号物料在制品库存中没找到，则错误提示：批次号{批次号}错误
                ApsWorkInProgressInventoryDo apsWorkInProgressInventoryDo = workInProgressInventoryDos.stream().filter(item -> item.getBatchnumber().equals(addBatchNumber)).findFirst().orElse(null);
                if (ObjectUtil.isEmpty(apsWorkInProgressInventoryDo)) {
                    /**
                     * 如果该批次号没有对应在制品库存，表示线下批次号，
                     * 如果批次号没有前缀*，则加上前缀*
                     */
                    if (!s.startsWith("*")) {
                        s = "*" + s;
                        addBatchNumberList.set(i, s);
                        //查询是否已被其他坯料计划领料
                        ApsBlankOrderDo blankOrderDo = this.baseMapper.countThisBatchNumber(s, req.getBlankId());
                        if (ObjectUtil.isNotEmpty(blankOrderDo)) {
                            return R.error("该批次号" + s + "已被其他坯料计划" + blankOrderDo.getBlanknumber() + "领料");
                        }
                        batchNumberList.set(batchNumberList.indexOf(addBatchNumber), s);
                    }
                } else {
                    //借调逻辑
                    borrowBlank(apsWorkInProgressInventoryDo);
                }
            }

//        //如果批次号有重复，则错误提示：批次号{批次号}重复
//        for (String item : batchNumberList) {
//            if (batchNumberList.stream().filter(i -> i.equals(item)).count() > 1) {
//                return R.error("批次号" + item + "重复");
//            }
//        }
//
//
////        //如果批次号不是9位数，则错误提示：热轧坯料批次号{批次号}错误，应该是9位
////
////        for (String s : batchNumberList) {
////            if (s.length() != 9) {
////                return R.error("热轧坯料批次号" + s + "错误，应该是9位");
////            }
////        }
//        List<ApsWorkInProgressInventoryDo> workInProgressInventoryDos = new LinkedList<>();
//        if (ObjectUtil.isNotEmpty(addBatchNumberList)) {
//            //查询新增的批次号
//            workInProgressInventoryDos =
//                    apsWorkInProgressInventoryService.list(new QueryWrapper<ApsWorkInProgressInventoryDo>().lambda()
//                            .eq(ApsWorkInProgressInventoryDo::getDeleted, "0")
//                            .in(ApsWorkInProgressInventoryDo::getBatchnumber, addBatchNumberList));
//        }
//
////        //如果坯料类型=热轧，检查每个批次号
////        if (apsBlankOrderDo.getBlanktype().equals("热轧")) {
////            for (String s : addBatchNumberList) {
////                //如果该批次号物料在制品库存中存在，则错误提示：批次号{批次号}错误
////                ApsWorkInProgressInventoryDo apsWorkInProgressInventoryDo = workInProgressInventoryDos.stream().filter(item -> item.getBatchnumber().equals(s)).findFirst().orElse(null);
////                if (ObjectUtil.isNotEmpty(apsWorkInProgressInventoryDo)) {
////                    return R.error("批次号" + s + "错误");
////                }
////            }
////        }
//
//        /**
//         * 如果该批次号物料在制品库存中存在
//         * 如果在制品备料计划ID不为空，并且不等于该坯料计划ID，错误提示：该坯料已经被其他坯料计划领料
//         * 如果在制品状态=待加工或加工中或待检验，错误提示：批次号{批次号}错误，该料卷在加工中
//         * 如果在制品状态=待入库或已入库或已出库，并且订单号对应订单的客户的客户类型不是内部客户，错误提示：批次号{批次号}错误，该料卷是其他客户订单的成品
//         * 如果在制品状态=备料中，并且在制品类型=不良在制品，错误提示：批次号{批次号}错误，该料卷检验不合格
//         * 查找该批次号物料=在制品的作业输出物料ID对应输出物料
//         * 如果{该批次号物料的输入物料描述}与{坯料的输入物料描述}不相同，
//         * 错误提示：该批次号{批次号}物料：{该批次号物料的输入物料描述}不是该坯料计划所需的坯料{坯料的输入物料描述}
//         */
//
//        for (String s : addBatchNumberList) {
//            //如果该批次号物料在制品库存中没找到，则错误提示：批次号{批次号}错误
//            ApsWorkInProgressInventoryDo apsWorkInProgressInventoryDo = workInProgressInventoryDos.stream().filter(item -> item.getBatchnumber().equals(s)).findFirst().orElse(null);
//            if (ObjectUtil.isEmpty(apsWorkInProgressInventoryDo)) {
//                continue;
//            }
//
//            //如果在制品备料计划ID不为空，并且不等于该坯料计划ID，错误提示：该坯料已经被其他坯料计划领料
//            if (ObjectUtil.isNotEmpty(apsWorkInProgressInventoryDo.getPlanmaterialid())
//                    && !apsWorkInProgressInventoryDo.getPlanmaterialid().equals(req.getBlankId())) {
//                ApsBlankOrderDo blankOrderDo = apsBlankOrderService.getById(apsWorkInProgressInventoryDo.getPlanmaterialid());
//                return R.error("该坯料已经被坯料计划" + blankOrderDo.getBlanknumber() + "领料");
//            }
//
////            //如果在制品类型=不良在制品，错误提示：批次号{批次号}错误，该料卷检验不合格
////            if (apsWorkInProgressInventoryDo.getWorkinprocesstype().equals("不良在制品")) {
////                return R.error("批次号" + s + "错误，该料卷检验不合格");
////            }
//
//            //如果在制品状态=加工中或待检验，错误提示：批次号{批次号}错误，该料卷在加工中
//            if (Arrays.asList("加工中", "待检验").contains(apsWorkInProgressInventoryDo.getWorkinprocessstatus())) {
//                return R.error("批次号" + s + "错误，该料卷在加工中");
//            }
//
//            //如果在制品状态=待入库或已入库或已出库，并且在制品的订单号对应订单的订单类型=客户订单，错误提示：批次号{批次号}错误，该料卷是其他客户订单的成品
//            if (Arrays.asList("待入库", "已入库", "已出库").contains(apsWorkInProgressInventoryDo.getWorkinprocessstatus())) {
//                if (ObjectUtil.isNotEmpty(apsWorkInProgressInventoryDo.getOrdertype()) && apsWorkInProgressInventoryDo.getOrdertype().equals("客户订单")) {
//                    return R.error("批次号" + s + "错误，该料卷是其他客户订单的成品");
//                }
//
//            }
//
//            //查找该批次号物料=在制品的作业输出物料ID对应输出物料
//            //如果{该批次号物料的输入物料描述}与{坯料的输入物料描述}不相同，或者坯料类型不等于坯料计划的坯料类型
//            //错误提示：该批次号{批次号}物料：{该批次号物料的输入物料描述}不是该坯料计划所需的坯料{坯料的输入物料描述}
//            String s1 = apsWorkInProgressInventoryDo.getJoboutputmaterial();
//            String s2 = apsBlankOrderDo.getInputreportdescribe();
//            if (!s1.equals(s2)) {
//                return R.error("该批次号" + s + "物料：" + s1 + "不是该坯料计划所需的坯料" + s2 + "");
//            }
//        }

            //更新坯料计划对应的领料批次号
            ApsBlankOrderDo apsBlankOrderDo = this.getById(req.getBlankId());
            List<String> preparematerialbatchnoList = new LinkedList<>();
            if (ObjectUtil.isNotEmpty(apsBlankOrderDo.getPreparematerialbatchno())) {
                preparematerialbatchnoList = new LinkedList<>(Arrays.asList(apsBlankOrderDo.getPreparematerialbatchno().split(",")));
            }
            preparematerialbatchnoList.removeAll(delBatchNumberList);
            preparematerialbatchnoList.addAll(addBatchNumberList);
            ApsBlankOrderDo updateBlank = new ApsBlankOrderDo();
            updateBlank.setId(apsBlankOrderDo.getId());
            updateBlank.setPreparematerialbatchno(String.join(",", preparematerialbatchnoList));


            /**
             * 检验通过，
             * 原批次号（修改前的值），对应的所有在制品的在制品状态=备料中，在制品的备料计划ID=(空)
             * 更新坯料计划的第一道工序作业的所有工序作业明细的待加工料卷批次号=领料批次号逗号拼接
             * 在制品的在制品状态=待加工
             * 在制品的备料计划ID=该坯料计划ID
             */
            //原批次号（修改前的值），对应的所有在制品的在制品状态=备料中，在制品的备料计划ID=(空)
            if (ObjectUtil.isNotEmpty(delBatchNumberList)) {
                apsWorkInProgressInventoryService.update(new UpdateWrapper<ApsWorkInProgressInventoryDo>().lambda()
                        .set(ApsWorkInProgressInventoryDo::getPlanmaterialid, null)
                        .setSql("WORKINPROCESSSTATUS = IF(PREVIOUSWORKINPROCESSSTATUS = '待加工','备料中',PREVIOUSWORKINPROCESSSTATUS)")
                        .eq(ApsWorkInProgressInventoryDo::getDeleted, "0")
                        .in(ApsWorkInProgressInventoryDo::getBatchnumber, delBatchNumberList));
//            //更新坯料计划该字段为空
//            apsBlankOrderService.update(new UpdateWrapper<ApsBlankOrderDo>().lambda()
//                    .set(ApsBlankOrderDo::getPreparematerialbatchno, null)
//                    .eq(ApsBlankOrderDo::getId, apsBlankOrderDo.getId()));
            }

            //更新坯料计划的第一道工序作业的所有工序作业明细的待加工料卷批次号=领料批次号逗号拼接
//        apsProcessOperationProcessEquService.update(new UpdateWrapper<ApsProcessOperationProcessEquDo>().lambda()
//                .set(ApsProcessOperationProcessEquDo::getBachmaterialprocess, String.join(",", batchNumberList))
//                .eq(ApsProcessOperationProcessEquDo::getDeleted, "0")
//                .eq(ApsProcessOperationProcessEquDo::getProcessid, req.getProcessId()));
            /**
             * 需求变更:根据作业明细的计划开工时间先后顺序以及剩余待开工卷数（=计划加工卷数-已开工卷数-已取消卷数）自动将领料批次号依次重新分配给这些作业明细，只用管剩余待开工卷数大于0的
             * 注意：是重新分配待加工料卷批次号，需要清空原待加工料卷批次号，不是在原有待加工料卷批次号后面追加
             */
            List<ApsProcessOperationProcessEquDo> updateOperationProcessEquList = apsProcessOperationProcessEquService.list(new LambdaQueryWrapper<ApsProcessOperationProcessEquDo>()
                    .eq(ApsProcessOperationProcessEquDo::getDeleted, "0")
                    .eq(ApsProcessOperationProcessEquDo::getProcessid, req.getProcessId())
                    .orderByAsc(ApsProcessOperationProcessEquDo::getPlanstartdate));
            List<String> finalBatchNumberList = batchNumberList;
            List<ApsProcessOperationProcessEquDo> needUpdateProcessEquList = new ArrayList<>();
            List<ApsProcessOperationProcessEquDo> clearBachMaterialProcessEquList = new ArrayList<>();
            //符合条件的需要先清除原待加工料卷批次号
            updateOperationProcessEquList.forEach(equDo -> {
                //剩余待开工卷数 =计划加工卷数-已开工卷数-已取消卷数
                int leaveWaitWorkRoll = equDo.getPlanprocessrall() - equDo.getStartingroll() - equDo.getCancelroll();
                if (leaveWaitWorkRoll > 0) {
                    if (StringUtils.isNotBlank(equDo.getBachmaterialprocess())) {
                        equDo.setBachmaterialprocess("");
                        clearBachMaterialProcessEquList.add(equDo);
                    }
                }
            });
            if (ObjectUtil.isNotEmpty(clearBachMaterialProcessEquList)) {
                apsProcessOperationProcessEquService.updateBatchById(clearBachMaterialProcessEquList);
            }
            updateOperationProcessEquList.forEach(equDo -> {
                //剩余待开工卷数 =计划加工卷数-已开工卷数-已取消卷数
                int leaveWaitWorkRoll = equDo.getPlanprocessrall() - equDo.getStartingroll() - equDo.getCancelroll();
                if (leaveWaitWorkRoll > 0 && finalBatchNumberList.size() > 0) {
                    //有几个剩余待开工卷数就要分配几个batchNumberList中批次号的个数
                    List<String> distributionNumbers = finalBatchNumberList.stream().limit(leaveWaitWorkRoll).collect(Collectors.toList());
                    equDo.setBachmaterialprocess(String.join(",", distributionNumbers));
                    finalBatchNumberList.removeAll(distributionNumbers);
                    needUpdateProcessEquList.add(equDo);
                }
            });
            if (ObjectUtil.isNotEmpty(needUpdateProcessEquList)) {
                apsProcessOperationProcessEquService.updateBatchById(needUpdateProcessEquList);
            }


            //在制品的在制品状态=待加工
            //在制品的备料计划ID=该坯料计划ID
            if (ObjectUtil.isNotEmpty(addBatchNumberList)) {
                apsWorkInProgressInventoryService.update(new UpdateWrapper<ApsWorkInProgressInventoryDo>().lambda()
                        .set(ApsWorkInProgressInventoryDo::getPlanmaterialid, req.getBlankId())
                        .setSql("PREVIOUSWORKINPROCESSSTATUS = WORKINPROCESSSTATUS")
                        .set(ApsWorkInProgressInventoryDo::getWorkinprocessstatus, "待加工")
                        .eq(ApsWorkInProgressInventoryDo::getDeleted, "0")
                        .in(ApsWorkInProgressInventoryDo::getBatchnumber, addBatchNumberList));
            }
            //赋值坯料计划冗余字段 是否待领料
            String isWaitGetMaterial = this.baseMapper.selectIsWaitGetMaterialById(apsBlankOrderDo.getId());
            apsBlankOrderDo.setIswaitgetmaterial(isWaitGetMaterial);
            this.updateById(updateBlank);
        } finally {
            log.debug("领料完毕，从list中清空当前领料锁，批次号：" + inputNumberList);
            addMaterialLock.removeAll(inputNumberList);
        }

        return R.ok();
    }

    private void borrowBlank(ApsWorkInProgressInventoryDo progressWork) {
        //借调逻辑
        if (ObjectUtil.isNotEmpty(progressWork) && ObjectUtil.isNotEmpty(progressWork.getWorkinprocessstatus()) && progressWork.getWorkinprocessstatus().equals(SaveConstans.WorkInProgressStatus.WAIT_WORK)) {
            //获取该在制品的批次号
            String batchnumber = progressWork.getBatchnumber();
            //查询被借调批次号的作业明细信息
            ApsProcessOperationProcessEquDo needBorrowOperationEqu = this.baseMapper.selectUseThisBatchNumberProcessEqu(batchnumber);
            //没找到被借调的作业明细，则直接结束借调逻辑
            if (ObjectUtil.isEmpty(needBorrowOperationEqu)) {
                return;
            }
            //查询被借调作业的信息
            ApsProcessOperationDo needBorrowOperation = apsProcessOperationService.getById(needBorrowOperationEqu.getProcessid());
//            //获取该在制品作业明细的ID
//            String processoperationequid = progressWork.getProcessoperationequid();
//            //在制品对应的工序作业输出物料ID
//            String joboutputmaterialid = progressWork.getJoboutputmaterialid();
////                //该对应工序作业的已借调卷数=已借调卷数+1
////                this.baseMapper.processAddSecondRoll(processoperationid);
////                //将在制品的作业输出物料ID对应的所有后道工序作业的所有作业明细的待加工料卷批次号去掉“{批次号}，”
////                this.baseMapper.processDeleteBatchNumber(batchnumber, processoperationid);
//            //查找该在制品作业明细ID对应的后道作业明细中，其工序作业的输入物料ID是该在制品的作业输出物料ID的所有作业明细，
//            //将这些作业明细的待加工料卷批次号去掉“{批次号}，”
//            this.baseMapper.updateBatchNumberByBorrow(batchnumber, processoperationequid, joboutputmaterialid);
//
//
//            //分配被借调作业明细=备料在制品所有后道作业明细中计划加工卷数-已开工卷数-已借调卷数>0的作业明细中，
//            // 优先取已取消卷数>已借调卷数，并且计划开工时间最晚的一个作业明细，
//            // 如果没有已取消卷数>已借调卷数的作业明细，则取计划开工时间最晚的一个作业明细
//
//            //查询被借调作业明细的信息
//            ApsProcessOperationProcessEquDo borrowOperationEqu = apsProcessOperationProcessEquService.getById(processoperationequid);
//            //查询被借调作业的信息
//            ApsProcessOperationDo needBorrowOperation = apsProcessOperationDao.selectNeedBorrowOperation(joboutputmaterialid);
//            List<ApsProcessOperationProcessEquDo> apsProcessOperationProcessEquDoList = apsProcessOperationProcessEquService.list(new QueryWrapper<ApsProcessOperationProcessEquDo>().lambda().eq(ApsProcessOperationProcessEquDo::getPreviousprocessesids, borrowOperationEqu.getId()));
//            //所有后道作业明细中计划加工卷数-已开工卷数-已借调卷数>0的作业明细
//            List<ApsProcessOperationProcessEquDo> collect1 = apsProcessOperationProcessEquDoList.stream().filter(item -> (item.getPlanprocessrall() - item.getStartingroll() - item.getSecondroll()) > 0).collect(Collectors.toList());
//            //优先取已取消卷数>已借调卷数
//            List<ApsProcessOperationProcessEquDo> collect = apsProcessOperationProcessEquDoList.stream().filter(item -> (item.getCancelroll() > item.getSecondroll())).collect(Collectors.toList());
//            ApsProcessOperationProcessEquDo needBorrowOperationEqu = null;
//            if (ObjectUtil.isNotEmpty(collect)) {
//                //并且计划开工时间最晚的一个作业明细
//                needBorrowOperationEqu = collect.stream().sorted(Comparator.comparing(ApsProcessOperationProcessEquDo::getPlanstartdate).reversed()).findFirst().orElse(null);
//            } else {
//                //如果没有已取消卷数>已借调卷数的作业明细，则取计划开工时间最晚的一个作业明细
//                needBorrowOperationEqu = collect1.stream().sorted(Comparator.comparing(ApsProcessOperationProcessEquDo::getPlanstartdate).reversed()).findFirst().orElse(null);
//            }
            if (ObjectUtil.isNotEmpty(needBorrowOperationEqu)) {

                LinkedList<String> oldBatchNumberList = new LinkedList<>(Arrays.asList(needBorrowOperationEqu.getBachmaterialprocess().split(",")));
                oldBatchNumberList.remove(batchnumber);
                String newBatchNumber = "";
                if (ObjectUtil.isNotEmpty(oldBatchNumberList)) {
                    newBatchNumber = String.join(",", oldBatchNumberList);
                }
                //更新明细的已借调卷数
                //更新工序作业的已借调卷数
                Integer secondroll = needBorrowOperationEqu.getSecondroll() + 1;
                ApsProcessOperationProcessEquDo needUpdateProcessEqu = new ApsProcessOperationProcessEquDo();
                needUpdateProcessEqu.setId(needBorrowOperationEqu.getId());
                needUpdateProcessEqu.setSecondroll(secondroll);
                needUpdateProcessEqu.setBachmaterialprocess(newBatchNumber);
                apsProcessOperationProcessEquService.updateById(needUpdateProcessEqu);

                /**
                 * 已借调卷数=已借调卷数+1
                 * 如果已借调卷数>已取消卷数，
                 *  临时计算本次取消卷数=1
                 *  根据被借调作业明细的本次取消卷数取消和更新作业明细，参见【取消和更新作业明细】
                 */
                if (secondroll > needBorrowOperationEqu.getCancelrollno()) {
                    needBorrowOperationEqu.setSecondroll(secondroll);
                    CancelOperationProcessEquVo cancelOperationProcessEquVo = new CancelOperationProcessEquVo();
                    cancelOperationProcessEquVo.setCurEquCancelRoll(1);
                    cancelOperationProcessEquVo.setApsProcessOperationDo(needBorrowOperation);
                    cancelOperationProcessEquVo.setProcessOperationProcessEquDo(needBorrowOperationEqu);
                    cancelOperationProcessEquVo.setCancelreason("1");
                    R r = apsProcessOperationService.cancelProcessOperationEqu(cancelOperationProcessEquVo);
                    if (!r.getCode().equals("200")) {
                        throw new CustomException(r.getMsg());
                    }

                }
            }


            /**
             * 更新被借调作业
             *  已借调卷数=已借调卷数+1，
             *  如果已借调卷数>已取消卷数，
             *      临时计算本次取消卷数=1
             *      已取消卷数=已取消卷数+本次取消卷数
             *      根据被借调作业的本次取消卷数取消和更新该工序作业，参见【取消和更新工序作业】
             */
            if (ObjectUtil.isNotEmpty(needBorrowOperation)) {
                //更新工序作业的一借调卷数
                Integer secondroll = needBorrowOperation.getSecondroll() + 1;
                ApsProcessOperationDo needUpdateProcess = new ApsProcessOperationDo();
                needUpdateProcess.setId(needBorrowOperation.getId());
                needUpdateProcess.setSecondroll(secondroll);
                apsProcessOperationService.updateById(needUpdateProcess);

                /**
                 * 如果已借调卷数>已取消卷数
                 * 本次取消卷数=1
                 * 根据本次取消卷数更新对应工序作业的取消卷数
                 */
                if (secondroll > needBorrowOperation.getCancelroll()) {
                    CancelProcessOperationVo cancelProcessOperationVo = new CancelProcessOperationVo();
                    needBorrowOperation.setSecondroll(secondroll);
                    cancelProcessOperationVo.setApsprocessoperationdo(needBorrowOperation);
                    cancelProcessOperationVo.setCurcancelroll(1);
                    cancelProcessOperationVo.setUpdatecuropration(false);
                    R r = apsProcessOperationService.cancelProcessOperation(cancelProcessOperationVo);
                    if (!r.getCode().equals("200")) {
                        throw new CustomException(r.getMsg());
                    }
                }
                //更新被借调作业及其坯料计划、订单等的生产状态
                CommonUpdateProductionStatusReq req = new CommonUpdateProductionStatusReq();
                req.setProcessOperationEquId(needBorrowOperationEqu.getId());
                apsProcessOperationProcessEquService.updateProductionStatus(req);
//                //撤回订单
//                //被撤回的作业明细
//                ApsProcessOperationProcessEquDo apsProcessOperationProcessEquDo = apsProcessOperationProcessEquService.getById(needBorrowOperationEqu.getId());
//                //被撤回的坯料计划
//                ApsBlankOrderDo changeBlankOrder = apsBlankOrderService.getById(apsProcessOperationProcessEquDo.getBlankid());
//                //被撤回的订单信息
//                ApsProductionOrderDo apsProductionOrderDo = apsProductionOrderService.getById(changeBlankOrder.getProductionorderid());
//                reportCheckService.goBackWf(needBorrowOperationEqu.getId(), apsProcessOperationProcessEquDo.getBlankid(), apsProductionOrderDo);


//                //处理后续工序状态
//                Map<String, Object> params2 = new HashMap<>();
//                params2.put("processId", processoperationid);
//                R r2 = apsProcessOperationService.updateBorrowProcess(params2);
//                if (!r2.getCode().equals("200")) {
//                    return R.error(r2.getMsg());
//                }
            }
        }
    }

    //领料锁
    private final ArrayList<String> checkAddMaterialList = new ArrayList<String>();
    private final List<String> checkAddMaterialLock = Collections.synchronizedList(checkAddMaterialList);

    @Override
    public R checkAddMaterial(AddMaterialReq req) {
        //分割批次号
        List<String> batchNumberList = new LinkedList<>();
//        if (ObjectUtil.isEmpty(req.getBatchNumbers())) {
//            return R.error("批次号不可为空");
//        }
        if (ObjectUtil.isEmpty(req.getBlankId())) {
            return R.error("坯料计划ID为空");
        }
        if (ObjectUtil.isEmpty(req.getProcessId())) {
            return R.error("坯料计划第一道工序作业的ID为空");
        }
        if (ObjectUtil.isNotEmpty(req.getBatchNumbers())) {
            batchNumberList = new LinkedList<>(Arrays.asList(req.getBatchNumbers().split(",")));
        }

        for (String s : batchNumberList) {
            if (checkAddMaterialLock.contains(s)) {
                log.error("批次号" + s + "正被操作，请稍后重试");
                return R.error("批次号" + s + "正被操作，请稍后重试");
            }
        }
        checkAddMaterialLock.addAll(batchNumberList);
        String res = "";
        try {
            //查询坯料计划详情
            ApsBlankOrderDo apsBlankOrderDo = apsBlankOrderService.getById(req.getBlankId());
            //查询坯料计划第一道工序作业的详情
            ApsProcessOperationDo apsProcessOperationDo = apsProcessOperationService.getById(req.getProcessId());
            int needStartRoll = apsProcessOperationDo.getLeavewaitworkroll();
            if (batchNumberList.size() > needStartRoll) {
                return R.error("领料数量不能大于剩余待开工卷数=" + needStartRoll);
            }

            //原来的批次号
            String oldBatchNumbers = req.getOldBatchNumbers();
            LinkedList<String> oldBatchNumberList = new LinkedList<>();
            if (ObjectUtil.isNotEmpty(oldBatchNumbers)) {
                oldBatchNumberList = new LinkedList<>(Arrays.asList(oldBatchNumbers.split(",")));
            }

            //新的批次号集合
            List<String> addBatchNumberList = new LinkedList<>();
            //删除的批次号集合
            List<String> delBatchNumberList = new LinkedList<>();

            for (String s : oldBatchNumberList) {
                if (!batchNumberList.contains(s)) {
                    delBatchNumberList.add(s);
                }
            }
            for (String s : batchNumberList) {
                if (!oldBatchNumberList.contains(s)) {
                    addBatchNumberList.add(s);
                }
            }

            //如果批次号有重复，则错误提示：批次号{批次号}重复
            for (String item : batchNumberList) {
                if (batchNumberList.stream().filter(i -> i.equals(item)).count() > 1) {
                    return R.error("批次号" + item + "重复");
                }
            }


            List<ApsWorkInProgressInventoryDo> workInProgressInventoryDos = new LinkedList<>();
            if (ObjectUtil.isNotEmpty(addBatchNumberList)) {
                //查询新增的批次号
                workInProgressInventoryDos =
                        apsWorkInProgressInventoryService.list(new QueryWrapper<ApsWorkInProgressInventoryDo>().lambda()
                                .eq(ApsWorkInProgressInventoryDo::getDeleted, "0")
                                .in(ApsWorkInProgressInventoryDo::getBatchnumber, addBatchNumberList));
            }


            //线下批次号
            List<String> offLineBatchNumberList = new LinkedList<>();
            //线上批次号 不匹配提示
            List<String> onLineBatchNumberWarnList = new LinkedList<>();

            /**
             * 如果该批次号物料在制品库存中存在
             * 如果在制品备料计划ID不为空，并且不等于该坯料计划ID，错误提示：该坯料已经被其他坯料计划领料
             * 如果在制品状态=待加工或加工中或待检验，错误提示：批次号{批次号}错误，该料卷在加工中
             * 如果在制品状态=待入库或已入库或已出库，并且订单号对应订单的客户的客户类型不是内部客户，错误提示：批次号{批次号}错误，该料卷是其他客户订单的成品
             * 如果在制品状态=备料中，并且在制品类型=不良在制品，错误提示：批次号{批次号}错误，该料卷检验不合格
             * 查找该批次号物料=在制品的作业输出物料ID对应输出物料
             * 如果{该批次号物料的输入物料描述}与{坯料的输入物料描述}不相同，
             * 错误提示：该批次号{批次号}物料：{该批次号物料的输入物料描述}不是该坯料计划所需的坯料{坯料的输入物料描述}
             */

            for (String s : addBatchNumberList) {
                String addBatchNumber = s;
                //如果该批次号物料在制品库存中没找到，则错误提示：批次号{批次号}错误
                ApsWorkInProgressInventoryDo apsWorkInProgressInventoryDo = workInProgressInventoryDos.stream().filter(item -> item.getBatchnumber().equals(addBatchNumber)).findFirst().orElse(null);
                if (ObjectUtil.isEmpty(apsWorkInProgressInventoryDo)) {
                    /**
                     * 如果该批次号没有对应在制品库存，表示线下批次号，
                     * 如果批次号没有前缀*，则加上前缀*
                     * 如果有在制品库存的坯料批次号包含{该批次号+“-”}或{该批次号+“-”}包含在制品库存的坯料批次号+“-”，
                     * 则错误提示：线下批次号{批次号1，批次号2}与其他线下批次号重复
                     * 提示确认：{批次号1，批次号2}是线下批次号，确认领料吗？
                     */
                    if (!s.startsWith("*")) {
                        s = "*" + s;
                    }

                    Integer count = this.baseMapper.selectCountWorkIn(s);
                    if (count > 0) {
                        return R.error("线下批次号" + s + "与其他线下批次号重复");
                    }
                    //查询是否已被其他坯料计划领料
                    ApsBlankOrderDo blankOrderDo = this.baseMapper.countThisBatchNumber(s, req.getBlankId());
                    if (ObjectUtil.isNotEmpty(blankOrderDo)) {
                        return R.error("该批次号" + s + "已被其他坯料计划" + blankOrderDo.getBlanknumber() + "领料");
                    }


                    offLineBatchNumberList.add(s);
                    continue;
                }

                //如果在制品备料计划ID不为空，并且不等于该坯料计划ID，错误提示：该坯料已经被其他坯料计划领料
                if (ObjectUtil.isNotEmpty(apsWorkInProgressInventoryDo.getPlanmaterialid())
                        && !apsWorkInProgressInventoryDo.getPlanmaterialid().equals(req.getBlankId())) {
                    ApsBlankOrderDo blankOrderDo = apsBlankOrderService.getById(apsWorkInProgressInventoryDo.getPlanmaterialid());
                    return R.error("该坯料已经被坯料计划" + blankOrderDo.getBlanknumber() + "领料");
                }

//            //如果在制品类型=不良在制品，错误提示：批次号{批次号}错误，该料卷检验不合格
//            if (apsWorkInProgressInventoryDo.getWorkinprocesstype().equals("不良在制品")) {
//                return R.error("批次号" + s + "错误，该料卷检验不合格");
//            }

                //如果在制品状态=加工中或待检验，错误提示：批次号{批次号}错误，该料卷在加工中
                if (Arrays.asList("加工中", "待检验").contains(apsWorkInProgressInventoryDo.getWorkinprocessstatus())) {
                    return R.error("批次号" + s + "错误，该料卷在加工中");
                }

                //如果在制品状态=待入库或已入库或已出库，并且在制品的订单号对应订单的订单类型=客户订单，错误提示：批次号{批次号}错误，该料卷是其他客户订单的成品
                if (Arrays.asList("待入库", "已入库", "已出库").contains(apsWorkInProgressInventoryDo.getWorkinprocessstatus())) {
                    if (ObjectUtil.isNotEmpty(apsWorkInProgressInventoryDo.getOrdertype()) && apsWorkInProgressInventoryDo.getOrdertype().equals("客户订单")) {
                        return R.error("批次号" + s + "错误，该料卷是其他客户订单的成品");
                    }

                }

//            //查找该批次号物料=在制品的作业输出物料ID对应输出物料
//            //如果{该批次号物料的输入物料描述}与{坯料的输入物料描述}不相同，或者坯料类型不等于坯料计划的坯料类型
//            //错误提示：该批次号{批次号}物料：{该批次号物料的输入物料描述}不是该坯料计划所需的坯料{坯料的输入物料描述}
//            String s1 = apsWorkInProgressInventoryDo.getJoboutputmaterial();
//            String s2 = apsBlankOrderDo.getInputreportdescribe();
//            if (!s1.equals(s2)) {
//                return R.error("该批次号" + s + "物料：" + s1 + "不是该坯料计划所需的坯料" + s2 + "");
//            }

            /*
            如果“{在制品的坯料类型} {在制品的作业输出物料} {在制品的计划输出卷重}吨”
            与“{坯料计划的坯料类型} {坯料输入物料描述} {坯料的卷重}吨”不相同，
            则提示确认：该批次号{批次号}物料：
            “{在制品的坯料类型} {在制品的作业输出物料} {在制品的计划输出卷重}吨”与该坯料计划所需的坯料“{坯料计划的坯料类型} {坯料输入物料描述} {坯料的卷重}吨”不相同，确认领料吗？
             */
                String a = apsWorkInProgressInventoryDo.getBlanktype() + " " + apsWorkInProgressInventoryDo.getJoboutputmaterial() + " " + CXCommonUtils.removeExtraZerosAndDecimal(apsWorkInProgressInventoryDo.getPlansinglerollweight()) + "吨";
                String b = apsBlankOrderDo.getBlanktype() + " " + apsBlankOrderDo.getInputreportdescribe() + " " + CXCommonUtils.removeExtraZerosAndDecimal(apsBlankOrderDo.getSinglerollweight()) + "吨";
                if (!a.equals(b)) {
                    onLineBatchNumberWarnList.add("该批次号" + s + "物料:'" + a + "'与该坯料计划所需的坯料：'" + b + "'不相同，确认领料吗？");
                }

//            //如果在制品的坯料类型不等于坯料计划的坯料类型，
//            // 则错误提示：该批次号{批次号}物料：{在制品的作业输出物料}不是该坯料计划所需的坯料类型
//            if (!apsWorkInProgressInventoryDo.getBlanktype().equals(apsBlankOrderDo.getBlanktype())) {
//                return R.error("该批次号" + s + "物料：" + s1 + "不是该坯料计划所需的坯料类型");
//            }
//
//            //如果在制品的计划输出卷重不等于坯料的卷重，
//            // 提示确认：批次号{批次号1，批次号2}物料卷重与坯料需求的卷重不同，确认领料吗？
//            if (!apsWorkInProgressInventoryDo.getPlansinglerollweight().equals(apsBlankOrderDo.getSinglerollweight())) {
//                onLineBatchNumberWarnList.add(s);
//            }
            }


            if (ObjectUtil.isNotEmpty(offLineBatchNumberList)) {
                res = "<b>" + String.join(",", offLineBatchNumberList) + "是线下批次号，确认领料吗？</b><br>";
            }
            if (ObjectUtil.isNotEmpty(onLineBatchNumberWarnList)) {
                for (String s : onLineBatchNumberWarnList) {
                    res = res + "<b>" + s + "</b>";
                }
            }
        } finally {
            log.debug("领料校验完毕，从list中清空当前领料锁，批次号：" + batchNumberList);
            checkAddMaterialLock.removeAll(batchNumberList);
        }
        return R.ok(res);
    }

    @Override
    public void apsScheduleRestart() {
        ApsUtils.checkScheduling(null);
        String needScheduleDate = "";
        DateTime nowDate = DateUtil.date();
        String time1 = DateUtil.format(nowDate, "yyyy-MM-dd 00:00:00");
        String time2 = DateUtil.format(nowDate, "yyyy-MM-dd 12:00:00");
        String time3 = DateUtil.format(nowDate, "yyyy-MM-dd 23:59:59");
        String now = DateUtil.format(nowDate, "yyyy-MM-dd HH:mm:ss");
        //如果当前时间在0-12点，重排明天的
        //如果当前时间在12-24点，重排后天的
        if (now.compareTo(time1) >= 0 && now.compareTo(time2) < 0) {
            needScheduleDate = DateUtil.format(DateUtil.offsetDay(nowDate, 1), "yyyy-MM-dd");
        }
        if (now.compareTo(time2) >= 0 && now.compareTo(time3) <= 0) {
            needScheduleDate = DateUtil.format(DateUtil.offsetDay(nowDate, 2), "yyyy-MM-dd");
        }

        if (ObjectUtil.isNotEmpty(needScheduleDate)) {
            //将实际完工时间大于明天的作业挪到实际完工时间
            this.baseMapper.updateFinishDateAboutProcessEqu(needScheduleDate);
            //查询在重排时间前一天之后的所有作业明细信息,不包括：铸轧、成退、中退、小卷成退
            List<ApsProcessOperationProcessEquDo> list = this.baseMapper.selectNeedRescheduleEqu(needScheduleDate);
            //查询重排时间之后的所有作业明细
            String finalNeedScheduleDate = needScheduleDate;
            List<ApsProcessOperationProcessEquDo> needScheduleList = list.stream().filter(item ->
                    item.getPlanstartdate().compareTo(DateUtil.parseDate(finalNeedScheduleDate)) >= 0
                    && item.getPlanstartdate().compareTo(DateUtil.offsetDay(DateUtil.parseDate(finalNeedScheduleDate),1)) <= 0
            ).collect(Collectors.toList());
            //查询重排时间前一天的所有作业明细
            List<ApsProcessOperationProcessEquDo> beforeScheduleList = list.stream().filter(item -> item.getPlanstartdate().compareTo(DateUtil.parseDate(finalNeedScheduleDate)) < 0).collect(Collectors.toList());
            //重排时间前一天的所有作业明细ID
            List<String> afterScheduleIdList = beforeScheduleList.stream().map(ApsProcessOperationProcessEquDo::getId).collect(Collectors.toList());
            for (ApsProcessOperationProcessEquDo apsProcessOperationProcessEquDo : needScheduleList) {
                //前道作业明细ID不为空
                if (ObjectUtil.isNotEmpty(apsProcessOperationProcessEquDo.getPreviousprocessesids())){
                    //如果存在
                    if (afterScheduleIdList.contains(apsProcessOperationProcessEquDo.getPreviousprocessesids())){

                    }
                }
            }


            //解锁明天有开工作业明细的坯料计划的所有作业明细
            this.baseMapper.removeLockNextDayProcessEqu(needScheduleDate);
            //解锁后天开工的所有作业明细
            this.baseMapper.removeLockNextTwoDayProcessEqu(needScheduleDate);
            //获取排程类型
            ApsScheduleConfigDo apsScheduleConfigDo = apsScheduleConfigService.getById("1");
            String scheduletype = apsScheduleConfigDo.getScheduletype();
            ApsTypeVo apsTypeVo = BeanUtil.toBean(scheduletype, ApsTypeVo.class);
            apsTypeVo.setMixIf("mix");
            apsTypeVo.setThMergeMode("notThMergeBf");
            apsTypeVo.setConstraintMode("lz");
            //重新排程
            apsService.apsSchedule(new ArrayList<>(), apsTypeVo, null);
        }
    }
}
