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

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.date.DateField;
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.extension.service.impl.ServiceImpl;
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.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.SimpleDateFormat;
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.safecommon.utils.SaveConstans.DatePattern.DATE_PATTERN_YMDHMS;
import static com.rongwei.safecommon.utils.SaveConstans.JobStatus.JOBSTATUS_TO_BE_START;
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.SchedulingStatus.PUBLISHED;
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 {

    private static final Logger logger = LoggerFactory.getLogger(ApsBlankOrderServiceImpl.class);

    @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;

    public static String removeExtraZerosAndDecimal(BigDecimal bd) {
        bd = bd.stripTrailingZeros();
        return bd.toPlainString();
    }

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

    @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<ApsBlankOrderDo> apsBlankOrderDos = this.baseMapper.selectBatchIds(blankIds);
        List<String> searchCondition = new ArrayList<String>() {{
            add("30");
            add("20");
            add("10");
        }};
        List<ApsBlankOrderDo> collect1 = apsBlankOrderDos.stream().filter(info -> !searchCondition.contains(info.getProductstatus())).collect(Collectors.toList());
        if (!collect1.isEmpty()) {
            logger.error("存在已开工的坯料计划：{}", collect1);
            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("请先解决冲突在发布");
        }
        // 更新坯料计划的生产状态为已发布
        this.update(new LambdaUpdateWrapper<ApsBlankOrderDo>().in(ApsBlankOrderDo::getId, blankIds)
                .set(ApsBlankOrderDo::getProductstatus, "30")
                .set(ApsBlankOrderDo::getSchedulingstatus, PUBLISHED)
                .set(ApsBlankOrderDo::getLockmark, LOCKMARK_Y));
        // 更新坯料计划对应工序作业的状态为待开工
        apsProcessOperationService.update(new LambdaUpdateWrapper<ApsProcessOperationDo>()
                .in(ApsProcessOperationDo::getBlankid, blankIds)
                .set(ApsProcessOperationDo::getProcessstatus, "30")
                .set(ApsProcessOperationDo::getLockmark, LOCKMARK_Y)
                .set(ApsProcessOperationDo::getWorkstatus, JOBSTATUS_TO_BE_START));
        // 更新坯料计划对应产品明细的生产状态为待开工
        apsProductDetailDao.updateProductStatusByBlankId(blankIds, "30");
        // 更新工序作业明细的生产状态为待开工
        processOperationProcessEquService.update(new LambdaUpdateWrapper<ApsProcessOperationProcessEquDo>()
                .in(ApsProcessOperationProcessEquDo::getId, processOperationProcessEquDos.stream().map(ApsProcessOperationProcessEquDo::getId).collect(Collectors.toList()))
                .set(ApsProcessOperationProcessEquDo::getLockmark, LOCKMARK_Y)
                .set(ApsProcessOperationProcessEquDo::getWorkstatus, JOBSTATUS_TO_BE_START)
        );
        List<String> collect = apsBlankOrderDos.stream().map(ApsBlankOrderDo::getProductionorderid).distinct().collect(Collectors.toList());
        // 更新工序作业的状态
        apsProcessOperationDao.publish(collect);
        // 更新订单表的生产状态
        blankIds.forEach(blankId -> {
            processOperationProcessEquService.updateOrder(blankId);
        });
        // 根据坯料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);
        // 根据坯料ID 删除 备份数据
        ApsUtils.clearBackup(null);
        return R.ok();
    }

    /*
    插单
     */
    @Override
    public R insertOrder(Map<String, Object> params) throws Exception {
        String productionOrderId = params.get("PRODUCTIONORDERID").toString();
        String blankOrderId = params.get("BLANKORDERID").toString();
        String deliveryDateStr = params.get("DELIVERYDATE").toString();
        SimpleDateFormat formatter = new SimpleDateFormat(DATE_PATTERN_YMDHMS);
        Date deliveryDate = formatter.parse(deliveryDateStr);

        //更新订单期望交货期
        ApsProductionOrderDo productionOrderDo = apsProductionOrderService.getById(productionOrderId);
        productionOrderDo.setDeliverydate(deliveryDate);
        boolean bool1 = apsProductionOrderService.updateById(productionOrderDo);

        // 更新坯料计划的生产状态为已发布
        boolean bool2 = this.update(new LambdaUpdateWrapper<ApsBlankOrderDo>().eq(ApsBlankOrderDo::getId, blankOrderId)
                .set(ApsBlankOrderDo::getDeliverydate, deliveryDate)
                .set(ApsBlankOrderDo::getLockmark, LOCKMARK_N));

        // 解锁订单所有作业状态=待开工的作业明细（是否锁定=否）
        boolean bool3 = apsProcessOperationService.update(new LambdaUpdateWrapper<ApsProcessOperationDo>()
                .eq(ApsProcessOperationDo::getBlankid, blankOrderId)
                .eq(ApsProcessOperationDo::getWorkstatus, JOBSTATUS_TO_BE_START)
                .set(ApsProcessOperationDo::getLockmark, LOCKMARK_N));

        if (bool1 && bool2 && bool3) {
            //重新排程
            this.apsReScheduling();
            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> blackOrderId = orderLockVo.getBlackOrderId();
        blackOrderId = blackOrderId.stream().filter(StringUtils::isNotBlank).collect(Collectors.toList());
        Date lockStartTime = orderLockVo.getLockStartTime();
        if (blackOrderId.isEmpty() && lockStartTime == null) {
            log.error("坯料计划ID和解锁时间都为空");
            return R.error("坯料计划ID和解锁时间都为空");
        }

        if (!blackOrderId.isEmpty()) {
            // 校验坯料计划是否开工
            Integer integer = apsBlankOrderDao.selectCount(new LambdaQueryWrapper<ApsBlankOrderDo>()
                    .in(ApsBlankOrderDo::getId, blackOrderId)
                    .in(ApsBlankOrderDo::getProductstatus, "40", "50"));
            if (integer > 0) {
                log.error("所选作业坯料计划已开工，不可以解锁");
                return R.error("所选作业坯料计划已开工，不可以解锁");
            }
        }
        if (lockStartTime != null) {
            // 获取排程参数设置
            List<ApsScheduleConfigDo> apsConfigs = apsScheduleConfigService.list();
            // 默认最小排程时间
            int defaultStartStartSchedulerun = 8;
            if (!apsConfigs.isEmpty()) {
                defaultStartStartSchedulerun = apsConfigs.get(0).getStartschedulerun();
            }
            Date date = orderLockGetSearChDate(lockStartTime, defaultStartStartSchedulerun, lockMark);
            // 获取所有 待发布 待开工的 坯料计划
            blackOrderId = this.baseMapper.getIdByLockTime(date, lockMark);
        }
        if (blackOrderId.isEmpty()) {
            log.debug("暂无需要锁定或者解锁的批量计划");
            return R.ok("暂无需要锁定或者解锁的批量计划");
        }
        // 组装待更新坯料计划实体
        List<ApsBlankOrderDo> blankOrderDos = new ArrayList<>();

        for (String id : blackOrderId) {
            ApsBlankOrderDo blankOrderDo = new ApsBlankOrderDo();
            blankOrderDo.setId(id);
            blankOrderDo.setLockmark(lockMark);
            if (LOCKMARK_N.equals(lockMark)) {
                blankOrderDo.setProductstatus("20");
            }
            blankOrderDos.add(blankOrderDo);
        }
        // 坯料计划锁定标识更新
        if (blankOrderDos.size() > 0) {
            this.updateBatchById(blankOrderDos);
        }
        // 工序作业锁定标识更新
        apsProcessOperationService.update(new LambdaUpdateWrapper<ApsProcessOperationDo>()
                .set(ApsProcessOperationDo::getLockmark, lockMark)
                .in(ApsProcessOperationDo::getBlankid, blackOrderId));
        // 工序作业排程计划锁定
        processOperationProcessEquService.update(new LambdaUpdateWrapper<ApsProcessOperationProcessEquDo>()
                .set(ApsProcessOperationProcessEquDo::getLockmark, lockMark)
                .in(ApsProcessOperationProcessEquDo::getBlankid, blackOrderId));
        // 解锁
        if (LOCKMARK_N.equals(lockMark)) {
            // 更新坯料计划对应工序作业的生产状态为待发布
            apsProcessOperationService.update(new LambdaUpdateWrapper<ApsProcessOperationDo>()
                    .in(ApsProcessOperationDo::getBlankid, blackOrderId)
                    .set(ApsProcessOperationDo::getProcessstatus, "20")
            );
            apsBlankOrderService.update(new LambdaUpdateWrapper<ApsBlankOrderDo>()
                    .in(ApsBlankOrderDo::getId, blackOrderId)
                    .set(ApsBlankOrderDo::getSchedulingstatus, "20"));
        }
        // 回传坯料计划ID 用于前段更新锁定状态
        return R.ok(blackOrderId);
    }

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

//        List<ApsBlankOrderVo> notLockOrders = apsBlankOrderDao.getNotLockOrders(tenantId);
        List<ApsBlankOrderVo> notLockOrders = new ArrayList<>();
        blankOrderAps(notLockOrders);
        //更新生产状态为“待发布”（20）
        updateProductStatusByOrderIds(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);
        //更新生产状态为“待发布”（20）
        updateProductStatusByOrderIds(resBlankList);
    }

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

    /**
     * 排程结果操作
     *
     * @param productionScheduleRetVo
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void apsAfter(ProductionScheduleRetVo productionScheduleRetVo, List<ApsBlankOrderVo> apsBlankOrders) {
        // 先删除所有作业的明细
        List<String> blankids = apsBlankOrders.stream().map(v -> v.getId()).collect(Collectors.toList());
        processOperationProcessEquService.remove(new LambdaUpdateWrapper<ApsProcessOperationProcessEquDo>().in(ApsProcessOperationProcessEquDo::getBlankid, blankids));
        // 排程结果保存
        List<ApsProcessOperationProcessEquDo> apsProcessOperationProcessEquDos = new ArrayList<>();
        // 最后节点集合
        List<ProductionProcesses> lastProcess = new ArrayList<>();
        if (productionScheduleRetVo.getProcesses() != null && productionScheduleRetVo.getProcesses().size() > 0) {
            for (ProductionProcesses process : productionScheduleRetVo.getProcesses()) {
                // 非合并生产工序
                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()));
                    apsProcessOperationProcessEqu.setPlanprocessrall(1);
                    apsProcessOperationProcessEqu.setHasconflict(process.getHasConflict());
                    apsProcessOperationProcessEqu.setConflictdes(process.getConflictDes());
                    apsProcessOperationProcessEqu.setSoftconflictdes(process.getSoftconflictdes());
                    apsProcessOperationProcessEqu.setDeleted("0");
                    apsProcessOperationProcessEqu.setUnfinishroll(apsProcessOperationProcessEqu.getPlanprocessrall());
                    apsProcessOperationProcessEqu.setPreviousprocessesids(CollUtil.join(process.getPreviousProcessesIds(), ","));
                    apsProcessOperationProcessEqu.setNextprocessesids(CollUtil.join(process.getNextProcessesIds(), ","));
                    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);
            // 回写订单排程交货期
            Map<String, Date> orderDeliveryTime = new HashMap<>();
            // 坯料计划ID集合
            List<ApsBlankOrderDo> blankIds = new ArrayList<>();
            for (ProductionProcesses process : lastProcess) {
                List<ProduceOrder> produceOrder = process.getProduceOrder();
                if (produceOrder != null && produceOrder.size() > 0) {
                    for (ProduceOrder order : produceOrder) {
                        Date endDate = Date.from(process.getEndTime().atZone(ZoneId.systemDefault()).toInstant());
                        ApsBlankOrderDo blankOrder = this.getById(order.getId());
                        ApsBlankOrderDo updateBlank = new ApsBlankOrderDo();
                        updateBlank.setId(order.getId());
                        updateBlank.setSchedulingstatus(TO_BE_PUBLISHED);
                        updateBlank.setScheduledateend(endDate);
                        updateBlank.setPromisedateend(endDate);
                        blankIds.add(updateBlank);
                        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);
            }
        }

        backupProcessOperation(apsProcessOperationProcessEquDos, productionScheduleRetVo.getProcesses());
    }

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


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

        /*****************************开始工序作业备份********************************/
        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)
        );
        // 清空作业明细
        processOperationProcessEquService.remove(new LambdaUpdateWrapper<ApsProcessOperationProcessEquDo>()
                .in(ApsProcessOperationProcessEquDo::getBlankid, blankids));
        // 更新生产订单的状态
        apsProductionOrderService.update(new LambdaUpdateWrapper<ApsProductionOrderDo>()
                .in(ApsProductionOrderDo::getId, apsProductionOrderIds)
                .set(ApsProductionOrderDo::getScheduledeliverydate, null)
                .set(ApsProductionOrderDo::getProductionstatus, TO_BE_SCHEDULED));
        // 更新坯料计划的状态
        this.baseMapper.cancelSchedulingUpdate(blankids);
        //获取坯料计划用到的产品明细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();
        //获取当前用户信息
        //获取当前用户的所属工厂
        SysUserVo currentUser = CXCommonUtils.getCurrentUser();
        String tenantId = currentUser.getOrganizationDoList().get(0).getFullpid().split(",")[1];


        //=============校验坯料长度=================
        //如果坯料需求的外观类型是板锭或铝板，并且坯料长度为空，则提示：坯料是板锭和铝板时长度必填
        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("坯料是铝板和板锭时长度必填");
//                }
//            }
//        }
        if (checkLevel == 2 || checkLevel == 3) {
            //==============拼接工艺路线输出成品和工艺输出物料规格==============
            //查询坯料计划对应工艺路线的输出成品
            List<String> processOutputList = this.baseMapper.selectProcessByBlankId(apsBlankOrderDo.getId());
            if (processOutputList != null && processOutputList.size() > 0) {
                //工艺路线输出成品
                String outputProcessMaterial = "";
                //排序
                processOutputList.sort(String::compareTo);
                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(String::compareTo).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);
                    //统一输出的重量
                    //公斤/千克
                    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("输出成品：" + 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.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}{输出单卷重}{数量单位}
        //组装一下list
        List<String> processOutputDesList = apsProcessOutputProductDoList.stream().map(item -> {
            String unit = "吨";
            if (item.getCountunit().equals(SaveConstans.CountUnit.KG)) {
                unit = "千克";
            }
            return item.getProductname() + item.getOutputonerollweigth() + unit;
        }).collect(Collectors.toList());

        //用于判断是否重复
        List<String> checkOutputDesList = new LinkedList<>();
        //有重复数据订单产品的数组
        List<String> endCheckOutputDesList = new LinkedList<>();
        for (String s : processOutputDesList) {
            if (checkOutputDesList.contains(s)) {
                endCheckOutputDesList.add(s);
            } else {
                checkOutputDesList.add(s);
            }
        }
        if (endCheckOutputDesList.size() > 0) {
            String join = String.join(",", endCheckOutputDesList);
            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.getPreparematerial().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("工艺路线错误，首道工序作业是坯料工序，备料不能作为输入物料");
                        }
                        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("请选择工艺路线");
            }
        }

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

        //更新或保存坯料计划和输出成品
        apsBlankOrderDo.setTenantid(tenantId);
        this.saveOrUpdate(apsBlankOrderDo);
        for (ApsProcessOutputProductDo apsProcessOutputProductDo : apsProcessOutputProductDoList) {
            apsProcessOutputProductDo.setBlankid(apsBlankOrderDo.getId());
            apsProcessOutputProductDo.setTenantid(tenantId);
        }
        apsProcessOutputProductService.saveOrUpdateBatch(apsProcessOutputProductDoList);

        //如果在制品不为空，并且为新增备料
        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);

            //借调逻辑
            if (ObjectUtil.isNotEmpty(progressWork) && ObjectUtil.isNotEmpty(progressWork.getWorkinprocessstatus()) && progressWork.getWorkinprocessstatus().equals(SaveConstans.WorkInProgressStatus.WAIT_WORK)) {
                //获取该在制品的工序作业ID
                String processoperationid = progressWork.getProcessoperationid();
                //获取该在制品的批次号
                String batchnumber = progressWork.getBatchnumber();
                //获取该在制品作业明细的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);
                //处理后续工序状态
                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());
                }
            }
        }

        return R.ok();
    }

    public R checkBlankOrder(CheckAndSaveOrUpdateBlankReq req, int j) {
        //获取校验等级
        Integer checkLevel = req.getCheckLevel();
        //获取坯料计划内容
        ApsBlankOrderDo apsBlankOrderDo = req.getApsBlankOrderDo();
        //获取当前用户信息
        //获取当前用户的所属工厂
        SysUserVo currentUser = CXCommonUtils.getCurrentUser();
        String tenantId = currentUser.getOrganizationDoList().get(0).getFullpid().split(",")[1];


        //=============校验坯料长度=================
        //如果坯料需求的外观类型是板锭或铝板，并且坯料长度为空，则提示：坯料是板锭和铝板时长度必填
        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("第" + j + "个坯料计划 坯料是铝板和铝锭时长度必填");
        }
//        //如果坯料产品类型对应产品外观类型是铝板或板锭，并且坯料长度为空，则提示：坯料是铝板和板锭时长度必填
//        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("第" + j + "个坯料计划 坯料是铝板和板锭时长度必填");
//                }
//            }
//        }
        if (checkLevel == 3) {
            //==============校验交期==================
            /**
             * 如果有评审日志，并且变更内容为空，则错误提示：请填写变更内容(暂未实现)
             * 如果有任一坯料计划的承诺交货期-起或止为空，错误提示：请填写所有坯料计划的承诺交货期
             * 如果有坯料计划的承诺交货期-止 < 排程交货期，错误提示：第{几}个坯料计划的承诺交货期-止不能早于排程交货期{排程交货期}
             */
            if (req.getCheckDevice()) {
                if (ObjectUtil.isEmpty(apsBlankOrderDo.getPromisedateend())) {
                    return R.error("请填写所有坯料计划的承诺交货期-止");
                }
                if (ObjectUtil.isEmpty(apsBlankOrderDo.getScheduledateend())) {
                    return R.error("第" + j + "个坯料计划 排程交货期-止不能为空");
                }
                if (apsBlankOrderDo.getPromisedateend().compareTo(apsBlankOrderDo.getScheduledateend()) < 0) {
                    return R.error("第" + j + "个坯料计划的承诺交货期-止不能早于排程交货期-止" + DateUtil.format(apsBlankOrderDo.getScheduledateend(), "yyyy-MM-dd"));
                }
            }
        }

        if (checkLevel == 2 || checkLevel == 3) {
            //==============拼接工艺路线输出成品==============
            //查询坯料计划对应工艺路线的输出成品
            List<String> processOutputList = this.baseMapper.selectProcessByBlankId(apsBlankOrderDo.getId());
            if (processOutputList != null && processOutputList.size() > 0) {
                //工艺路线输出成品
                String outputProcessMaterial = "";
                //排序
                processOutputList.sort(String::compareTo);
                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(String::compareTo).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("第" + j + "个坯料计划 工艺路线没有输出成品");
            }
        }

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

                    //输出的单卷重
                    BigDecimal outputWeight = new BigDecimal(0);
                    //坯料的单卷重
                    BigDecimal blankWeight = new BigDecimal(0);
                    //统一输出的重量
                    //公斤/千克
                    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("第" + j + "个坯料计划 输出成品：" + apsProcessOutputProductDo.getProductname() + " 输出单卷重不能超过坯料单卷重");
                    }
                } else {//不存在对应的产品明细
                    return R.error("第" + j + "个坯料计划 输出成品：" + 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("第" + j + "个坯料计划 输出成品：" + apsProcessOutputProductDo.getProductname() + " 计划输出订单重量 不能大于 订单总数量");
//                    }
                }
            }
            if (!haveProductDetail && apsProcessOutputProductDo.getPlanoutputonerollweight().compareTo(new BigDecimal(0)) > 0) {
                return R.error("第" + j + "个坯料计划 输出成品：" + apsProcessOutputProductDo.getProductname() + " 已修改或已删除，并已开工，计划输出订单重量 不能大于 0");
            }

            //如果输出成品技术要求的坯料外观类型是铝板或铝锭，并且长度为空，则提示：输出成品是板锭和铝板时长度必填
            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("第" + j + "个坯料计划 输出成品：" + apsProcessOutputProductDo.getProductname() + " 是板锭和铝板时长度必填");
            }
        }

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

        //如果存在订单产品和输出单卷重都相同坯料输出成品，错误提示：以下坯料输出成品重复：{订单产品1}{输出单卷重}{数量单位}，{订单产品2}{输出单卷重}{数量单位}
        //组装一下list
        List<String> processOutputDesList = apsProcessOutputProductDoList.stream().map(item -> {
            String unit = "吨";
            if (item.getCountunit().equals(SaveConstans.CountUnit.KG)) {
                unit = "千克";
            }
            return item.getProductname() + item.getOutputonerollweigth() + unit;
        }).collect(Collectors.toList());

        //用于判断是否重复
        List<String> checkOutputDesList = new LinkedList<>();
        //有重复数据订单产品的数组
        List<String> endCheckOutputDesList = new LinkedList<>();
        for (String s : processOutputDesList) {
            if (checkOutputDesList.contains(s)) {
                endCheckOutputDesList.add(s);
            } else {
                checkOutputDesList.add(s);
            }
        }
        if (endCheckOutputDesList.size() > 0) {
            String join = String.join(",", endCheckOutputDesList);
            return R.error("第" + j + "个坯料计划 以下坯料输出成品重复：" + 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("第" + j + "个坯料计划 坯料输出成品的计划输出订单重量合计不能大于坯料的总重量");
        }


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


        if (checkLevel == 3) {
            //============校验工艺路线=============
            //如果坯料需求的坯料输出成品字段值不等于坯料需求的工艺输出成品字段值，错误提示：工艺路线输出成品与坯料输出成品不一致
            if (!apsBlankOrderDo.getOutputfinishproduct().equals(apsBlankOrderDo.getOutputmaterial())) {
                return R.error("第" + j + "个坯料计划 工艺路线输出成品" + apsBlankOrderDo.getOutputmaterial() + "与坯料输出成品" + apsBlankOrderDo.getOutputfinishproduct() + "不一致");
            }
        }
        //如果坯料需求的是否备料=是，并且首道工序作业的是否坯料工序=是，错误提示：工艺路线错误，首道工序作业是坯料工序，备料不能作为输入物料
        //是否备料
        if (apsBlankOrderDo.getPreparematerial().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("第" + j + "个坯料计划 工艺路线错误，首道工序作业是坯料工序，备料不能作为输入物料");
                    }
                    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("第" + j + "个坯料计划 请选择工艺路线");
            }
        }

        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("第" + j + "个坯料计划 " + r.getMsg());
            }
        }
        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());
        removeBlankOrderAndChild(apsBlankOrderDo);

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

        //如果是否备料=是，备料对应在制品的备料计划ID=(空)，在制品状态=备料中
        if (apsBlankOrderDo.getPreparematerial().equals("是")) {
            apsWorkInProgressInventoryService.update(new UpdateWrapper<ApsWorkInProgressInventoryDo>().lambda()
                    .set(ApsWorkInProgressInventoryDo::getBlankid, "")
                    .set(ApsWorkInProgressInventoryDo::getWorkinprocessstatus, SaveConstans.WorkInProgressStatus.WAITING)
                    .eq(ApsWorkInProgressInventoryDo::getBlankid, 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()));

        //逻辑删除坯料计划的工艺路线
        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()) ? removeExtraZerosAndDecimal(apsProcessOutputProductDo.getThickness()) : "";
                    String prowidth = ObjectUtil.isNotEmpty(apsProcessOutputProductDo.getProwidth()) ? removeExtraZerosAndDecimal(apsProcessOutputProductDo.getProwidth()) : "";
                    String prolength = ObjectUtil.isNotEmpty(apsProcessOutputProductDo.getProlength()) ? 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().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().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 + " " + removeExtraZerosAndDecimal(item.getOutputonerollweigth()) + "吨 * " + item.getPlanoutputrollcount() + "卷 = " + 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().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().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(List<String> blankIdList) {
        //是否正在排程
        ApsUtils.checkScheduling(null);

        //判断当前坯料计划是否存在排程备份


        return null;
    }
}
