package com.xisc.pm.app.service.strategy.pdmattype;

import com.google.common.collect.Maps;
import com.xisc.boot.common.feign.pojo.MtUserInfo;
import com.xisc.boot.common.sys.UserClient;
import com.xisc.pm.api.dto.PmBoardMatInfoDTO;
import com.xisc.pm.api.dto.PmCompositeBlockDTO;
import com.xisc.pm.api.dto.PmCompositeSlabCalcDTO;
import com.xisc.pm.api.dto.PmCutPlaceDTO;
import com.xisc.pm.api.dto.PmPdRecDTO;
import com.xisc.pm.api.dto.PmProContFilterRequestDTO;
import com.xisc.pm.api.dto.PmProContFilterResposeDTO;
import com.xisc.pm.api.dto.PmProContYieldDTO;
import com.xisc.pm.api.dto.PmProContYieldRequestDTO;
import com.xisc.pm.api.dto.PmProContYieldResposeDTO;
import com.xisc.pm.api.dto.PmSmallPlateDTO;
import com.xisc.pm.api.dto.RuleParamDTO;
import com.xisc.pm.api.dto.ctevent.ProContBoardEndDTO;
import com.xisc.pm.api.dto.ctevent.ProContOverDTO;
import com.xisc.pm.app.service.PmPdRecService;
import com.xisc.pm.app.service.PmPdRuleSetService;
import com.xisc.pm.app.service.strategy.FormPlateModeStrategyFactory;
import com.xisc.pm.app.service.strategy.SecondaryDesignHelper;
import com.xisc.pm.app.service.strategy.formplatemode.FormPlateModeStrategy;
import com.xisc.pm.domain.entity.PmAxisBrdTrimBt;
import com.xisc.pm.domain.entity.PmContMscTech;
import com.xisc.pm.domain.entity.PmPdBlockPos;
import com.xisc.pm.domain.entity.PmPdContSum;
import com.xisc.pm.domain.entity.PmPdPlateCut;
import com.xisc.pm.domain.entity.PmPlateCutLenBt;
import com.xisc.pm.domain.entity.PmPlateTrimBt;
import com.xisc.pm.domain.entity.PmProContProdTtr;
import com.xisc.pm.domain.entity.PmSlabSpecBt;
import com.xisc.pm.domain.entity.PmSmallPltPos;
import com.xisc.pm.domain.entity.PmSpeStAddvBt;
import com.xisc.pm.domain.entity.PmThickAddvBt;
import com.xisc.pm.domain.repository.MmPlateRepository;
import com.xisc.pm.domain.repository.PmAxisBrdTrimBtRepository;
import com.xisc.pm.domain.repository.PmContMscTechRepository;
import com.xisc.pm.domain.repository.PmCutPlaceBtRepository;
import com.xisc.pm.domain.repository.PmMidSlabAddvBtRepository;
import com.xisc.pm.domain.repository.PmPdBlockPosRepository;
import com.xisc.pm.domain.repository.PmPdContSumRepository;
import com.xisc.pm.domain.repository.PmPdPlateCutRepository;
import com.xisc.pm.domain.repository.PmPdTmpYieldBtRepository;
import com.xisc.pm.domain.repository.PmPlateCutLenBtRepository;
import com.xisc.pm.domain.repository.PmPlateTrimBtRepository;
import com.xisc.pm.domain.repository.PmProContProdTtrRepository;
import com.xisc.pm.domain.repository.PmQdToPoOrderRepository;
import com.xisc.pm.domain.repository.PmSlabScrapLenBtRepository;
import com.xisc.pm.domain.repository.PmSlabSpecBtRepository;
import com.xisc.pm.domain.repository.PmSmallPltPosRepository;
import com.xisc.pm.domain.repository.PmSpeStAddvBtRepository;
import com.xisc.pm.domain.repository.PmThickAddvBtRepository;
import com.xisc.pm.domain.repository.PmWmMatPileRepository;
import com.xisc.pm.domain.vo.QdToPoOrderVO1;
import com.xisc.pm.infra.client.EventClient;
import com.xisc.pm.infra.constant.Constants;
import com.xisc.pm.infra.constant.Constants.PdStatusHp;
import com.xisc.pm.infra.constant.CtEventConstants;
import com.xisc.pm.infra.constant.PmPlateConstants;
import com.xisc.pm.infra.constant.RuleSetConstants;
import com.xisc.pm.infra.enums.CutPositionType;
import com.xisc.pm.infra.enums.CutType;
import com.xisc.pm.infra.enums.FormPlateMode;
import com.xisc.pm.infra.enums.PdEventType;
import com.xisc.pm.infra.enums.PdMatType;
import com.xisc.pm.infra.enums.PdProdFlag;
import com.xisc.pm.infra.enums.PdRuleSetType;
import com.xisc.pm.infra.mapper.QdToPoOrderMapper;
import com.xisc.pm.infra.util.BeanUtils;
import io.choerodon.core.exception.CommonException;
import io.choerodon.core.oauth.CustomUserDetails;
import io.choerodon.core.oauth.DetailsHelper;
import org.apache.commons.lang3.StringUtils;
import org.hzero.core.base.BaseConstants;
import org.hzero.core.message.Message;
import org.hzero.core.message.MessageAccessor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cglib.beans.BeanCopier;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

/**
 * 组板材料类型策略公共实现抽象类
 * 
 * @author jun.zeng
 *
 */
public abstract class DefaultPdMatTypeStrategy<T> implements PdMatTypeStrategy {

    @Autowired
    protected PmCutPlaceBtRepository pmCutPlaceBtRepository;

    @Autowired
    protected PmSlabSpecBtRepository pmSlabSpecBtRepository;

    @Autowired
    protected PmPlateTrimBtRepository pmPlateTrimBtRepository;
    
    @Autowired
    protected PmThickAddvBtRepository pmThickAddvBtRepository;

    @Autowired
    protected PmPlateCutLenBtRepository pmPlateCutLenBtRepository;

    @Autowired
    protected PmSpeStAddvBtRepository pmSpeStAddvBtRepository;

    @Autowired
    protected PmQdToPoOrderRepository pmQdToPoOrderRepository;

    @Autowired
    protected PmSlabScrapLenBtRepository pmSlabScrapLenBtRepository;

    @Autowired
    protected PmWmMatPileRepository pmWmMatPileRepository;

    @Autowired
    protected MmPlateRepository mmPlateRepository;

    @Autowired
    protected PmPdTmpYieldBtRepository pmPdTmpYieldBtRepository;

    @Autowired
    protected PmPdPlateCutRepository pmPdPlateCutRepository;

    @Autowired
    protected PmPdRuleSetService pmPdRuleSetService;

    @Autowired
    protected PmProContProdTtrRepository pmProContProdTtrRepository;

    @Autowired
    protected PmPdContSumRepository pmPdContSumRepository;

    @Autowired
    protected PmSmallPltPosRepository pmSmallPltPosRepository;

    @Autowired
    protected PmPdBlockPosRepository pmPdBlockPosRepository;

    @Autowired
    protected PmContMscTechRepository pmContMscTechRepository;

    @Autowired
    protected PmPdRecService pmPdRecService;

    @Autowired
    protected EventClient eventClient;
    
    @Autowired
    private PmAxisBrdTrimBtRepository pmAxisBrdTrimBtRepository;
    
    @Autowired
    private PmMidSlabAddvBtRepository pmMidSlabAddvBtRepository;
    
    @Autowired
    private QdToPoOrderMapper qdToPoOrderMapper;

    @Autowired
    private UserClient userClient;

    @Override
    public boolean contPublicConditionsCheck(PmProContFilterResposeDTO pmProContFilterResposeDTO, Long tenantId) {
        // 处理合同公用条件检查函数
        BigDecimal userdBoardNum = pmProContFilterResposeDTO.getUserdBoardNum() == null ? BigDecimal.ZERO
                : pmProContFilterResposeDTO.getUserdBoardNum();
        if (pmProContFilterResposeDTO.getOnPdFlag() != null
                && BaseConstants.Flag.YES.longValue() == pmProContFilterResposeDTO.getOnPdFlag()) {
            // 正在组板中
            if (StringUtils.isNotBlank(pmProContFilterResposeDTO.getPdPlanner())
                    || StringUtils.isNotBlank(pmProContFilterResposeDTO.getPdProgram())) {
                // 合同【{0}】正在组板，组板程序【{1}】，组板责任者【{2}】
                Map<Long, MtUserInfo> mtUserInfoMap = userClient.userInfoBatchGet(tenantId,
                        Arrays.asList(Long.parseLong(pmProContFilterResposeDTO.getPdPlanner())));
                String loginName = mtUserInfoMap.containsKey(Long.parseLong(pmProContFilterResposeDTO.getPdPlanner())) ?
                        mtUserInfoMap.get(Long.parseLong(pmProContFilterResposeDTO.getPdPlanner())).getLoginName(): pmProContFilterResposeDTO.getPdPlanner();
                Message message = MessageAccessor.getMessage("pm.pm_pro_cont_010",
                        new Object[] { pmProContFilterResposeDTO.getOrderNo(), pmProContFilterResposeDTO.getPdProgram(),
                                loginName });
                pmProContFilterResposeDTO.setUnSelectedReason(message.getDesc());
                return false;
            }
        }
        // 如果合同欠件小于零，直接结束
        if (BigDecimal.ZERO.compareTo(userdBoardNum) > 0) {
            // 合同【{0}】无欠量
            Message message = MessageAccessor.getMessage("pm.pm_pro_cont_012",
                    new Object[] { pmProContFilterResposeDTO.getOrderNo(), pmProContFilterResposeDTO.getPdProgram(),
                            pmProContFilterResposeDTO.getPdPlanner() });
            pmProContFilterResposeDTO.setUnSelectedReason(message.getDesc());
            return false;
        }
        if (BigDecimal.ZERO.compareTo(userdBoardNum) == 0 && (!pdProgram().equals(pmProContFilterResposeDTO.getPdProgram())
                || !DetailsHelper.getUserDetails().getUserId().toString().equals(pmProContFilterResposeDTO.getPdPlanner()))) {
            // 合同【{0}】正在被其他程序组板!
            Message message = MessageAccessor.getMessage("pm.pm_pro_cont_012",
                    new Object[] { pmProContFilterResposeDTO.getOrderNo(), pmProContFilterResposeDTO.getPdProgram(),
                            pmProContFilterResposeDTO.getPdPlanner() });
            pmProContFilterResposeDTO.setUnSelectedReason(message.getDesc());
            return false;
        }

        if (pmProContFilterResposeDTO.getPdHoldFlag() != null
                && BaseConstants.Flag.YES.longValue() == pmProContFilterResposeDTO.getPdHoldFlag()) {
            // 合同【{0}】已处于合同封锁状态!
            Message message = MessageAccessor.getMessage("pm.pm_pro_cont_011",
                    new Object[] { pmProContFilterResposeDTO.getOrderNo() });
            pmProContFilterResposeDTO.setUnSelectedReason(message.getDesc());
            return false;
        }
        return true;
    }

    @Override
    public PmCompositeSlabCalcDTO checkAndInitParams(Map<String, PmProContFilterResposeDTO> filterOrderMap,
            PmProContYieldRequestDTO pmProContYield, PmProContProdTtr axisPmProContProdTtr) {
        return getFormPlateModeStrategy(pmProContYield).checkAndInitParams(filterOrderMap, pmProContYield,
                axisPmProContProdTtr);
    }

    @Override
    public void plateCut(Map<String, PmProContFilterResposeDTO> filterOrderMap, PmProContYieldRequestDTO pmProContYield,
            PmProContProdTtr axisPmProContProdTtr, PmCompositeSlabCalcDTO compositeSlabCalcDTO) {
        String rollLineNo = getRollLineNo(pmProContYield.getPmContFilterRequest());
        // 读取粗切位置决定基准表
        PmCutPlaceDTO pmCutPlaceDTO = pmCutPlaceBtRepository.queryCutPlace(getFormPlateMode(pmProContYield), rollLineNo,
                axisPmProContProdTtr, pmProContYield.getTenantId());
        compositeSlabCalcDTO.setCutMax(pmCutPlaceDTO.getCutMax());
        compositeSlabCalcDTO.setCutMin(pmCutPlaceDTO.getCutMin());

        // 异宽粗切位置数组
        compositeSlabCalcDTO.setDiffCut(compositeSlabCalcDTO.getDiffLoc());
        // 定义每块小板终点位置
        BigDecimal endPos = BigDecimal.ZERO;
        BigDecimal commonCutSegment = BigDecimal.ZERO;
        if (compositeSlabCalcDTO.getMLen() != null && !compositeSlabCalcDTO.getMLen().isEmpty()) {
            for (int i = 0; i < compositeSlabCalcDTO.getMLen().size(); i++) {
                BigDecimal mLen = compositeSlabCalcDTO.getMLen().get(i);
                if (mLen.compareTo(compositeSlabCalcDTO.getCutMax()) > 0) {
                    throw new CommonException("pm.pm_pro_cont_019", compositeSlabCalcDTO.getCutMax().toPlainString());
                }
                endPos = endPos.add(mLen);
                // 添加分段累计和
                compositeSlabCalcDTO.getSum().add(endPos);
                if (compositeSlabCalcDTO.getDiffLoc().contains(i)) {
                    // 重新开始统计命令粗切汇总初始值
                    commonCutSegment = mLen;
                    continue;
                }
                commonCutSegment = commonCutSegment.add(mLen);
                if (commonCutSegment.compareTo(compositeSlabCalcDTO.getCutMax()) > 0) {
                    // 如果小板长度累计大于cut_max 且不是异宽位置，当前下标切一刀
                    compositeSlabCalcDTO.getCommandCut().add(i);
                    // 重新开始统计命令粗切汇总初始值
                    commonCutSegment = mLen;
                }
            }
        }
    }

    @Override
    public void calcSampleLen(PmProContYieldRequestDTO pmProContYield, PmProContProdTtr axisPmProContProdTtr,
            PmCompositeSlabCalcDTO pmCompositeSlabCalc) {
        BigDecimal ltpt = null;
        BigDecimal ltpm = null;
        BigDecimal sampleLenHead = axisPmProContProdTtr.getSampleLenHead();
        BigDecimal sampleLenTail = axisPmProContProdTtr.getSampleLenTail();

        if ("B".equals(axisPmProContProdTtr.getSampleModeCode())) {
            sampleLenHead = BigDecimal.ZERO;
            // 尾部试材长度
            ltpt = axisPmProContProdTtr.getSampleLenTail();
        } else if ("T".equals(axisPmProContProdTtr.getSampleModeCode())) {
            sampleLenTail = BigDecimal.ZERO;
            // 头部试样长度
            ltpt = axisPmProContProdTtr.getSampleLenHead();
        } else if ("A".equals(axisPmProContProdTtr.getSampleModeCode())) {
            // 头部+尾部
            ltpt = axisPmProContProdTtr.getSampleLenHead().add(axisPmProContProdTtr.getSampleLenTail());
        } else {
            ltpt = BigDecimal.ZERO;
            sampleLenTail = BigDecimal.ZERO;
            sampleLenHead = BigDecimal.ZERO;
        }
        if ("A".equals(axisPmProContProdTtr.getPickPatternCode())) {
            int planCutNum = pmCompositeSlabCalc.getDiffCut().size() + pmCompositeSlabCalc.getCommandCut().size();
            ltpm = ltpt.multiply(BigDecimal.valueOf(planCutNum));
        } else {
            ltpm = BigDecimal.ZERO;
        }
        pmCompositeSlabCalc.setSampleLenHead(sampleLenHead);
        pmCompositeSlabCalc.setSampleLenTail(sampleLenTail);
        pmCompositeSlabCalc.setSampleLenMid(ltpm);
        pmCompositeSlabCalc.setSampleLength(ltpt.add(ltpm));
    }

    @Override
    public void calcRsCodeAndBroadFlag(PmProContYieldRequestDTO pmProContYield, PmProContProdTtr axisPmProContProdTtr,
            PmCompositeSlabCalcDTO pmCompositeSlabCalc) {
        // 获取切边量
        List<PmPlateTrimBt> pmPlateTrimBts = pmPlateTrimBtRepository.selectMaxTrimWidth(pmProContYield.getTenantId(),
                axisPmProContProdTtr.getProdSideMark(), axisPmProContProdTtr.getOrderThick(),
                pmProContYield.getCompositeBoard().getSzWidth(), pmProContYield.getPmContFilterRequest().getMoldWidth(),
                pmProContYield.getPmContFilterRequest().getMscLineNo());
        if (pmPlateTrimBts == null || pmPlateTrimBts.isEmpty()) {
            // 未匹配到钢板切边量基准表,切边标记【{0}】,轴合同厚度【{1}】,组合板宽【{2}】,结晶器【{3}】，设计产线号【{4}】
            throw new CommonException("pm.pm_pro_cont_020" , axisPmProContProdTtr.getProdSideMark() 
                    , axisPmProContProdTtr.getOrderThick().toPlainString() ,pmProContYield.getCompositeBoard().getSzWidth().toPlainString() 
                    , pmProContYield.getPmContFilterRequest().getMoldWidth().toPlainString() , pmProContYield.getPmContFilterRequest().getMscLineNo());
        }
        BigDecimal trimWidth = null;
        // 取小板宽度切边量的最大值
        for (PmPlateTrimBt pmPlateTrimBt : pmPlateTrimBts) {
            for (BigDecimal mLen : pmCompositeSlabCalc.getMLen()) {
                if (mLen.compareTo(pmPlateTrimBt.getSmallPlateLenFrom()) >= 0
                        && mLen.compareTo(pmPlateTrimBt.getSmallPlateLenTo()) <= 0) {
                    if (trimWidth == null || pmPlateTrimBt.getTrimWidth().compareTo(trimWidth) > 0) {
                        trimWidth = pmPlateTrimBt.getTrimWidth();
                    }
                }
            }
        }
        if(trimWidth == null) {
            // 未匹配到钢板切边量基准表,切边标记【{0}】,轴合同厚度【{1}】,组合板宽【{2}】,结晶器【{3}】，设计产线号【{4}】
            throw new CommonException("pm.pm_pro_cont_020" , axisPmProContProdTtr.getProdSideMark() 
                    , axisPmProContProdTtr.getOrderThick().toPlainString() ,pmProContYield.getCompositeBoard().getSzWidth().toPlainString() 
                    , pmProContYield.getPmContFilterRequest().getMoldWidth().toPlainString() , pmProContYield.getPmContFilterRequest().getMscLineNo());
        }
        pmCompositeSlabCalc.setTrimWidth(trimWidth);
        if (pmProContYield.getCompositeBoard().getSzWidth().add(trimWidth)
                .compareTo(pmProContYield.getPmContFilterRequest().getMoldWidth()) <= 0) {
            pmCompositeSlabCalc.setBroadFlag(BaseConstants.Flag.NO.toString());
            pmCompositeSlabCalc.setRsCode(3);
        } else {
            pmCompositeSlabCalc.setRsCode(0);
            pmCompositeSlabCalc.setBroadFlag(BaseConstants.Flag.YES.toString());
        }
    }

    @Override
    public void calcLpre(PmProContYieldRequestDTO pmProContYield, PmProContProdTtr axisPmProContProdTtr,
            PmCompositeSlabCalcDTO pmCompositeSlabCalc) {
        if (pmProContYield.getPmContFilterRequest().getFixSlabLenFlag() != null
                && pmProContYield.getPmContFilterRequest().getFixSlabLenFlag().intValue() == 1) {
            pmCompositeSlabCalc.setLpre(pmProContYield.getPmContFilterRequest().getFixSlabLen());
        } else if (pmProContYield.getPmContFilterRequest().getFixSlabLenFlag() != null
                && pmProContYield.getPmContFilterRequest().getFixSlabLenFlag().intValue() == 2) {
            pmCompositeSlabCalc.setLpre(pmProContYield.getPmContFilterRequest().getSampleLenGice());
        } else if (FormPlateMode.G.getType().equals(getFormPlateMode(pmProContYield).getType())) {
            // TODO
            // 如果组板方式不为AB型
            // 临时板坯长度 = 轴合同厚度 * 组合板宽 * 组合板长/(结晶器厚度 * 结晶器宽度 * 临时收得率)
            // lpre = order_thick * combine_plate_width * combine_plate_len/(mold_thick *
            // mold_width * TMP_YIELD)
//            pmPdTmpYieldBtRepository.selectByCondition(Condition.builder(PmPdTmpYieldBt.class)
//                    .andWhere(Sqls.custom().andEqualTo(PmPdTmpYieldBt.FIELD_TENANT_ID, pmProContYield.getTenantId())
//                            .andEqualTo(PmPdTmpYieldBt.FIELD_CC_TYPE, pmProContYield.getPmContFilterRequest().getCcType())
//                            .andEqualTo(PmPdTmpYieldBt.FIELD_PROD_CODE_HP, axisPmProContProdTtr.getProdCodeHp())
//                            .andEqualTo(PmPdTmpYieldBt.FIELD_WITH_SIDE_FLAG, axisPmProContProdTtr.getProdSideMark())
//
//                            ).build());
        }
    }

    @Override
    public PmProContYieldResposeDTO calcYield(Map<String, PmProContFilterResposeDTO> filterOrderMap,
            PmProContYieldRequestDTO pmProContYield, PmProContProdTtr axisPmProContProdTtr,
            PmContMscTech axisPmContMscTech, PmCompositeSlabCalcDTO pmCompositeSlabCalc) {
        PmProContYieldResposeDTO yieldRespose = new PmProContYieldResposeDTO();
        yieldRespose.setTenantId(pmProContYield.getTenantId());
        yieldRespose.setCombinePlateWidth(pmProContYield.getCompositeBoard().getSzWidth());
        yieldRespose.setCombinePlateLen(pmProContYield.getCompositeBoard().getSzLength());
        yieldRespose.setBroadFlag(pmCompositeSlabCalc.getBroadFlag());
        yieldRespose.setRsCode(pmCompositeSlabCalc.getRsCode());
        BeanCopier copier = BeanCopier.create(PmProContFilterResposeDTO.class, PmProContProdTtr.class, false);
        BeanCopier copierContMscTech = BeanCopier.create(PmProContFilterResposeDTO.class, PmContMscTech.class, false);
        Map<String, PmProContProdTtr> pmProContProdTtrs = new HashMap<String, PmProContProdTtr>();
        Map<String, PmContMscTech> pmContMscTechs = new HashMap<String, PmContMscTech>();

        // 通过参数匹配厚度余量
        PmThickAddvBt pmThickAddvBt = pmThickAddvBtRepository.selectTaimAddv(pmProContYield.getTenantId(), axisPmProContProdTtr.getOrderNo()
                , axisPmContMscTech.getWholeHeatTreatmentType(), pmProContYield.getPmContFilterRequest().getMscLineNo()
                , axisPmProContProdTtr.getOrderThick(), pmProContYield.getCompositeBoard().getSzWidth());
        if (null == pmThickAddvBt) {
            // 没有匹配到厚度余量基准表数据，公差类型代码【THK_TOL_TYPE】，轴合同热处理类型【WHOLE_HEAT_TREATMENT_TYPE】，轴合同订货厚度【order_thick】，组合板宽度【combine_plate_width】
            throw new CommonException("pm.pm_pro_cont_021");
        }

        // 轧制目标厚度taim
        BigDecimal maxTaim = null;
        BigDecimal minTaim = null;
        // 合同最大头部或者尾部切舍量
        BigDecimal maxOrderheadCut = null;
        BigDecimal maxOrderTailCut = null;
        // 合同厚度是否是否一致
        boolean thkEqualsFlag = true ;
        // 公差交集下限是否一致
        boolean minTaimFlag = true;
        
        for (Entry<String, PmProContFilterResposeDTO> entry : filterOrderMap.entrySet()) {
            PmProContProdTtr pmProContProdTtr = new PmProContProdTtr();
            PmContMscTech pmContMscTech = new PmContMscTech();
            copier.copy(entry.getValue(), pmProContProdTtr, null);
            pmProContProdTtrs.put(entry.getKey(), pmProContProdTtr);
            copierContMscTech.copy(entry.getValue(), pmContMscTech, null);
            pmContMscTechs.put(entry.getKey(), pmContMscTech);
            if(thkEqualsFlag && axisPmProContProdTtr.getOrderThick().compareTo(pmProContProdTtr.getOrderThick()) != 0) {
                // 如果合同厚度不等于轴合同订货厚度需要取交集的中间值
                thkEqualsFlag = false ;
            }
            if(minTaimFlag && axisPmProContProdTtr.getThickTolMin().compareTo(pmProContProdTtr.getThickTolMin()) != 0) {
                minTaimFlag = false ;
            }
            // 合同订货区间
            BigDecimal thickMin = pmProContProdTtr.getOrderThick().add(pmProContProdTtr.getThickTolMin());
            BigDecimal thickMax = pmProContProdTtr.getOrderThick().add(pmProContProdTtr.getThickTolMax());
            // 记录交集
            if (maxTaim == null || maxTaim.compareTo(thickMax) > 0) {
                maxTaim = thickMax;
            }
            if (minTaim == null || minTaim.compareTo(thickMin) < 0) {
                minTaim = thickMin;
            }
            if (maxOrderheadCut == null || maxOrderheadCut.compareTo(pmContMscTech.getHeadCut()) < 0) {
                maxOrderheadCut = pmContMscTech.getHeadCut();
            }
            if (maxOrderTailCut == null || maxOrderTailCut.compareTo(pmContMscTech.getTailCut()) < 0) {
                maxOrderTailCut = pmContMscTech.getTailCut();
            }
        }
        
        if(filterOrderMap.size() == 1) {
            // 当为一个合同时，轧制目标厚度=订货厚度+厚度公差下限+厚度余量
            yieldRespose.setTaim(minTaim.add(pmThickAddvBt.getThickAddv()));
        } else if(filterOrderMap.size() > 1 && thkEqualsFlag && minTaimFlag) {
            // 当为多个合同且下公差相同时，轧制目标厚度=订货厚度+厚度公差下限+厚度余量
            yieldRespose.setTaim(minTaim.add(pmThickAddvBt.getThickAddv()));
        } else if(filterOrderMap.size() > 1 && thkEqualsFlag && !minTaimFlag) {
            // 当为多个合同且下公差不同时，计算每个合同的区间[订货厚度+厚度公差下限，订货厚度+合同厚度公差上限]，取各区间交集，轧制目标厚度=交集的下限+厚度余量，
            // 当轧制目标厚度＜交集的上限时允许通过，当轧制目标厚度≥交集的上限时不允许通过。
            yieldRespose.setTaim(minTaim.add(pmThickAddvBt.getThickAddv()));
            if (yieldRespose.getTaim().compareTo(maxTaim) >= 0) {
                throw new CommonException("pm.pm_pro_cont_022");
            }
        } else {
            // 当为多个合同且多个厚度时，取出每个合同的区间[订货厚度+厚度公差下限，订货厚度+合同厚度公差上限], 取各区间交集。若交集的上限-交集的下限>厚度余量，则通过，
            // 轧制目标厚度=两个交集的中间值。否则，不通过，报错：多个合同厚度不同且超过厚度余量范围，不能组在一起。
            BigDecimal diff = maxTaim.subtract(minTaim);
            if(diff.compareTo(pmThickAddvBt.getThickAddv()) <= 0) {
                throw new CommonException("pm.pm_pro_cont_056");
            }
            yieldRespose.setTaim(minTaim.add(diff.divide(BigDecimal.valueOf(2L) , PmPlateConstants.WT_SCALE , RoundingMode.HALF_UP)));
        }
        
        yieldRespose.setRollThickTolMax(maxTaim.subtract(axisPmProContProdTtr.getOrderThick()));
        yieldRespose.setRollThickTolMin(minTaim.subtract(axisPmProContProdTtr.getOrderThick()));
        // 钢板切缝切头尾范围基准表数据
        PmPlateCutLenBt plateCutLenBt = pmPlateCutLenBtRepository.selectPmPlateCutLenBt(pmProContYield,
                axisPmProContProdTtr, axisPmContMscTech, pmCompositeSlabCalc.getBroadFlag(),
                pmProContYield.getPmContFilterRequest().getMscLineNo());
        if (plateCutLenBt == null || plateCutLenBt.getPlateCutWidth() == null
                || plateCutLenBt.getHeadCut() == null
                || plateCutLenBt.getTailCut() == null) {
            throw new CommonException("pm.pm_pro_cont_023");
        }
        // 轧制目标宽度
        if (pmCompositeSlabCalc.getRsCode() == 3) {
            yieldRespose.setWaim(pmProContYield.getPmContFilterRequest().getMoldWidth());
        } else {
            // 轧制目标宽度=组合板宽度 + (轴合同)宽度余量 + 切边量 +（取板方式_宽度方向-1） × 钢板切缝宽度
            BigDecimal waim = yieldRespose.getCombinePlateWidth().add(axisPmProContProdTtr.getWidthAddv() == null ? BigDecimal.ZERO : axisPmProContProdTtr.getWidthAddv())
                    .add(pmCompositeSlabCalc.getTrimWidth());
            BigDecimal pickModeWidDir = BigDecimal.valueOf(pmProContYield.getCompositeBoard().getPickModeWidDir())
                    .subtract(BigDecimal.ONE);
            yieldRespose.setWaim(waim.add(pickModeWidDir.multiply(plateCutLenBt.getPlateCutWidth())));
        }
        // 轧制目标长度laim
        // 组合板长度 + 试样长度 + 组板基表头部切舍量 + 组板基表尾部切舍量 +（取板方式_长度方向-1）* 长方向切缝量 +
        // MAX（大板上所有合同的头部切舍量）+ MAX（大板上所有合同的尾部切舍量）
        // 长方向切缝量 == 钢板切缝宽度
        BigDecimal headCut = plateCutLenBt.getHeadCut().add(maxOrderheadCut);
        BigDecimal tailCut = plateCutLenBt.getTailCut().add(maxOrderTailCut);
        BigDecimal pickModeLenDir = BigDecimal.valueOf(pmProContYield.getCompositeBoard().getPickModeLenDir())
                .subtract(BigDecimal.ONE);
        BigDecimal laim = yieldRespose.getCombinePlateLen().add(pmCompositeSlabCalc.getSampleLength())
                .add(headCut).add(tailCut)
                .add(pickModeLenDir.multiply(plateCutLenBt.getPlateCutWidth()));
        yieldRespose.setLaim(laim);
        yieldRespose.setHeadCut(headCut);
        yieldRespose.setTailCut(tailCut);
        // (2) 基准收得率norm_yield = （订货厚度*组合板宽度*组合板长度）*（1-氧化铁皮损失率）÷（轧制目标长度*轧制目标宽度*轧制目标厚度）
        yieldRespose.setNormYield(calcNormYield(axisPmProContProdTtr.getOrderThick(), yieldRespose));

        List<PmSpeStAddvBt> pmSpeStAddvBts = pmSpeStAddvBtRepository.selectSlabAddv(pmProContYield.getTenantId(),
                pmProContYield.getPmContFilterRequest().getMscLineNo(),
                pmProContYield.getPmContFilterRequest().getStNo(), pmProContYield.getPmContFilterRequest().getSgSign(),
                pmProContYield.getPmContFilterRequest().getMoldThick(), yieldRespose.getTaim());
        if (pmSpeStAddvBts != null && !pmSpeStAddvBts.isEmpty()) {
            yieldRespose.setSlabAddv(pmSpeStAddvBts.get(0).getSlabAddv());
        }

        calc(pmProContYield, axisPmProContProdTtr, pmCompositeSlabCalc, pmProContProdTtrs, yieldRespose);

        // 出钢材，余坯校验限制收得率
        checkCommandYield(yieldRespose,pmProContYield);

        return yieldRespose;
    }

    protected void checkCommandYield(PmProContYieldResposeDTO yieldRespose, PmProContYieldRequestDTO pmProContYield) {
    }

    /**
     * 计算基准收得率
     * 基准收得率norm_yield = （订货厚度*组合板宽度*组合板长度）*（1-氧化铁皮损失率）÷（轧制目标长度*轧制目标宽度*轧制目标厚度）
     * @param orderThick
     * @param yieldRespose
     * @return
     */
    private BigDecimal calcNormYield(BigDecimal orderThick ,PmProContYieldResposeDTO yieldRespose) {
        BigDecimal compositeSlabArea = orderThick.multiply(yieldRespose.getCombinePlateLen())
                .multiply(yieldRespose.getCombinePlateWidth()).multiply(PmPlateConstants.IRON_OXIDE_EFFECT_RATE);
        BigDecimal targetSlabArea = yieldRespose.getLaim().multiply(yieldRespose.getWaim())
                .multiply(yieldRespose.getTaim());
        return compositeSlabArea.divide(targetSlabArea, PmPlateConstants.YIELD_SCALE, RoundingMode.HALF_UP);
    }

    /**
     * 计算（余坯，出钢材计算逻辑）
     *
     * @param pmProContYield
     * @param axisPmProContProdTtr
     * @param pmCompositeSlabCalc
     * @param pmProContProdTtrs
     * @param yieldRespose
     */
    public void calc(PmProContYieldRequestDTO pmProContYield, PmProContProdTtr axisPmProContProdTtr,
            PmCompositeSlabCalcDTO pmCompositeSlabCalc, Map<String, PmProContProdTtr> pmProContProdTtrs,
            PmProContYieldResposeDTO yieldRespose) {
        // 板坯厚,板坯宽
        BigDecimal slabTaim = pmProContYield.getPmContFilterRequest().getMoldThick();
        BigDecimal slabWaim = pmProContYield.getPmContFilterRequest().getMoldWidth();
        // 密度（克/cm^3）
        BigDecimal prodDensity = axisPmProContProdTtr.getProdDensity();

        // 轧制有效重量(克) = 轴合同订货厚度 × 组合宽 × 组合长 × 密度
        BigDecimal effectWt = axisPmProContProdTtr.getOrderThick().multiply(yieldRespose.getCombinePlateLen())
                .multiply(yieldRespose.getCombinePlateWidth()).multiply(prodDensity)
                .divide(BigDecimal.valueOf(1000), PmPlateConstants.TEM_SCALE, RoundingMode.HALF_UP);

        // 必要板坯重量(克) = 轧制有效重量÷基准收得率 = (轧制目标厚 * 轧制目标宽 * 轧制目标长 * 密度) / (1-s1);
        BigDecimal needSlabWtGram = effectWt.divide(yieldRespose.getNormYield(), PmPlateConstants.TEM_SCALE,
                RoundingMode.HALF_UP);

        // 宽，厚转化为cm - 密度单位转化为 克/cm
        BigDecimal waimAndTaimAndDensity = slabTaim.multiply(slabWaim).multiply(prodDensity)
                .divide(BigDecimal.valueOf(100), PmPlateConstants.TEM_SCALE, RoundingMode.HALF_UP);

        // 必要板坯长度（cm) = 必要板坯重量(克) ÷（板坯厚度 × 板坯宽度 × 密度）;
        // 转化为毫米
        BigDecimal needSlabLen = needSlabWtGram.multiply(BigDecimal.TEN).divide(waimAndTaimAndDensity,
                PmPlateConstants.LEN_SCALE, RoundingMode.HALF_UP);
        
        // 单位克
        BigDecimal infurSlabWtGram = null;
        boolean fixSlabLenFlag = false;
        if (pmProContYield.getPmContFilterRequest().getFixSlabLenFlag() == 1) {
            fixSlabLenFlag = true;
            if(SecondaryDesignHelper.isTwoRollFlag()) {
                // 如果是二次开坯
                pmProContYield.getPmContFilterRequest().setFixSlabLen(needSlabLen);
            }
            // 指定长
            if (needSlabLen.compareTo(pmProContYield.getPmContFilterRequest().getFixSlabLen()) > 0) {
                throw new CommonException("pm.pm_pro_cont_024" , pmProContYield.getPmContFilterRequest().getFixSlabLen().toPlainString() , needSlabLen.toPlainString());
            }
            yieldRespose.setPreCleanSlabLen(pmProContYield.getPmContFilterRequest().getFixSlabLen());
            yieldRespose.setInfurSlabLen(pmProContYield.getPmContFilterRequest().getFixSlabLen());
            
            infurSlabWtGram = yieldRespose.getPreCleanSlabLen().multiply(waimAndTaimAndDensity)
                    .divide(PmPlateConstants.CM_TO_MM, PmPlateConstants.TEM_SCALE, RoundingMode.HALF_UP);

            // 进加热炉板坯重量(吨)
            yieldRespose.setInfurSlabWt(yieldRespose.getPreCleanSlabLen().multiply(waimAndTaimAndDensity)
                    .divide(PmPlateConstants.TEN_GRAM_TO_TON, PmPlateConstants.WT_SCALE, RoundingMode.HALF_UP));
            // 清理前(吨)
            yieldRespose.setPreCleanSlabWt(yieldRespose.getPreCleanSlabLen().multiply(waimAndTaimAndDensity)
                    .divide(PmPlateConstants.TEN_GRAM_TO_TON, PmPlateConstants.WT_SCALE, RoundingMode.HALF_UP));

            yieldRespose.setNeedSlabLen(needSlabLen);
            yieldRespose.setNeedSlabWt(needSlabWtGram.divide(PmPlateConstants.GRAM_TO_TON , PmPlateConstants.WT_SCALE , RoundingMode.HALF_UP));
        } else if (pmProContYield.getPmContFilterRequest().getFixSlabLenFlag() == 2) {
            fixSlabLenFlag = true;
            if(SecondaryDesignHelper.isTwoRollFlag()) {
                // 如果是指定长
                pmProContYield.getPmContFilterRequest().setFixSlabLen(needSlabLen);
            }
            // 奉送样
            if (needSlabLen.compareTo(pmProContYield.getPmContFilterRequest().getSampleLenGice()) > 0) {
                throw new CommonException("pm.pm_pro_cont_024", pmProContYield.getPmContFilterRequest().getSampleLenGice().toPlainString() , needSlabLen.toPlainString());
            }
            yieldRespose.setPreCleanSlabLen(pmProContYield.getPmContFilterRequest().getSampleLenGice());
            yieldRespose.setInfurSlabLen(pmProContYield.getPmContFilterRequest().getSampleLenGice());

            infurSlabWtGram = yieldRespose.getPreCleanSlabLen().multiply(waimAndTaimAndDensity)
                    .divide(PmPlateConstants.CM_TO_MM, PmPlateConstants.TEM_SCALE, RoundingMode.HALF_UP);

            // 吨
            yieldRespose.setInfurSlabWt(yieldRespose.getPreCleanSlabLen().multiply(waimAndTaimAndDensity)
                    .divide(PmPlateConstants.TEN_GRAM_TO_TON, PmPlateConstants.WT_SCALE, RoundingMode.HALF_UP));
            // 吨
            yieldRespose.setPreCleanSlabWt(yieldRespose.getPreCleanSlabLen().multiply(waimAndTaimAndDensity)
                    .divide(PmPlateConstants.TEN_GRAM_TO_TON, PmPlateConstants.WT_SCALE, RoundingMode.HALF_UP));
            yieldRespose.setNeedSlabLen(needSlabLen);
            yieldRespose.setNeedSlabWt(needSlabWtGram.divide(PmPlateConstants.GRAM_TO_TON , PmPlateConstants.WT_SCALE , RoundingMode.HALF_UP));
        } else {
            infurSlabWtGram = noFixSlabLenCalc(pmProContYield, yieldRespose,axisPmProContProdTtr, effectWt ,waimAndTaimAndDensity , needSlabLen , infurSlabWtGram , needSlabWtGram);
        }
        
        // 厚板轧制重量roll_wt_hp = 轧制目标厚度(mm) * 轧制目标宽度(mm) * 轧制目标长度(mm) * 密度
        // 转化为吨
        yieldRespose.setRollWtHp(yieldRespose.getTaim().multiply(yieldRespose.getWaim())
                .multiply(yieldRespose.getLaim()).multiply(prodDensity)
                .divide(BigDecimal.valueOf(1000L * 1000L * 1000L), PmPlateConstants.WT_SCALE, RoundingMode.HALF_UP));
        
        if (yieldRespose.getNeedSlabLen().compareTo(yieldRespose.getPreCleanSlabLen()) > 0
                || yieldRespose.getNeedSlabLen().compareTo(yieldRespose.getInfurSlabLen()) > 0) {
            throw new CommonException("pm.pm_pro_cont_047" , yieldRespose.getPreCleanSlabLen().toPlainString() , yieldRespose.getInfurSlabLen().toPlainString() , yieldRespose.getNeedSlabLen().toPlainString());
        }

        // 计算长度重量下限
        yieldRespose.setPreCleanSlabMinLen(yieldRespose.getPreCleanSlabLen());
        yieldRespose.setPreCleanSlabMinWt(yieldRespose.getPreCleanSlabWt());
        yieldRespose.setInfurSlabMinLen(yieldRespose.getInfurSlabLen());
        yieldRespose.setInfurSlabMinWt(yieldRespose.getInfurSlabWt());
        // 计算长度重量上限
        if (Constants.RollDirectCode.C.equals(pmProContYield.getPmContFilterRequest().getRollDirectCode())) {
            if (pmCompositeSlabCalc.getRsCode() == 3) {
                // 无展宽
                yieldRespose.setPreCleanSlabMaxLen(yieldRespose.getPreCleanSlabLen());
                yieldRespose.setPreCleanSlabMaxWt(yieldRespose.getPreCleanSlabWt());
                yieldRespose.setInfurSlabMaxLen(yieldRespose.getInfurSlabLen());
                yieldRespose.setInfurSlabMaxWt(yieldRespose.getInfurSlabWt());
            } else {
                // 展宽
                yieldRespose
                        .setPreCleanSlabMaxLen(yieldRespose.getPreCleanSlabLen().add(PmPlateConstants.SLAB_LEN_DIFF));
                yieldRespose.setInfurSlabMaxLen(yieldRespose.getInfurSlabLen().add(PmPlateConstants.SLAB_LEN_DIFF));
                yieldRespose.setPreCleanSlabMaxWt(yieldRespose.getPreCleanSlabMaxLen().multiply(waimAndTaimAndDensity)
                        .divide(PmPlateConstants.TEN_GRAM_TO_TON, PmPlateConstants.WT_SCALE, RoundingMode.HALF_UP));
                yieldRespose.setInfurSlabMaxWt(yieldRespose.getInfurSlabMaxLen().multiply(waimAndTaimAndDensity)
                        .divide(PmPlateConstants.TEN_GRAM_TO_TON, PmPlateConstants.WT_SCALE, RoundingMode.HALF_UP));
            }
        } else {
            if (fixSlabLenFlag) {
                yieldRespose
                        .setPreCleanSlabMaxLen(yieldRespose.getPreCleanSlabLen().add(PmPlateConstants.SLAB_LEN_DIFF));
                yieldRespose.setInfurSlabMaxLen(yieldRespose.getInfurSlabLen().add(PmPlateConstants.SLAB_LEN_DIFF));
                yieldRespose.setPreCleanSlabMaxWt(yieldRespose.getPreCleanSlabMaxLen().multiply(waimAndTaimAndDensity)
                        .divide(PmPlateConstants.TEN_GRAM_TO_TON, PmPlateConstants.WT_SCALE, RoundingMode.HALF_UP));
                yieldRespose.setInfurSlabMaxWt(yieldRespose.getInfurSlabMaxLen().multiply(waimAndTaimAndDensity)
                        .divide(PmPlateConstants.TEN_GRAM_TO_TON, PmPlateConstants.WT_SCALE, RoundingMode.HALF_UP));
            } else {
                yieldRespose.setPreCleanSlabMaxLen(yieldRespose.getPreCleanSlabLen());
                yieldRespose.setPreCleanSlabMaxWt(yieldRespose.getPreCleanSlabWt());
                yieldRespose.setInfurSlabMaxLen(yieldRespose.getInfurSlabLen());
                yieldRespose.setInfurSlabMaxWt(yieldRespose.getInfurSlabWt());
            }
        }

        FormPlateModeStrategy formPlateModeStrategy = getFormPlateModeStrategy(pmProContYield);
        // 轧制收得率 ＝ 轧制有效重量 ÷ 进加热炉板坯重量
        yieldRespose.setRollYield(effectWt.divide(infurSlabWtGram, PmPlateConstants.YIELD_SCALE, RoundingMode.HALF_UP));
        // 命令收得率 ＝Σ合同单重 ÷ 进加热炉板坯重量
        BigDecimal sumPlateWt = formPlateModeStrategy.getSumPlateWt(pmProContProdTtrs, pmProContYield);
        yieldRespose.setSumPlateWt(sumPlateWt);
        yieldRespose.setCommandYield(
                sumPlateWt.divide(yieldRespose.getInfurSlabWt(), PmPlateConstants.YIELD_SCALE, RoundingMode.HALF_UP));
    }

    /**
     * 非指定计算
     *
     * @param pmProContYield
     * @param yieldRespose
     * @param effectWt 轧制有效重量（克）
     * @param waimAndTaimAndDensity 
     * @param needSlabLen 必要板坯长
     * @param infurSlabWtGram 进加热炉重量（克）初始值可能为空
     * @param needSlabWtGram 必要板坯重量（克）
     * 
     * @return newInfurSlabWtGram 进加热炉重量（克） 
     * 
     */
    public BigDecimal noFixSlabLenCalc(PmProContYieldRequestDTO pmProContYield, PmProContYieldResposeDTO yieldRespose,PmProContProdTtr axisPmProContProdTtr ,BigDecimal effectWt,
            BigDecimal waimAndTaimAndDensity , BigDecimal needSlabLen ,BigDecimal infurSlabWtGram , BigDecimal needSlabWtGram) {
        BigDecimal newInfurSlabWtGram = infurSlabWtGram;
        QdToPoOrderVO1 qdToPoOrderVO1 = pmQdToPoOrderRepository.selectByOrderNo(axisPmProContProdTtr.getOrderNo() , pmProContYield.getTenantId());
        // 查询质量接口，根据产品品种获取是否能展宽，如果不是
        if(qdToPoOrderVO1 != null && StringUtils.isNotBlank(qdToPoOrderVO1.getAxialRollMark()) && BaseConstants.Flag.NO.toString().equals(qdToPoOrderVO1.getAxialRollMark())) {
            // 当组合大板宽度/必要板坯长度*1.02 <1.6,且 组合大板宽度 > 结晶器宽度，则重新计算轧制目标宽度waim
            BigDecimal raito = yieldRespose.getCombinePlateWidth().divide(needSlabLen , PmPlateConstants.TEM_SCALE , RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(1.02f));
            if(raito.compareTo(BigDecimal.valueOf(1.6f)) < 0 && yieldRespose.getCombinePlateWidth().compareTo(pmProContYield.getPmContFilterRequest().getMoldWidth()) > 0) {
                // 重算目标宽
                BigDecimal waim = yieldRespose.getWaim().subtract(axisPmProContProdTtr.getWidthAddv() == null ? BigDecimal.ZERO : axisPmProContProdTtr.getWidthAddv());
                PmAxisBrdTrimBt pmAxisBrdTrimBt = pmAxisBrdTrimBtRepository.selectPmAxisBrdTrimBt(pmProContYield.getTenantId(), pmProContYield.getPmContFilterRequest().getMscLineNo()
                        , axisPmProContProdTtr.getTrimCode());
                waim = waim.add(pmAxisBrdTrimBt.getWidthAddv());
                yieldRespose.setWaim(waim);
                // 重算基准收得率
                calcNormYield(axisPmProContProdTtr.getOrderThick(), yieldRespose);
                needSlabWtGram = effectWt.divide(yieldRespose.getNormYield(), PmPlateConstants.TEM_SCALE,
                        RoundingMode.HALF_UP);
                needSlabLen = needSlabWtGram.multiply(BigDecimal.TEN).divide(waimAndTaimAndDensity,
                        PmPlateConstants.LEN_SCALE, RoundingMode.HALF_UP);
            }
        }
     
        BigDecimal saleWtRate = BigDecimal.ONE;
        if (yieldRespose.getSlabAddv() != null) {
            saleWtRate = saleWtRate.add(yieldRespose.getSlabAddv());
        }
        // 必要板坯重量 = 必要板坯重量 * （1 + 板坯加料），
        // 必要板坯长度 = 必要板坯长度 * （1 + 板坯加料），
        needSlabWtGram = needSlabWtGram.multiply(saleWtRate);
        needSlabLen = needSlabLen.multiply(saleWtRate);
        
        yieldRespose.setNeedSlabLen(needSlabLen);
        yieldRespose.setNeedSlabWt(needSlabWtGram.divide(PmPlateConstants.GRAM_TO_TON , PmPlateConstants.WT_SCALE , RoundingMode.HALF_UP));
        
        // 中间坯
        BigDecimal lenWidthDiff = pmMidSlabAddvBtRepository.queryLenWidthDiff(yieldRespose.getTaim(), axisPmProContProdTtr.getOrderNo()
                , pmProContYield.getPmContFilterRequest().getMscLineNo(), pmProContYield.getPmContFilterRequest().getStNo()
                , pmProContYield.getPmContFilterRequest().getSgSign(), pmProContYield.getPmContFilterRequest().getMoldThick(), pmProContYield.getTenantId());
        if(lenWidthDiff != null) {
            // 中间坯长度 = 板坯厚度 * 板坯宽度 * 必要板坯长度 / (中间坯厚度目标 * 轧制目标宽度waim)
            BigDecimal midSLabThkAim = qdToPoOrderMapper.queryMidSLabThkAim(axisPmProContProdTtr.getOrderNo(), pmProContYield.getPmContFilterRequest().getMscLineNo(), pmProContYield.getTenantId());
            if(midSLabThkAim != null) {
                BigDecimal midLen = pmProContYield.getPmContFilterRequest().getMoldThick().multiply(pmProContYield.getPmContFilterRequest().getMoldWidth()).multiply(needSlabLen);
                midLen = midLen.divide(midSLabThkAim.multiply(yieldRespose.getWaim()) , PmPlateConstants.LEN_SCALE , RoundingMode.HALF_UP);
                if(midLen.subtract(yieldRespose.getWaim()).abs().compareTo(lenWidthDiff) < 0) {
                    // 若 |中间坯长度-轧制目标宽度| < len_width_diff，则要将中间坯长度补长，使中间坯长度 = 轧制目标宽度 + len_width_diff
                    midLen = yieldRespose.getWaim().add(lenWidthDiff);
                }
                // 更新必要板坯长度 = 中间坯厚度目标 * 轧制目标宽度 * 中间坯长度 / （板坯厚度 * 板坯宽度）
                needSlabLen = midSLabThkAim.multiply(yieldRespose.getWaim()).multiply(midLen)
                        .divide(pmProContYield.getPmContFilterRequest().getMoldThick().multiply(pmProContYield.getPmContFilterRequest().getMoldWidth()) , PmPlateConstants.LEN_SCALE , RoundingMode.HALF_UP);
            }
        }
        
        needSlabWtGram = needSlabLen.multiply(waimAndTaimAndDensity).divide(BigDecimal.TEN ,PmPlateConstants.TEM_SCALE , RoundingMode.HALF_UP);
        
        yieldRespose.setNeedSlabLen(needSlabLen);
        yieldRespose.setNeedSlabWt(needSlabWtGram.divide(PmPlateConstants.GRAM_TO_TON , PmPlateConstants.WT_SCALE , RoundingMode.HALF_UP));
        
        // 单位克
        newInfurSlabWtGram = needSlabWtGram ;
        yieldRespose.setInfurSlabWt(yieldRespose.getNeedSlabWt());
        // 进加热炉板坯长度 = 进加热炉板坯重量 ÷（板坯厚度×板坯宽度×密度）
        yieldRespose.setInfurSlabLen(needSlabLen);
        // 清理前板坯重量 ＝ 进加热炉板坯重量
        yieldRespose.setPreCleanSlabWt(yieldRespose.getInfurSlabWt());
        // 清理前板坯长度 ＝ 清理前板坯重量 ÷（板坯厚度×板坯宽度×密度）
        yieldRespose.setPreCleanSlabLen(needSlabLen);
//        yieldRespose.setInfurSlabWt(newInfurSlabWtGram.divide(PmPlateConstants.GRAM_TO_TON, PmPlateConstants.WT_SCALE,
//                RoundingMode.HALF_UP));
//        // 进加热炉板坯长度 = 进加热炉板坯重量 ÷（板坯厚度×板坯宽度×密度）
//        yieldRespose.setInfurSlabLen(newInfurSlabWtGram.multiply(PmPlateConstants.CM_TO_MM)
//                .divide(waimAndTaimAndDensity, PmPlateConstants.LEN_SCALE, RoundingMode.HALF_UP));
//        // 清理前板坯重量 ＝ 进加热炉板坯重量
//        yieldRespose.setPreCleanSlabWt(yieldRespose.getInfurSlabWt());
//        // 清理前板坯长度 ＝ 清理前板坯重量 ÷（板坯厚度×板坯宽度×密度）
//        yieldRespose.setPreCleanSlabLen(newInfurSlabWtGram.multiply(PmPlateConstants.CM_TO_MM)
//                .divide(waimAndTaimAndDensity, PmPlateConstants.LEN_SCALE, RoundingMode.HALF_UP));
        return newInfurSlabWtGram;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void executePd(PmProContYieldRequestDTO pmProContYield,
            Map<String, PmProContFilterResposeDTO> filterOrderMap, PmCompositeSlabCalcDTO pmCompositeSlabCalc,
            PmProContYieldResposeDTO pmProContYieldRespose) {

        PmContMscTech axisPmContMscTech = new PmContMscTech();
        axisPmContMscTech.setTenantId(pmProContYield.getTenantId());
        axisPmContMscTech.setMscLineNo(pmProContYield.getPmContFilterRequest().getMscLineNo());
        axisPmContMscTech.setOrderNo(pmProContYield.getAxisOrderNo());
        axisPmContMscTech = this.pmContMscTechRepository.selectOne(axisPmContMscTech);

        PmProContProdTtr axisPmProContProdTtr = new PmProContProdTtr();
        axisPmProContProdTtr.setTenantId(pmProContYield.getTenantId());
        axisPmProContProdTtr.setOrderNo(pmProContYield.getAxisOrderNo());
        axisPmProContProdTtr = this.pmProContProdTtrRepository.selectOne(axisPmProContProdTtr);

        T t = generateResult(pmProContYield, filterOrderMap, pmCompositeSlabCalc, pmProContYieldRespose,
                axisPmContMscTech, axisPmProContProdTtr);

        // 粗切位置信息插入大板粗切位置表PM_PD_PLATE_CUT
        List<PmPdPlateCut> pmPdPlateCuts = null;
        if (needPlateCut()) {
            pmPdPlateCuts = buildPmPdPlateCuts(pmCompositeSlabCalc, pmProContYield);
        }

        List<PmPdBlockPos> pmPdBlockPoss = new ArrayList<>(pmProContYield.getCompositeBoard().getBlkCount());
        List<PmPdContSum> pmPdContSums = new ArrayList<>(pmProContYield.getCompositeBoard().getOrderCount());
        List<PmSmallPltPos> pmSmallPltPoss = new ArrayList<>(pmProContYield.getCompositeBoard().getSpCount());

        buildOtherInfo(pmProContYield, pmPdBlockPoss, pmPdContSums, pmSmallPltPoss, axisPmProContProdTtr,
                filterOrderMap);
        // 生成粗切位置，组合子板，合同，小板位置表数据
        if (needPlateCut() && pmPdPlateCuts != null && !pmPdPlateCuts.isEmpty()) {
            this.pmPdPlateCutRepository.batchInsertSelective(pmPdPlateCuts);
        }

        this.pmPdBlockPosRepository.batchInsertSelective(pmPdBlockPoss);
        this.pmPdContSumRepository.batchInsertSelective(pmPdContSums);
        this.pmSmallPltPosRepository.batchInsertSelective(pmSmallPltPoss);
        // 生产命令表数据
        generateCmd(t, pmPdContSums, pmSmallPltPoss, pmProContYield.getPmContFilterRequest().getMscLineNo());
        try {
            // 抛组板履历事件
            pdEventRec(t, pmPdContSums, filterOrderMap, pmProContYield);
            // 抛合同跟踪事件
            ctEventRec(t, pmSmallPltPoss, filterOrderMap, pmProContYield,pmCompositeSlabCalc, pmProContYieldRespose);
            // 抛轧钢
            callMatStatusModify(t, pmProContYield.getTenantId());
            if(pmProContYield.isSendCtEvent()) {
                eventClient.sendEvent(pmProContYield.getTenantId());
            }
        } finally {
            if(pmProContYield.isSendCtEvent()) {
                eventClient.remove();
            }
        }
    }

    public void callMatStatusModify(T t, Long tenantId) {
    }

    /**
     * 合同跟踪事件
     * 
     * @param t
     * @param pmSmallPltPoss
     * @param filterOrderMap
     * @param pmProContYield
     * @param pmProContYieldRespose 
     * @param pmCompositeSlabCalc 
     */
    public void ctEventRec(T t, List<PmSmallPltPos> pmSmallPltPoss,
            Map<String, PmProContFilterResposeDTO> filterOrderMap, PmProContYieldRequestDTO pmProContYield, PmCompositeSlabCalcDTO pmCompositeSlabCalc, PmProContYieldResposeDTO pmProContYieldRespose) {
        List<ProContBoardEndDTO> boardEndEventList = new ArrayList<>();
        
        PmProContFilterResposeDTO axisPmProCont = filterOrderMap.get(pmProContYield.getAxisOrderNo());
        // 虚拟板坯维度
        boardEndEventList.add(new ProContBoardEndDTO(axisPmProCont.getOrderNo()).setMatNo(pmProContYield.getVirtualSlabNo())
                            .setLen(pmProContYieldRespose.getPreCleanSlabLen()).setMoMatNo(getSlabMoMatNo(pmProContYield))
                            .setMscLineNo(pmProContYield.getPmContFilterRequest().getMscLineNo())
                            .setPdMatStatus(PdStatusHp.PD_STATUS_HP_12).setPpFlag(PdProdFlag.VIRTUAL_SLAB.getCode())
                            .setThk(pmProContYield.getPmContFilterRequest().getMoldThick())
                            .setWid(pmProContYield.getPmContFilterRequest().getMoldWidth())
                            .setWgt(pmProContYieldRespose.getPreCleanSlabWt())
                            .setWholeBacklogCode(getWholeBacklogCode(axisPmProCont, pmProContYield))
                            .setStNo(axisPmProCont.getStNo())
                            .setSgSign(axisPmProCont.getSgSign())
                            .setProcCd(getProcCd()));
        // 大板维度
        boardEndEventList.add(new ProContBoardEndDTO(axisPmProCont.getOrderNo()).setMatNo(pmProContYield.getVirtualSlabNo())
                .setLen(pmProContYieldRespose.getLaim()).setMoMatNo(getSlabMoMatNo(pmProContYield))
                .setMscLineNo(pmProContYield.getPmContFilterRequest().getMscLineNo())
                .setPdMatStatus(PdStatusHp.PD_STATUS_HP_12).setPpFlag(PdProdFlag.VIRTUAL_COMPOSITE_SLAB.getCode())
                .setThk(pmProContYieldRespose.getTaim())
                .setWid(pmProContYieldRespose.getWaim())
                .setWgt(pmProContYieldRespose.getRollWtHp())
                .setWholeBacklogCode(getWholeBacklogCode(axisPmProCont, pmProContYield))
                .setStNo(axisPmProCont.getStNo())
                .setSgSign(axisPmProCont.getSgSign())
                .setProcCd(getProcCd()));
        
        for (PmSmallPltPos pmSmallPltPos : pmSmallPltPoss) {
            PmProContFilterResposeDTO pmProContFilterResposeDTO = filterOrderMap.get(pmSmallPltPos.getOrderNo());
            // 小板维度
            boardEndEventList
                    .add(new ProContBoardEndDTO(pmSmallPltPos.getOrderNo()).setMatNo(pmProContYield.getVirtualSlabNo()+pmSmallPltPos.getSmallPlateDivNo())
                            .setLen(pmSmallPltPos.getOrderLen()).setMoMatNo(getMoMatNo(pmSmallPltPos.getPdMatNo(), pmProContYield))
                            .setMscLineNo(pmProContYield.getPmContFilterRequest().getMscLineNo())
                            .setPdMatStatus(PdStatusHp.PD_STATUS_HP_12).setPpFlag(getSmallPosPpFlag())
                            .setThk(pmSmallPltPos.getOrderThick()).setWid(pmSmallPltPos.getOrderWidth())
                            .setWgt(pmProContFilterResposeDTO.getOrderUnitAimWt())
                            .setWholeBacklogCode(getWholeBacklogCode(axisPmProCont, pmProContYield))
                            .setStNo(pmProContFilterResposeDTO.getStNo())
                            .setSgSign(pmProContFilterResposeDTO.getSgSign())
                            .setProcCd(getProcCd()));
        }
        eventClient.addEvent(getCtEventCode(), boardEndEventList);
    }

    protected String getProcCd() {
        return Constants.ProdcCd.CD_14A;
    }

    /**
     * 小板维度母材料号
     * @param pdMatNo
     * @param pmProContYield
     * @return
     */
    protected String getMoMatNo(String pdMatNo, PmProContYieldRequestDTO pmProContYield) {
        return pdMatNo;
    }
    
    /**
     * 大板维度母材料号
     * @param pmProContYield
     * @return
     */
    protected String getSlabMoMatNo(PmProContYieldRequestDTO pmProContYield) {
        return getSlabNo(pmProContYield);
    }

    protected String getCtEventCode() {
        return CtEventConstants.EVENT_PM_BOARD_END;
    }

    /**
     * 合同跟踪组板参数，产品标记
     * 
     * @param t
     * @param pmProContYield
     * @return
     */
    protected abstract Long ctEventBoardPpFlag(T t, PmProContYieldRequestDTO pmProContYield);

    @Transactional(rollbackFor = Exception.class)
    public void pdEventRec(T t, List<PmPdContSum> pmPdContSums, Map<String, PmProContFilterResposeDTO> filterOrderMap,
            PmProContYieldRequestDTO pmProContYield) {
        List<PmPdRecDTO> pmPdRecs = new ArrayList<>();
        List<PmPdRecDTO> pmPdRecsOver = new ArrayList<>();

        List<PmProContYieldDTO> pmProContYieldDTOs = pmProContYield.getPmProContYieldList();
        Map<String, PmProContYieldDTO> pmProContYieldDTOMap = Maps.uniqueIndex(pmProContYieldDTOs,
                PmProContYieldDTO::getOrderNo);

        List<ProContOverDTO> overEventList = new ArrayList<>();
        CustomUserDetails userDetails = DetailsHelper.getUserDetails();
        for (PmPdContSum contSum : pmPdContSums) {
            PmPdRecDTO pmPdRecDTO = new PmPdRecDTO();
            pmPdRecDTO.setOrderNo(contSum.getOrderNo());
            pmPdRecDTO.setEventMaker(userDetails == null ? "" : userDetails.getUserId().toString());
            pmPdRecDTO.setMscLineNo(pmProContYield.getPmContFilterRequest().getMscLineNo());
            pmPdRecDTO.setKeyvalue1(pmProContYield.getVirtualSlabNo());
            pmPdRecDTO.setKeyvalue1Desc(PmPdRecDTO.DESC_VIRTUAL_SLAB_NO);
            pmPdRecDTO.setMatNo(pmProContYield.getVirtualSlabNo());
            PmProContYieldDTO contYieldDTO = pmProContYieldDTOMap.get(contSum.getOrderNo());

            if (contYieldDTO.getPdOverDevoQty() != null
                    && contYieldDTO.getPdOverDevoQty().compareTo(BigDecimal.ZERO) > 0) {
                PmPdRecDTO pmPdRecOverDTO = BeanUtils.copy(pmPdRecDTO, PmPdRecDTO.class);
                pmPdRecOverDTO.setKeyvalue2(contYieldDTO.getPdOverDevoQty().toPlainString());
                pmPdRecOverDTO.setKeyvalue2Desc(PmPdRecDTO.DESC_OVER_QTY);
                pmPdRecOverDTO.setKeyvalue3(contYieldDTO.getPdOverDevoCause());
                pmPdRecOverDTO.setKeyvalue3Desc(PmPdRecDTO.DESC_OVER_REASON);
                pmPdRecOverDTO.setQty(contYieldDTO.getPdOverDevoQty());
                Map<String, Object> params = new HashMap<String, Object>();
                params.put(PmPlateConstants.EventParam.PD_OVER_DEVO_CAUSE, contYieldDTO.getPdOverDevoCause());
                pmPdRecOverDTO.setParams(params);
                pmPdRecsOver.add(pmPdRecOverDTO);
                PmProContFilterResposeDTO pmProContFilterResposeDTO = filterOrderMap.get(contSum.getOrderNo());
                BigDecimal wgt = contYieldDTO.getPdOverDevoQty()
                        .multiply(pmProContFilterResposeDTO.getOrderUnitAimWt());
                overEventList.add(new ProContOverDTO(contSum.getOrderNo(),
                        contYieldDTO.getPdOverDevoQty(), wgt, "SC")); // TODO 超投原因
            } else {

            }
            pmPdRecs.add(pmPdRecDTO);

        }
        if (!pmPdRecsOver.isEmpty()) {
            pmPdRecService.createEvent(pmProContYield.getTenantId(), pmPdRecsOver, PdEventType.HP03,
                    Constants.SYSTEM_PD_CODE, Constants.PdOver.CC_SLAB_FINSHED_PD_OVER);
            eventClient.addEvent(CtEventConstants.EVENT_PM_CONTRACT_BOARD_OVER, overEventList);
        }
        PdEventType finshedPdEventType = pdFinshedEventType();
        if (finshedPdEventType != null) {
            pmPdRecService.createEvent(pmProContYield.getTenantId(), pmPdRecs, pdFinshedEventType(),
                    Constants.SYSTEM_PD_CODE, pdProgram());
        }

    }

    /**
     * 生产命令表数据
     * 
     * @param matParam       组板信息
     * @param pmPdContSums   合同信息
     * @param pmSmallPltPoss 小板位置信息
     * @param mscLineNo      设计产线号
     */
    public abstract void generateCmd(T matParam, List<PmPdContSum> pmPdContSums, List<PmSmallPltPos> pmSmallPltPoss,
            String mscLineNo);

    /**
     * 生产组板计算结果
     * 
     * @param pmProContYield        请求参数
     * @param filterOrderMap        合同列表
     * @param pmCompositeSlabCalc   组板计算结果
     * @param pmProContYieldRespose 收得率计算结构
     * @param axisPmContMscTech     轴合同生产工艺参数
     * @param axisPmProContProdTtr  轴合同生产属性参数
     * @return
     */
    public abstract T generateResult(PmProContYieldRequestDTO pmProContYield,
            Map<String, PmProContFilterResposeDTO> filterOrderMap, PmCompositeSlabCalcDTO pmCompositeSlabCalc,
            PmProContYieldResposeDTO pmProContYieldRespose, PmContMscTech axisPmContMscTech,
            PmProContProdTtr axisPmProContProdTtr);

    /**
     * 获取大板粗切位置
     * 
     * @param pmCompositeSlabCalc
     * @param pmProContYield
     * @return
     */
    private List<PmPdPlateCut> buildPmPdPlateCuts(PmCompositeSlabCalcDTO pmCompositeSlabCalc,
            PmProContYieldRequestDTO pmProContYield) {
        List<PmPdPlateCut> pmPdPlateCuts = new ArrayList<>(pmCompositeSlabCalc.getMLen().size());

        if (pmCompositeSlabCalc.getDiffCut() != null && !pmCompositeSlabCalc.getDiffCut().isEmpty()) {
            for (Integer diffCut : pmCompositeSlabCalc.getDiffCut()) {
                pmPdPlateCuts.add(buildPmPdPlateCut(CutType.D, CutPositionType.F, pmProContYield.getVirtualSlabNo(),
                        pmProContYield.getTenantId(), diffCut.longValue()));
                pmPdPlateCuts.add(buildPmPdPlateCut(CutType.D, CutPositionType.M, pmProContYield.getVirtualSlabNo(),
                        pmProContYield.getTenantId(), diffCut.longValue()));

            }
        }
        if (pmCompositeSlabCalc.getCommandCut() != null && !pmCompositeSlabCalc.getCommandCut().isEmpty()) {
            for (Integer commandCut : pmCompositeSlabCalc.getCommandCut()) {
                pmPdPlateCuts.add(buildPmPdPlateCut(CutType.P, CutPositionType.F, pmProContYield.getVirtualSlabNo(),
                        pmProContYield.getTenantId(), commandCut.longValue()));
                pmPdPlateCuts.add(buildPmPdPlateCut(CutType.P, CutPositionType.M, pmProContYield.getVirtualSlabNo(),
                        pmProContYield.getTenantId(), commandCut.longValue()));
            }
        }
        return pmPdPlateCuts;
    }

    /**
     * 组装粗切位置
     * 
     * @param cutType
     * @param cutPositionType
     * @param virtualSlabNo
     * @param tenantId
     * @param positionSeq
     * @return
     */
    private PmPdPlateCut buildPmPdPlateCut(CutType cutType, CutPositionType cutPositionType, String virtualSlabNo,
            Long tenantId, Long positionSeq) {
        PmPdPlateCut pdPlateCut = new PmPdPlateCut();
        pdPlateCut.setCutType(cutType.getCode());
        pdPlateCut.setPdMatNo(virtualSlabNo);
        pdPlateCut.setPositionSeq(positionSeq);
        pdPlateCut.setPositionType(cutPositionType.getCode());
        pdPlateCut.setTenantId(tenantId);
        return pdPlateCut;
    }

    /**
     * 组装其他信息
     * 
     * @param pmProContYield
     * @param pmPdBlockPoss  组合子板位置表
     * @param pmPdContSums   组板合同总量表
     * @param filterOrderMap

     * 
     */
    public void buildOtherInfo(PmProContYieldRequestDTO pmProContYield, List<PmPdBlockPos> pmPdBlockPoss,
            List<PmPdContSum> pmPdContSums, List<PmSmallPltPos> pmSmallPltPoss, PmProContProdTtr axisPmProContProdTtr,
            Map<String, PmProContFilterResposeDTO> filterOrderMap) {
        String pdMatNo = pmProContYield.getVirtualSlabNo();
        Long tenantId = pmProContYield.getTenantId();
        Map<String , PmPdContSum> contMap = new HashMap<>();
        int i = 1;
        if (!"0".equals(axisPmProContProdTtr.getTrimCode())
                && pmProContYield.getCompositeBoard().getSpCount() >= PmPlateConstants.SMALL_PLATE_SPLIT_NO.length) {
            throw new CommonException("pm.pm_pro_cont_044");
        }
        String slabNo = getSlabNo(pmProContYield);
        String plateNo = getPlateNo(pmProContYield);
        for (PmCompositeBlockDTO block : pmProContYield.getCompositeBoard().getPmCompositeBlockDTOs()) {
            Set<String> contAddFlag = new HashSet<>(pmProContYield.getCompositeBoard().getOrderCount());
            for (PmSmallPlateDTO smallPlate : block.getSmallPlateDTOList()) {
                Long proContId = filterOrderMap.get(smallPlate.getOrderNo()).getProContId();
                if(!contMap.containsKey(smallPlate.getOrderNo())) {
                    PmPdContSum pmPdContSum = BeanUtils.copy(smallPlate, PmPdContSum.class);
                    pmPdContSum.setTenantId(tenantId);
                    pmPdContSum.setOrderNo(smallPlate.getOrderNo());
                    pmPdContSum.setPdMatNo(pdMatNo);
                    PdMatType pdMatType = PdMatType.getByCode(pmProContYield.getPmContFilterRequest().getMatDesignKind());
                    pmPdContSum.setPdMatType(pdMatType.getActualPdMatType());
                    pmPdContSum.setProContId(proContId);
                    pmPdContSum.setSlabNo(slabNo);
                    pmPdContSum.setPlateNo(plateNo);
                    if (axisPmProContProdTtr.getOrderNo().equals(smallPlate.getOrderNo())) {
                        pmPdContSum.setAxisFlag(BaseConstants.Flag.YES.longValue());
                    } else {
                        pmPdContSum.setAxisFlag(BaseConstants.Flag.NO.longValue());
                    }

                    pmPdContSum.setPdStatus(getOrderSumStatus());
                    pmPdContSums.add(pmPdContSum);
                    contMap.put(smallPlate.getOrderNo(), pmPdContSum);
                } else {
                    if(!contAddFlag.contains(smallPlate.getOrderNo())) {
                        // 同一个板块类一个小板合同只计算一次
                        PmPdContSum pmPdContSum = contMap.get(smallPlate.getOrderNo());
                    }
                }
                if (!contAddFlag.contains(smallPlate.getOrderNo())) {
                    // 如果不在标记中，则需要添加到组板子板位置表 和合同总量表中
                    contAddFlag.add(smallPlate.getOrderNo());
                    PmPdBlockPos pmPdBlockPos = new PmPdBlockPos();
                    pmPdBlockPos.setPdMatNo(pdMatNo);
                    pmPdBlockPos.setOrderBlockNo(block.getBlockNo().longValue());
                    pmPdBlockPos.setOrderNo(smallPlate.getOrderNo());
                    pmPdBlockPos.setOrderSeqNo(smallPlate.getOrderSeqNo());
                    pmPdBlockPos.setBlockPlateNum(BigDecimal.valueOf(smallPlate.getOrderBlockNum()));
                    pmPdBlockPos.setSlabNo(slabNo);
                    pmPdBlockPos.setPlateNo(plateNo);
                    pmPdBlockPos.setProContId(proContId);
                    pmPdBlockPos.setTenantId(tenantId);
                    pmPdBlockPoss.add(pmPdBlockPos);
                }

                PmSmallPltPos pmSmallPltPos = BeanUtils.copy(smallPlate, PmSmallPltPos.class);
                pmSmallPltPos.setTenantId(tenantId);
                pmSmallPltPos.setPdMatNo(pdMatNo);
                pmSmallPltPos.setSlabNo(slabNo);
                pmSmallPltPos.setPlateNo(plateNo);
                pmSmallPltPos.setProContId(proContId);
                pmSmallPltPos.setPickModeLenDir(smallPlate.getPickModeLenDirPos().longValue());
                pmSmallPltPos.setPickModeWidDir(smallPlate.getPickModeWidDirPos().longValue());
                pmSmallPltPos.setSmallPlateDivNo(getSmallPlateDivNo(plateNo, axisPmProContProdTtr.getTrimCode() , i++));
                pmSmallPltPoss.add(pmSmallPltPos);
            }
        }
    }
    
    /**
     * 获取小板分割号
     * @param trimCode
     * @param index
     * @return
     */
    public String getSmallPlateDivNo(String plateNo, String trimCode , int index) {
        if ("0".equals(trimCode)) {
            return PmPlateConstants.SMALL_PLATE_SPLIT_NO[0];
        } else {
            return PmPlateConstants.SMALL_PLATE_SPLIT_NO[index];
        }
    }
    
    /**
     * // TODO 规则库 7，10，12的参数 初始化规则集校验（合同抽取用）C01 S01
     * 
     * @param pdRuleSetType
     * @param pmProContFilterResposeDTO
     * @param pmProContFilterRequest
     * @param pmSlabSpecBts
     * @return
     */
    protected RuleParamDTO initContFilterRuleParam(PdRuleSetType pdRuleSetType,
            PmProContFilterResposeDTO pmProContFilterResposeDTO, PmProContFilterRequestDTO pmProContFilterRequest,
            List<PmSlabSpecBt> pmSlabSpecBts) {
        RuleParamDTO ruleParamDTO = new RuleParamDTO(pdRuleSetType);
        Map<String, Object> data = new HashMap<>();

        data.put(RuleSetConstants.ORDER_NO, pmProContFilterResposeDTO.getOrderNo());
        data.put(RuleSetConstants.ORDER_NO_SET, pmProContFilterRequest.getOrderNoList());
        data.put(RuleSetConstants.MSC_LINE_NO, pmProContFilterResposeDTO.getMscLineNo());
        data.put(RuleSetConstants.ROLL_DIRECT_CODE, pmProContFilterRequest.getRollDirectCode());
        data.put(RuleSetConstants.FUR_TYPE, pmProContFilterRequest.getFurType());
        data.put(RuleSetConstants.MOLD_THICK, pmProContFilterRequest.getMoldThick());
        data.put(RuleSetConstants.MOLD_WIDTH, pmProContFilterRequest.getMoldWidth());
        data.put(RuleSetConstants.MAT_DESIGN_KIND, pmProContFilterRequest.getMatDesignKind());
        data.put(RuleSetConstants.ST_NO, pmProContFilterRequest.getStNo());
        data.put(RuleSetConstants.CC_TYPE, pmProContFilterRequest.getCcType());
        data.put(RuleSetConstants.FORM_PLATE_MODE, pmProContFilterRequest.getFormPlateMode());
        data.put(RuleSetConstants.PONO, pmProContFilterRequest.getPono());
        data.put(RuleSetConstants.TENANT_ID, pmProContFilterRequest.getTenantId());
        // 获取工艺参数
        PmContMscTech pmContMscTech = new PmContMscTech();
        BeanCopier.create(PmProContFilterResposeDTO.class, PmContMscTech.class, false).copy(pmProContFilterResposeDTO,
                pmContMscTech, null);
        data.put(RuleSetConstants.PM_CONT_MSC_TECH, pmContMscTech);
        // 获取合同属性
        PmProContProdTtr pmProContProdTtr = new PmProContProdTtr();
        BeanCopier.create(PmProContFilterResposeDTO.class, PmProContProdTtr.class, false)
                .copy(pmProContFilterResposeDTO, pmProContProdTtr, null);
        data.put(RuleSetConstants.PM_PRO_CONT_PROD_ATTR, pmProContProdTtr);
        // 设置连铸板坯生产规格基准表数据
        if (pmSlabSpecBts != null && !pmSlabSpecBts.isEmpty()) {
            data.put(RuleSetConstants.PM_SLAB_SPEC_BT, pmSlabSpecBts.get(0));
        }

        ruleParamDTO.setBizData(data);
        ruleParamDTO.setTenantId(pmProContFilterRequest.getTenantId());
        return ruleParamDTO;
    }

    /**
     * 获取组板方式
     * 
     * @param pmProContYield
     * @return
     */
    protected FormPlateModeStrategy getFormPlateModeStrategy(PmProContYieldRequestDTO pmProContYield) {
        FormPlateMode mode = FormPlateMode.getByCode(pmProContYield.getPmContFilterRequest().getFormPlateMode());
        FormPlateModeStrategy formPlateModeStrategy = FormPlateModeStrategyFactory.matchStrategy(mode);
        if (formPlateModeStrategy == null) {
            throw new CommonException("pm.pm_pro_cont_038");
        }
        return formPlateModeStrategy;
    }

    /**
     * 获取取板方式枚举
     *
     * @param pmProContYield
     * @return
     */
    protected FormPlateMode getFormPlateMode(PmProContYieldRequestDTO pmProContYield) {
        return FormPlateMode.valueOf(pmProContYield.getPmContFilterRequest().getFormPlateMode());
    }

    /**
     * 获取轧制产线号
     *
     * @param pmContFilterRequest
     * @return
     */
    protected String getRollLineNo(PmProContFilterRequestDTO pmContFilterRequest) {
        String rollLineNo = pmContFilterRequest.getMscLineNo()
                .substring(pmContFilterRequest.getMscLineNo().length() - 2);
        return rollLineNo;
    }

    /**
     * 获取板坯号
     * 
     * @param pmProContYield
     * @return
     */
    protected String getSlabNo(PmProContYieldRequestDTO pmProContYield) {
        return null;
    }

    protected String getOrderSumStatus() {
        return PdStatusHp.PD_STATUS_HP_12;
    }

    /**
     * 获取钢板号
     * 
     * @param pmProContYield
     * @return
     */
    protected String getPlateNo(PmProContYieldRequestDTO pmProContYield) {
        return null;
    }

    @Override
    public void pdDeleteEvent(List<PmPdContSum> pmPdContSums, String virtualSlabNo, String mscLineNo, String apiCode,
            Long tenantId) {
        PdEventType pdEventType = pdDeleteEventType();
        if (pdEventType != null) {
            List<PmPdRecDTO> pmPdRecs = new ArrayList<>();
            for (PmPdContSum contSum : pmPdContSums) {
                PmPdRecDTO pmPdRecDTO = new PmPdRecDTO();
                pmPdRecDTO.setOrderNo(contSum.getOrderNo());
                pmPdRecDTO.setMscLineNo(mscLineNo);
                pmPdRecDTO.setMatNo(virtualSlabNo);
                pmPdRecDTO.setKeyvalue1(virtualSlabNo);
                pmPdRecDTO.setKeyvalue1Desc(PmPdRecDTO.DESC_VIRTUAL_SLAB_NO);
                pmPdRecs.add(pmPdRecDTO);
            }
            this.pmPdRecService.createEvent(tenantId, pmPdRecs, pdEventType, Constants.SYSTEM_CODE, apiCode);
        }
    }

    @Override
    public void pdDeleteEvent(List<PmPdContSum> pmPdContSums, String virtualSlabNo, String mscLineNo, String apiCode,
                              Long tenantId, String moduleCode, String eventMaker) {
        PdEventType pdEventType = pdDeleteEventType();
        if (pdEventType != null) {
            List<PmPdRecDTO> pmPdRecs = new ArrayList<>();
            for (PmPdContSum contSum : pmPdContSums) {
                PmPdRecDTO pmPdRecDTO = new PmPdRecDTO();
                pmPdRecDTO.setOrderNo(contSum.getOrderNo());
                pmPdRecDTO.setMscLineNo(mscLineNo);
                pmPdRecDTO.setMatNo(virtualSlabNo);
                pmPdRecDTO.setKeyvalue1(virtualSlabNo);
                pmPdRecDTO.setKeyvalue1Desc(PmPdRecDTO.DESC_VIRTUAL_SLAB_NO);
                pmPdRecDTO.setEventMaker(eventMaker);
                pmPdRecDTO.setModuleCode(moduleCode);
                pmPdRecs.add(pmPdRecDTO);
            }
            this.pmPdRecService.createEvent(tenantId, pmPdRecs, pdEventType, Constants.SYSTEM_CODE, apiCode);
        }
    }

    /**
     * 组板释放事件
     * 
     * @return
     */
    protected abstract PdEventType pdDeleteEventType();

    /**
     * 组板完成事件
     * 
     * @return
     */
    protected abstract PdEventType pdFinshedEventType();

    @Override
    public List<PmBoardMatInfoDTO> selectBoardMatByOrderNo(String orderNo, Long organizationId) {
        return new ArrayList<>();
    }

    /**
     * 获取全程工序代码
     *
     * @param axisPmProCont
     * @return
     */
    protected String getWholeBacklogCode(PmProContFilterResposeDTO axisPmProCont, PmProContYieldRequestDTO pmProContYield) {
        return axisPmProCont.getWholeBacklogCode();
    }

    /**
     * 获取产品标记
     *
     * @return
     */
    protected Long getSmallPosPpFlag() {
        return PdProdFlag.VIRTUAL_SUB_SLAB.getCode();
    }


}
