package cn.estsh.i3plus.ext.mes.pcn.apiservice.serviceimpl.busi;

import cn.estsh.i3plus.ext.mes.pcn.api.base.*;
import cn.estsh.i3plus.ext.mes.pcn.api.busi.*;
import cn.estsh.i3plus.ext.mes.pcn.pojo.bean.MesOutPutStatistics;
import cn.estsh.i3plus.ext.mes.pcn.pojo.bean.MesSubassemblyRemadeRecord;
import cn.estsh.i3plus.ext.mes.pcn.pojo.bean.MesWorkCenterExt;
import cn.estsh.i3plus.ext.mes.pcn.pojo.bean.MesWorkOrderExt;
import cn.estsh.i3plus.ext.mes.pcn.pojo.model.SxSubassemblyRemadeDetailModel;
import cn.estsh.i3plus.ext.mes.pcn.pojo.model.SxSubassemblyRemadeModel;
import cn.estsh.i3plus.ext.mes.pcn.pojo.repository.MesOutPutStatisticsRepository;
import cn.estsh.i3plus.ext.mes.pcn.pojo.repository.MesSubassemblyRemadeRecordRepository;
import cn.estsh.i3plus.ext.mes.pcn.pojo.util.MesPcnExtConstWords;
import cn.estsh.i3plus.ext.mes.pcn.pojo.util.MesPcnExtEnumUtil;
import cn.estsh.i3plus.mes.pcn.api.iservice.base.IConfigService;
import cn.estsh.i3plus.platform.common.convert.ConvertBean;
import cn.estsh.i3plus.platform.common.exception.ImppExceptionEnum;
import cn.estsh.i3plus.platform.common.tool.MathOperation;
import cn.estsh.i3plus.pojo.base.enumutil.CommonEnumUtil;
import cn.estsh.i3plus.pojo.base.enumutil.MesPcnEnumUtil;
import cn.estsh.i3plus.pojo.mes.bean.MesConfig;
import cn.estsh.i3plus.pojo.mes.bean.MesPart;
import cn.estsh.i3plus.pojo.mes.bean.MesShift;
import cn.estsh.i3plus.pojo.mes.bean.MesWorkOrder;
import cn.estsh.i3plus.pojo.mes.repository.MesWorkOrderRepository;
import cn.estsh.impp.framework.boot.exception.ImppExceptionBuilder;
import cn.estsh.impp.framework.boot.util.ImppRedis;
import cn.estsh.impp.framework.boot.util.SpringContextsUtil;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author wangjie
 * @version 1.0
 * @date 2021/1/13 9:28
 **/
@Slf4j
@Service
public class SxSubassemblyRemadeService implements ISxSubassemblyRemadeService {

    @Autowired
    private IWorkCenterExtService workCenterExtService;

    @Autowired
    private ISxWorkOrderExtService workOrderExtService;

    @Autowired
    private IPartExtService partExtService;

    @Autowired
    private ISubassemblyRemadeRecordService subassemblyRemadeRecordService;

    @Autowired
    private IConfigService configService;

    @Autowired
    private IShiftExtService shiftExtService;

    @Autowired
    private IOutPutStatisticsService outPutStatisticsService;

    @Autowired
    private MesOutPutStatisticsRepository outPutStatisticsRepository;

    @Autowired
    private IShiftProdCenterRecordService shiftProdCenterRecordService;

    @Autowired
    private MesSubassemblyRemadeRecordRepository subassemblyRemadeRecordRepository;

    @Autowired
    private MesWorkOrderRepository workOrderRepository;

    @Autowired
    private ISxOutPutStatisticsTravelService outPutStatisticsTravelService;

    @Resource(name = MesPcnExtConstWords.REDIS_RES_PCN)
    private ImppRedis redisMesPcn;

    @Override
    public synchronized SxSubassemblyRemadeModel querySubassemblyRemadeData(SxSubassemblyRemadeModel model) {
        log.info("组件返工[查询刷新],生产线[{}]操作人[{}]线程[{}]", model.getWorkCenterCode(), model.getUserInfo(), Thread.currentThread().getName());
        return doCommonQuery(model);
    }

    @Override
    public synchronized SxSubassemblyRemadeModel resetSubassemblyRemadeData(SxSubassemblyRemadeModel model) {
        log.info("组件返工[重置],生产线[{}]操作人[{}]线程[{}]", model.getWorkCenterCode(), model.getUserInfo(), Thread.currentThread().getName());
        doHandleCachedData(model.getOrganizeCode(), model.getWorkCenterCode(), 3, null);
        return doCommonQuery(model);
    }

    private SxSubassemblyRemadeModel doCommonQuery(SxSubassemblyRemadeModel model) {

        //验证生产线是否属于组件线
        checkWorkCenterIsSubassembly(model.getOrganizeCode(), model.getWorkCenterCode());

        //获取缓存中的工单数据
        SxSubassemblyRemadeModel modelCached = doGetAndCheckCachedSubassemblyRemadeData(model);

        //缓存存在数据,条码根据扫描时间倒序后回显
        if (null != modelCached) return sortDetailList2SubassemblyRemadeModel(modelCached, model);

        //缓存中的工单数据不存在时获取当前生产线的首个启动返工工单
        MesWorkOrder workOrder = workOrderExtService.getFirstOpenedRemadeWorkOrderByWorkCenterCode(model.getOrganizeCode(), model.getWorkCenterCode());

        //检验工单的计划数量与完成数据是否一致, 物料是否属于组件
        checkWorkOrderQtyAndPart(workOrder, model.getWorkCenterCode());

        //初始化数据
        return initSubassemblyRemadeData(workOrder, model.getUserInfo());
    }

    private SxSubassemblyRemadeModel sortDetailList2SubassemblyRemadeModel(SxSubassemblyRemadeModel modelCached, SxSubassemblyRemadeModel model) {
        log.info("组件返工[子方法]加载缓存数据,生产线[{}]操作人[{}]线程[{}]", model.getWorkCenterCode(), model.getUserInfo(), Thread.currentThread().getName());
        
        if (!CollectionUtils.isEmpty(modelCached.getDetailList())) {
            List<SxSubassemblyRemadeDetailModel> detailList = modelCached.getDetailList().stream().filter(o -> null != o)
                    .sorted(Comparator.comparing(SxSubassemblyRemadeDetailModel::getScanDatetime).reversed()).collect(Collectors.toList());
            modelCached.setDetailList(detailList);
        }
        return modelCached;
    }

    private SxSubassemblyRemadeModel initSubassemblyRemadeData(MesWorkOrder workOrder, String userInfo) {
        log.info("组件返工[子方法]初始化数据,生产线[{}]操作人[{}]线程[{}]", workOrder.getWorkCenterCode(), userInfo, Thread.currentThread().getName());
        SxSubassemblyRemadeModel model = new SxSubassemblyRemadeModel();
        model.setOrganizeCode(workOrder.getOrganizeCode());
        model.setWorkOrderNo(workOrder.getOrderNo());
        model.setWorkCenterCode(workOrder.getWorkCenterCode());
        model.setPartNo(workOrder.getPartNo());
        model.setPartNameRdd(workOrder.getPartNameRdd());
        model.setQty(workOrder.getQty());
        model.setCompleteQty(workOrder.getCompleteQty());
        return model;
    }

    private void checkWorkOrderQtyAndPart(MesWorkOrder workOrder, String workCenterCode) {
        if (null == workOrder) {
            throw ImppExceptionBuilder.newInstance()
                    .setSystemID(CommonEnumUtil.SOFT_TYPE.MES_PCN.getCode())
                    .setErrorCode(ImppExceptionEnum.VARIFY_EXCEPTION_DATA_NOT_EXIT.getCode())
                    .setErrorDetail("生产线[%s]暂无启动状态的生产工单", workCenterCode)
                    .build();
        }
        if (MathOperation.compareTo(workOrder.getQty(), workOrder.getCompleteQty()) == 0) {
            throw ImppExceptionBuilder.newInstance()
                    .setSystemID(CommonEnumUtil.SOFT_TYPE.MES_PCN.getCode())
                    .setErrorCode(ImppExceptionEnum.VARIFY_EXCEPTION.getCode())
                    .setErrorDetail("生产线[%s]生产工单[%s]计划数量[%s]与完成数量[%s]已一致,且工单状态还是[启动]状态,请先变更此工单的状态",
                            workCenterCode, workOrder.getOrderNo(), workOrder.getQty(), workOrder.getCompleteQty())
                    .build();
        }

        getAndCheckPart(workOrder.getOrganizeCode(), workOrder.getWorkCenterCode(), workOrder.getOrderNo(), workOrder.getPartNo());
    }

    private MesPart getAndCheckPart(String organizeCode, String workCenterCode, String workOrderNo, String partNo) {
        MesPart partDb = partExtService.getPart(organizeCode, partNo);
        if (null == partDb) {
            throw ImppExceptionBuilder.newInstance()
                    .setSystemID(CommonEnumUtil.SOFT_TYPE.MES_PCN.getCode())
                    .setErrorCode(ImppExceptionEnum.VARIFY_EXCEPTION_DATA_NOT_EXIT.getCode())
                    .setErrorDetail("生产线[%s]生产工单[%s]关联的物料[%s]不存在", workCenterCode, workOrderNo, partNo)
                    .build();
        }
        if (StringUtils.isEmpty(partDb.getCategoryCode2())) {
            throw ImppExceptionBuilder.newInstance()
                    .setSystemID(CommonEnumUtil.SOFT_TYPE.MES_PCN.getCode())
                    .setErrorCode(ImppExceptionEnum.VARIFY_EXCEPTION.getCode())
                    .setErrorDetail("生产线[%s]生产工单[%s]关联的物料[%s]未配置[物料类型2],物料不属于组件", workCenterCode, workOrderNo, partNo)
                    .build();
        }
        return partDb;
    }

    private SxSubassemblyRemadeModel doGetAndCheckCachedSubassemblyRemadeData(SxSubassemblyRemadeModel model) {
        SxSubassemblyRemadeModel modelCached = (SxSubassemblyRemadeModel) doHandleCachedData(model.getOrganizeCode(), model.getWorkCenterCode(), 1, null);
        if (null != modelCached) {
            MesWorkOrder workOrder = workOrderExtService.getWorkOrderByOrderNo(model.getOrganizeCode(), modelCached.getWorkOrderNo());
            if (null == workOrder ||
                    (workOrder.getWorkOrderStatus() != MesPcnExtEnumUtil.WORK_ORDER_STATUS.OPEN.getValue() ||
                            workOrder.getWorkOrderType() != MesPcnExtEnumUtil.WORK_ORDER_TYPE.REWORK_ORDER.getValue() ||
                            MathOperation.compareTo(workOrder.getQty(), workOrder.getCompleteQty()) == 0)) {
                doHandleCachedData(model.getOrganizeCode(), model.getWorkCenterCode(), 3, null);
                modelCached = null;

                log.info("组件返工[子方法]获取缓存数据,验证缓存工单信息失败,已删除缓存,生产线[{}]操作人[{}]线程[{}]工单[{}]",
                        model.getWorkCenterCode(), model.getUserInfo(), Thread.currentThread().getName(), JSONObject.toJSONString(workOrder));
            } else {
                if (MathOperation.compareTo(workOrder.getQty(), modelCached.getQty()) != 0 ||
                        MathOperation.compareTo(workOrder.getCompleteQty(), modelCached.getCompleteQty()) != 0) {

                    log.info("组件返工[子方法]获取缓存数据,验证缓存工单信息失败,已更新缓存,生产线[{}]操作人[{}]线程[{}]工单[{}]缓存[{}]",
                            model.getWorkCenterCode(), model.getUserInfo(), Thread.currentThread().getName(), JSONObject.toJSONString(workOrder), JSONObject.toJSONString(modelCached));

                    modelCached.setQty(workOrder.getQty());
                    modelCached.setCompleteQty(workOrder.getCompleteQty());
                    doHandleCachedData(model.getOrganizeCode(), model.getWorkCenterCode(), 2, modelCached);
                }
            }
        }
        return modelCached;
    }

    private MesWorkCenterExt checkWorkCenterIsSubassembly(String organizeCode, String workCenterCode) {
        MesWorkCenterExt workCenterExt = workCenterExtService.getWorkCenterExtByWorkCenterCode(organizeCode, workCenterCode);
        if (null == workCenterExt) {
            throw ImppExceptionBuilder.newInstance()
                    .setSystemID(CommonEnumUtil.SOFT_TYPE.MES_PCN.getCode())
                    .setErrorCode(ImppExceptionEnum.VARIFY_EXCEPTION_DATA_NOT_EXIT.getCode())
                    .setErrorDetail("生产线[%s]不存在", workCenterCode)
                    .build();
        }
        if (StringUtils.isEmpty(workCenterExt.getLineType()) || MesPcnExtEnumUtil.LINE_TYPE.ONTOLOGY_LINE.getValueStr().equals(workCenterExt.getLineType())) {
            throw ImppExceptionBuilder.newInstance()
                    .setSystemID(CommonEnumUtil.SOFT_TYPE.MES_PCN.getCode())
                    .setErrorCode(ImppExceptionEnum.VARIFY_EXCEPTION.getCode())
                    .setErrorDetail("生产线[%s]不属于组件线", workCenterCode)
                    .build();
        }
        return workCenterExt;
    }

    @Override
    public synchronized SxSubassemblyRemadeModel doScanSubassemblyRemadeSn(SxSubassemblyRemadeModel model) {
        log.info("组件返工[扫描],生产线[{}]操作人[{}]线程[{}]", model.getWorkCenterCode(), model.getUserInfo(), Thread.currentThread().getName());
        
        //验证生产线是否属于组件线
        checkWorkCenterIsSubassembly(model.getOrganizeCode(), model.getWorkCenterCode());

        //获取工单
        MesWorkOrder workOrder = workOrderExtService.getWorkOrderByOrderNo(model.getOrganizeCode(), model.getWorkOrderNo());

        //验证工单
        checkWorkOrderExt4Scan(workOrder, model);

        //获取缓存中的工单数据
        SxSubassemblyRemadeModel modelCached = (SxSubassemblyRemadeModel) doHandleCachedData(model.getOrganizeCode(), model.getWorkCenterCode(), 1, null);

        //验证工单与缓存工单是否一致
        modelCached = doCheckWorkOrderDdAndCached(model, workOrder, modelCached);

        //获取物料
        MesPart partDb = getAndCheckPart(model.getOrganizeCode(), model.getWorkCenterCode(), model.getWorkOrderNo(), workOrder.getPartNo());

        //根据条码获取条码信息
        SxSubassemblyRemadeDetailModel detail = ((ISxSubassemblyRemadeStrategyService) SpringContextsUtil.
                getBean(getClazz(model.getOrganizeCode(), partDb.getCategoryCode2()))).
                querySubassemblyRemadeSn(model.getOrganizeCode(), model.getSn(), model.getWorkOrderNo(), partDb.getPartNo(), model.getWorkCenterCode());

        //验证条码是否已经绑定此返工工单
        checkSnIsBindOrder(model.getOrganizeCode(), detail.getSn(), detail.getWorkOrderNo());

        //验证条码是否存在缓存
        checkSnIsCached(detail.getSn(), modelCached);

        //初始化数据
        if (null == modelCached) modelCached = initSubassemblyRemadeData(workOrder, model.getUserInfo());

        //条码写入缓存
        return doCachedDetailList2SubassemblyRemadeData(model, modelCached, detail);
    }

    private SxSubassemblyRemadeModel doCachedDetailList2SubassemblyRemadeData(
            SxSubassemblyRemadeModel model, SxSubassemblyRemadeModel modelCached, SxSubassemblyRemadeDetailModel detail) {

        List<SxSubassemblyRemadeDetailModel> detailList = CollectionUtils.isEmpty(modelCached.getDetailList()) ? new ArrayList<>() : modelCached.getDetailList();
        detailList.add(detail);
        modelCached.setDetailList(detailList);
        modelCached.setLastSn(detail.getSn());

        doHandleCachedData(model.getOrganizeCode(), model.getWorkCenterCode(), 2, modelCached);

        log.info("组件返工[扫描]条码写入缓存,生产线[{}]操作人[{}]线程[{}]缓存[{}]",
                model.getWorkCenterCode(), model.getUserInfo(), Thread.currentThread().getName(), JSONObject.toJSONString(modelCached));

        return sortDetailList2SubassemblyRemadeModel(modelCached, model);
    }

    private void checkSnIsCached(String sn, SxSubassemblyRemadeModel modelCached) {
        if (null != modelCached && !CollectionUtils.isEmpty(modelCached.getDetailList())) {
            Optional<SxSubassemblyRemadeDetailModel> detailOp = modelCached.getDetailList().stream().filter(o -> (null != o && o.getSn().equals(sn))).findFirst();
            if (null != detailOp && detailOp.isPresent()) {
                throw ImppExceptionBuilder.newInstance()
                        .setSystemID(CommonEnumUtil.SOFT_TYPE.MES_PCN.getCode())
                        .setErrorCode(ImppExceptionEnum.VARIFY_EXCEPTION.getCode())
                        .setErrorDetail("条码[%s]不可重复扫描", sn)
                        .build();
            }
        }
    }

    private void checkSnIsBindOrder(String organizeCode, String sn, String workOrderNo) {
        MesSubassemblyRemadeRecord repair = subassemblyRemadeRecordService.getSubassemblyRemadeRecordBySnAndWorkOrderNo(organizeCode, sn, workOrderNo);
        if (null != repair) {
            throw ImppExceptionBuilder.newInstance()
                    .setSystemID(CommonEnumUtil.SOFT_TYPE.MES_PCN.getCode())
                    .setErrorCode(ImppExceptionEnum.VARIFY_EXCEPTION_DATA_EXIT.getCode())
                    .setErrorDetail("扫描的条码[%s]已经关联返工工单[%s]", sn, workOrderNo)
                    .build();
        }
    }

    private String getClazz(String organizeCode, String categoryCode2) {
        //获取实现方法
        String clazz = MesPcnExtEnumUtil.ORG_ZJ_REMADE_METHOD_CFG.valueOfCalss(getConfig(organizeCode), categoryCode2);
        if (StringUtils.isEmpty(clazz)) {
            throw ImppExceptionBuilder.newInstance()
                    .setSystemID(CommonEnumUtil.SOFT_TYPE.MES_PCN.getCode())
                    .setErrorCode(ImppExceptionEnum.VARIFY_EXCEPTION_DATA_NOT_EXIT.getCode())
                    .setErrorDetail("工厂[%s]未配置组件返工条码查询策略[%s]", organizeCode, MesPcnExtConstWords.ORG_ZJ_REMADE_METHOD_CFG)
                    .build();
        }
        return clazz;
    }

    private String getConfig(String organizeCode) {
        MesConfig config = configService.getMesConfigByCfgCode(MesPcnExtConstWords.ORG_ZJ_REMADE_METHOD_CFG, organizeCode);
        return (null == config || StringUtils.isEmpty(config.getCfgValue())) ? null : config.getCfgValue();
    }

    private SxSubassemblyRemadeModel doCheckWorkOrderDdAndCached(SxSubassemblyRemadeModel model, MesWorkOrder workOrder, SxSubassemblyRemadeModel modelCached) {
        if (null != modelCached) {
            if (!workOrder.getOrderNo().equals(modelCached.getWorkOrderNo())) {
                log.info("组件返工[扫描]验证缓存工单与显示工单不一致,已删除缓存,生产线[{}]操作人[{}]线程[{}]工单[{}]缓存[{}]",
                        model.getWorkCenterCode(), model.getUserInfo(), Thread.currentThread().getName(), JSONObject.toJSONString(workOrder), JSONObject.toJSONString(modelCached));
                doHandleCachedData(model.getOrganizeCode(), model.getWorkCenterCode(), 3, null);
                modelCached = null;
            } else {
                if (MathOperation.compareTo(workOrder.getQty(), modelCached.getQty()) != 0 ||
                        MathOperation.compareTo(workOrder.getCompleteQty(), modelCached.getCompleteQty()) != 0) {
                    log.info("组件返工[扫描]验证缓存工单数量与显示工单数量不一致,已更新缓存,生产线[{}]操作人[{}]线程[{}]工单[{}]缓存[{}]",
                            model.getWorkCenterCode(), model.getUserInfo(), Thread.currentThread().getName(), JSONObject.toJSONString(workOrder), JSONObject.toJSONString(modelCached));
                    modelCached.setQty(workOrder.getQty());
                    modelCached.setCompleteQty(workOrder.getCompleteQty());
                    doHandleCachedData(model.getOrganizeCode(), model.getWorkCenterCode(), 2, modelCached);
                }
            }
        }

        if (null == modelCached) {
            //缓存中的工单数据不存在时获取当前生产线的首个启动工单
            MesWorkOrder workOrderDb = workOrderExtService.getFirstOpenedRemadeWorkOrderByWorkCenterCode(workOrder.getOrganizeCode(), workOrder.getWorkCenterCode());
            if (null == workOrderDb) {
                throw ImppExceptionBuilder.newInstance()
                        .setSystemID(CommonEnumUtil.SOFT_TYPE.MES_PCN.getCode())
                        .setErrorCode(ImppExceptionEnum.VARIFY_EXCEPTION_DATA_NOT_EXIT.getCode())
                        .setErrorDetail("生产线[%s]暂无启动状态的生产工单", workOrder.getWorkCenterCode())
                        .build();
            }
            if (!workOrderDb.getOrderNo().equals(workOrder.getOrderNo())) {
                throw ImppExceptionBuilder.newInstance()
                        .setSystemID(CommonEnumUtil.SOFT_TYPE.MES_PCN.getCode())
                        .setErrorCode(ImppExceptionEnum.VARIFY_EXCEPTION_DATA_NOT_EXIT.getCode())
                        .setErrorDetail("生产线[%s]当前首个启动状态的返工工单[%s]与当前显示的生产工单[%s]不一致,请点击刷新按钮",
                                workOrder.getWorkCenterCode(), workOrderDb.getOrderNo(), workOrder.getOrderNo())
                        .build();
            }
        }
        return modelCached;
    }

    private void checkWorkOrderExt4Scan(MesWorkOrder workOrder, SxSubassemblyRemadeModel model) {
        if (null == workOrder) {
            throw ImppExceptionBuilder.newInstance()
                    .setSystemID(CommonEnumUtil.SOFT_TYPE.MES_PCN.getCode())
                    .setErrorCode(ImppExceptionEnum.VARIFY_EXCEPTION_DATA_NOT_EXIT.getCode())
                    .setErrorDetail("生产工单[%s]不存在,请点击重置按钮", model.getWorkOrderNo())
                    .build();
        }

        if (!workOrder.getWorkCenterCode().equals(model.getWorkCenterCode())) {
            throw ImppExceptionBuilder.newInstance()
                    .setSystemID(CommonEnumUtil.SOFT_TYPE.MES_PCN.getCode())
                    .setErrorCode(ImppExceptionEnum.VARIFY_EXCEPTION.getCode())
                    .setErrorDetail("生产工单[%s]关联的生产线[%s]与当前生产线[%s]不一致,请点击重置按钮",
                            workOrder.getOrderNo(), workOrder.getWorkCenterCode(), model.getWorkCenterCode())
                    .build();
        }

        if (MesPcnExtEnumUtil.WORK_ORDER_STATUS.OPEN.getValue() != workOrder.getWorkOrderStatus()) {
            throw ImppExceptionBuilder.newInstance()
                    .setSystemID(CommonEnumUtil.SOFT_TYPE.MES_PCN.getCode())
                    .setErrorCode(ImppExceptionEnum.VARIFY_EXCEPTION_DATA_NOT_EXIT.getCode())
                    .setErrorDetail("生产线[%s]生产工单[%s]状态为[%s],请点击重置按钮",
                            workOrder.getWorkCenterCode(), workOrder.getOrderNo(), MesPcnExtEnumUtil.WORK_ORDER_STATUS.valueOfDescription(workOrder.getWorkOrderStatus()))
                    .build();
        }

        if (MesPcnExtEnumUtil.WORK_ORDER_TYPE.REWORK_ORDER.getValue() != workOrder.getWorkOrderType()) {
            throw ImppExceptionBuilder.newInstance()
                    .setSystemID(CommonEnumUtil.SOFT_TYPE.MES_PCN.getCode())
                    .setErrorCode(ImppExceptionEnum.VARIFY_EXCEPTION_DATA_NOT_EXIT.getCode())
                    .setErrorDetail("生产线[%s]生产工单[%s]类型为[%s],请点击重置按钮",
                            workOrder.getWorkCenterCode(), workOrder.getOrderNo(), MesPcnExtEnumUtil.WORK_ORDER_TYPE.valueOfDescription(workOrder.getWorkOrderType()))
                    .build();
        }

        if (MathOperation.compareTo(workOrder.getQty(), workOrder.getCompleteQty()) == 0) {
            throw ImppExceptionBuilder.newInstance()
                    .setSystemID(CommonEnumUtil.SOFT_TYPE.MES_PCN.getCode())
                    .setErrorCode(ImppExceptionEnum.VARIFY_EXCEPTION.getCode())
                    .setErrorDetail("生产线[%s]生产工单[%s]计划数量[%s]与完成数量[%s]已一致,且工单状态还是[启动]状态,请先变更此工单的状态,再点击重置按钮",
                            workOrder.getWorkCenterCode(), workOrder.getOrderNo(), workOrder.getQty(), workOrder.getCompleteQty())
                    .build();
        }
    }

    @Override
    public synchronized SxSubassemblyRemadeModel updateSubassemblyRemadeSn(SxSubassemblyRemadeModel model) {
        log.info("组件返工[修改],生产线[{}]操作人[{}]线程[{}]", model.getWorkCenterCode(), model.getUserInfo(), Thread.currentThread().getName());

        //获取缓存中的工单数据
        SxSubassemblyRemadeModel modelCached = doGetAndCheckCachedSubassemblyRemadeData(model);

        //验证缓存数据是否存在
        checkModelCachedIsNull(modelCached, model);

        //修改数据
        doUpdateSn4ModelCached(modelCached, model);

        //数据回显
        return sortDetailList2SubassemblyRemadeModel(modelCached, model);
    }

    private void doUpdateSn4ModelCached(SxSubassemblyRemadeModel modelCached, SxSubassemblyRemadeModel model) {
        List<SxSubassemblyRemadeDetailModel> detailList = modelCached.getDetailList();

        Integer index = null;
        if (!CollectionUtils.isEmpty(detailList)) {
            for (int i = 0; i < detailList.size(); i ++) {
                if (detailList.get(i).getSn().equals(model.getSn())) {
                    index = i;
                    break;
                }
            }
        }
        if (CollectionUtils.isEmpty(detailList) || StringUtils.isEmpty(index)) {
            throw ImppExceptionBuilder.newInstance()
                    .setSystemID(CommonEnumUtil.SOFT_TYPE.MES_PCN.getCode())
                    .setErrorCode(ImppExceptionEnum.VARIFY_EXCEPTION_DATA_NOT_EXIT.getCode())
                    .setErrorDetail("生产线[%s]组件返工缓存数据不包含此条码[%s],请点击刷新按钮", model.getWorkCenterCode(), model.getSn())
                    .build();
        }

        if (CommonEnumUtil.TRUE_OR_FALSE.TRUE.getValue() == model.getActionType()) {
            if (MathOperation.compareTo(model.getQty(), detailList.get(index).getInitQty()) > 0) {
                throw ImppExceptionBuilder.newInstance()
                        .setSystemID(CommonEnumUtil.SOFT_TYPE.MES_PCN.getCode())
                        .setErrorCode(ImppExceptionEnum.VARIFY_EXCEPTION_DATA_NOT_EXIT.getCode())
                        .setErrorDetail("条码[%s]修改的返工数量[%s]不能超过初始数量[%s],请点击刷新按钮", model.getSn(), model.getQty(), detailList.get(index).getInitQty())
                        .build();
            }
            detailList.get(index).setQty(model.getQty());
        } else {
            detailList.remove(index.intValue());
        }

        if (CollectionUtils.isEmpty(detailList)) {
            doHandleCachedData(model.getOrganizeCode(), model.getWorkCenterCode(), 3, null);
            log.info("组件返工[修改]已经不存在条码明细,已删除缓存,生产线[{}]操作人[{}]线程[{}]", model.getWorkCenterCode(), model.getUserInfo(), Thread.currentThread().getName());
        } else {
            doHandleCachedData(model.getOrganizeCode(), model.getWorkCenterCode(), 2, modelCached);
            log.info("组件返工[修改]已更新缓存,生产线[{}]操作人[{}]线程[{}]缓存[{}]",
                    model.getWorkCenterCode(), model.getUserInfo(), Thread.currentThread().getName(), JSONObject.toJSON(modelCached));
        }
    }

    private void checkModelCachedIsNull(SxSubassemblyRemadeModel modelCached, SxSubassemblyRemadeModel model) {
        if (null == modelCached) {
            throw ImppExceptionBuilder.newInstance()
                    .setSystemID(CommonEnumUtil.SOFT_TYPE.MES_PCN.getCode())
                    .setErrorCode(ImppExceptionEnum.VARIFY_EXCEPTION_DATA_NOT_EXIT.getCode())
                    .setErrorDetail("生产线[%s]组件返工缓存数据已经不存在,请点击刷新按钮", model.getWorkCenterCode())
                    .build();
        }
    }

    @Override
    public synchronized SxSubassemblyRemadeModel insertSubassemblyRemadeSn(SxSubassemblyRemadeModel model) {
        log.info("组件返工[提交],生产线[{}]操作人[{}]线程[{}]", model.getWorkCenterCode(), model.getUserInfo(), Thread.currentThread().getName());

        //获取缓存中的工单数据
        SxSubassemblyRemadeModel modelCached = (SxSubassemblyRemadeModel) doHandleCachedData(model.getOrganizeCode(), model.getWorkCenterCode(), 1, null);

        //验证缓存数据是否存在
        checkModelCachedIsNull4Insert(modelCached, model);

        //验证生产线是否属于组件线
        MesWorkCenterExt workCenterExt = checkWorkCenterIsSubassembly(model.getOrganizeCode(), model.getWorkCenterCode());

        //验证生产线是否已启动班次
        checkCenterIsStart(workCenterExt);

        //获取工单
        MesWorkOrderExt workOrder = workOrderExtService.getWorkOrderExtByOrderNo(model.getOrganizeCode(), model.getWorkOrderNo());

        //验证工单
        checkWorkOrderExt4Scan(workOrder, model);

        //验证工单与缓存工单是否一致
        doCheckWorkOrderDdAndCached4Insert(model, workOrder, modelCached);

        //验证物料
        getAndCheckPart(model.getOrganizeCode(), model.getWorkCenterCode(), model.getWorkOrderNo(), workOrder.getPartNo());

        //验证工单数量与条码明细的数量汇总
        Double completeQty = getAndcheckCompleteQty4Insert(modelCached);

        //写入DB
        insertSubassemblyRemadeData(modelCached, completeQty, workOrder, workCenterExt, model.getUserInfo());

        //提交后删除缓存数据
        doHandleCachedData(model.getOrganizeCode(), model.getWorkCenterCode(), 3, null);

        //初始化数据
        return initSubassemblyRemadeData(workOrder, model.getUserInfo());
    }

    private void checkCenterIsStart(MesWorkCenterExt workCenterExt) {
        if (StringUtils.isEmpty(workCenterExt.getWorkTime()) || StringUtils.isEmpty(workCenterExt.getShiftCode())) {
            throw ImppExceptionBuilder.newInstance()
                    .setSystemID(CommonEnumUtil.SOFT_TYPE.MES_PCN.getCode())
                    .setErrorCode(ImppExceptionEnum.VARIFY_EXCEPTION.getCode())
                    .setErrorDetail("生产线[%s]未启动班次", workCenterExt.getWorkCenterCode())
                    .build();
        }
        MesShift shiftDb = shiftExtService.getShiftByShiftCodeAndCenterCode(workCenterExt.getOrganizeCode(), workCenterExt.getWorkCenterCode(), workCenterExt.getShiftCode());
        if (null == shiftDb) {
            throw ImppExceptionBuilder.newInstance()
                    .setSystemID(CommonEnumUtil.SOFT_TYPE.MES_PCN.getCode())
                    .setErrorCode(ImppExceptionEnum.VARIFY_EXCEPTION_DATA_NOT_EXIT.getCode())
                    .setErrorDetail("生产线[%s]班次[%s]信息不存在", workCenterExt.getWorkCenterCode(), workCenterExt.getShiftCode())
                    .build();
        }
        String workTime = shiftProdCenterRecordService.getWorkTime(shiftDb.getStartTime(), shiftDb.getEndTime());
        if (StringUtils.isEmpty(workTime) || !workTime.equals(workCenterExt.getWorkTime())) {
            throw ImppExceptionBuilder.newInstance()
                    .setSystemID(CommonEnumUtil.SOFT_TYPE.MES_PCN.getCode())
                    .setErrorCode(ImppExceptionEnum.BUSINESS_EXCEPTION.getCode())
                    .setErrorDetail("生产线[%s]需要重新启动班次", workCenterExt.getWorkCenterCode())
                    .build();
        }
    }

    private void insertSubassemblyRemadeData(SxSubassemblyRemadeModel modelCached, Double completeQty, MesWorkOrderExt workOrder, MesWorkCenterExt workCenterExt, String userInfo) {
        log.info("组件返工[提交]写入DB,生产线[{}]操作人[{}]线程[{}]工单完成数量[{}]缓存[{}]",
                modelCached.getWorkCenterCode(), userInfo, Thread.currentThread().getName(), completeQty, JSONObject.toJSONString(modelCached));
        for (SxSubassemblyRemadeDetailModel detail : modelCached.getDetailList()) {
            if (null == detail) {
                continue;
            }
            MesSubassemblyRemadeRecord record = new MesSubassemblyRemadeRecord();
            record.setSystemSyncStatus(MesPcnExtEnumUtil.IF_SYNC_STATUS.NO_SYNC.getValue());
            record.setSn(detail.getSn());
            record.setPackageNo(detail.getPackageNo());
            record.setCtNo(detail.getCtNo());
            record.setLotNo(detail.getLotNo());
            record.setQty(detail.getQty());
            record.setInitWorkOrderNo(detail.getInitWorkOrderNo());
            record.setInitWorkCenterCode(detail.getInitWorkCenterCode());
            record.setInitQty(detail.getInitQty());
            record.setWorkOrderNo(modelCached.getWorkOrderNo());
            record.setWorkCenterCode(modelCached.getWorkCenterCode());
            record.setPartNo(modelCached.getPartNo());
            record.setPartNameRdd(modelCached.getPartNameRdd());
            record.setWorkTime(workCenterExt.getWorkTime());
            record.setShiftCode(workCenterExt.getShiftCode());
            record.setOrganizeCode(modelCached.getOrganizeCode());
            ConvertBean.serviceModelInitialize(record, userInfo);
            subassemblyRemadeRecordRepository.insert(record);
        }

        workOrder.setCompleteQty(completeQty);
        if (MathOperation.compareTo(workOrder.getQty(), completeQty) == 0) {
            workOrder.setWorkOrderStatus(MesPcnExtEnumUtil.WORK_ORDER_STATUS.CLOSE.getValue());
        }
        workOrder.setSystemSyncStatus(MesPcnExtEnumUtil.IF_SYNC_STATUS.NO_SYNC.getValue());
        ConvertBean.serviceModelUpdate(workOrder, userInfo);
        workOrderRepository.save(workOrder);

        MesOutPutStatistics outPutStatistics = outPutStatisticsService.getOutPutStatisticsByTimeAndOrderNo(
                modelCached.getOrganizeCode(), modelCached.getWorkOrderNo(), workCenterExt.getWorkTime(), workCenterExt.getShiftCode());
        if (null == outPutStatistics) {
            outPutStatistics = new MesOutPutStatistics();
            outPutStatistics.setWorkCenterCode(modelCached.getWorkCenterCode());
            outPutStatistics.setWorkOrderNo(modelCached.getWorkOrderNo());
            outPutStatistics.setPartNo(modelCached.getPartNo());
            outPutStatistics.setPartNameRdd(modelCached.getPartNameRdd());
            outPutStatistics.setShiftCode(workCenterExt.getShiftCode());
            outPutStatistics.setWorkTime(workCenterExt.getWorkTime());
            outPutStatistics.setOrderStartTime(workOrder.getStartTime());
            outPutStatistics.setQty(modelCached.getAmountQty());
            outPutStatistics.setPlanQty(workOrder.getQty());
            outPutStatistics.setPlanRemakeQty(workOrder.getPlanQty());
            outPutStatistics.setRemakeQty(new Double(0));
            outPutStatistics.setWorkOrderType(workOrder.getWorkOrderType());
            outPutStatistics.setOrganizeCode(modelCached.getOrganizeCode());
            outPutStatistics.setCategoryCode(workCenterExt.getCategoryCode());
            outPutStatistics.setCareerCode(workCenterExt.getCareerCode());
            outPutStatistics.setOutPutFlag(workOrder.getOutPutFlag());
            outPutStatistics.setSystemSyncStatus(MesPcnExtEnumUtil.IF_SYNC_STATUS.NO_SYNC.getValue());
            ConvertBean.serviceModelInitialize(outPutStatistics, userInfo);
            outPutStatistics.setStartTime(outPutStatistics.getModifyDatetime());
            outPutStatistics.setEndTime(outPutStatistics.getStartTime());
            outPutStatisticsRepository.insert(outPutStatistics);
        } else {
            outPutStatistics.setQty(MathOperation.add(outPutStatistics.getQty(), modelCached.getAmountQty()));
            ConvertBean.serviceModelUpdate(outPutStatistics, userInfo);
            outPutStatistics.setEndTime(outPutStatistics.getModifyDatetime());
            outPutStatistics.setSystemSyncStatus(MesPcnExtEnumUtil.IF_SYNC_STATUS.NO_SYNC.getValue());
            outPutStatisticsRepository.save(outPutStatistics);
        }
        outPutStatisticsTravelService.insertOutPutStatisticsTravel(outPutStatistics);
    }

    private Double getAndcheckCompleteQty4Insert(SxSubassemblyRemadeModel modelCached) {
        Double amountQty = modelCached.getDetailList().stream()
                .filter(o -> (null != o && !StringUtils.isEmpty(o.getQty()))).mapToDouble(SxSubassemblyRemadeDetailModel::getQty).sum();
        Double completeQty = MathOperation.add(modelCached.getCompleteQty(), amountQty);
        if (MathOperation.compareTo(modelCached.getQty(), completeQty) < 0) {
            throw ImppExceptionBuilder.newInstance()
                    .setSystemID(CommonEnumUtil.SOFT_TYPE.MES_PCN.getCode())
                    .setErrorCode(ImppExceptionEnum.VARIFY_EXCEPTION.getCode())
                    .setErrorDetail("生产线[%s]组件返工工单[%s]当前完成数量[%s]累加条码明细汇总数量[%s]之和[%s]已超过工单计划数量[%s],提交失败",
                            modelCached.getWorkCenterCode(), modelCached.getWorkOrderNo(),
                            modelCached.getCompleteQty().intValue(), amountQty.intValue(), completeQty.intValue(), modelCached.getQty().intValue())
                    .build();
        }
        modelCached.setAmountQty(amountQty);
        return completeQty;
    }

    private void checkModelCachedIsNull4Insert(SxSubassemblyRemadeModel modelCached, SxSubassemblyRemadeModel model) {
        checkModelCachedIsNull(modelCached, model);
        checkModelCachedDetailListIsNull(modelCached, model);
    }

    private void checkModelCachedDetailListIsNull(SxSubassemblyRemadeModel modelCached, SxSubassemblyRemadeModel model) {
        if (CollectionUtils.isEmpty(modelCached.getDetailList())) {
            throw ImppExceptionBuilder.newInstance()
                    .setSystemID(CommonEnumUtil.SOFT_TYPE.MES_PCN.getCode())
                    .setErrorCode(ImppExceptionEnum.VARIFY_EXCEPTION_DATA_NOT_EXIT.getCode())
                    .setErrorDetail("生产线[%s]组件返工缓存数据已经不存在条码明细,请点击刷新按钮", model.getWorkCenterCode())
                    .build();
        }
    }

    private void doCheckWorkOrderDdAndCached4Insert(SxSubassemblyRemadeModel model, MesWorkOrder workOrder, SxSubassemblyRemadeModel modelCached) {
        if (!workOrder.getOrderNo().equals(modelCached.getWorkOrderNo())) {
            throw ImppExceptionBuilder.newInstance()
                    .setSystemID(CommonEnumUtil.SOFT_TYPE.MES_PCN.getCode())
                    .setErrorCode(ImppExceptionEnum.VARIFY_EXCEPTION_DATA_NOT_EXIT.getCode())
                    .setErrorDetail("生产线[%s]显示工单[%s]与缓存工单[%s]不一致,请点击刷新按钮", model.getWorkCenterCode(), model.getWorkOrderNo(), modelCached.getWorkOrderNo())
                    .build();
        } else {
            if (MathOperation.compareTo(workOrder.getQty(), modelCached.getQty()) != 0 ||
                    MathOperation.compareTo(workOrder.getCompleteQty(), modelCached.getCompleteQty()) != 0) {
                log.info("组件返工[提交]验证缓存工单数量与显示工单数量不一致,已更新缓存,生产线[{}]操作人[{}]线程[{}]工单[{}]缓存[{}]",
                        model.getWorkCenterCode(), model.getUserInfo(), Thread.currentThread().getName(), JSONObject.toJSONString(workOrder), JSONObject.toJSONString(modelCached));
                modelCached.setQty(workOrder.getQty());
                modelCached.setCompleteQty(workOrder.getCompleteQty());
                doHandleCachedData(model.getOrganizeCode(), model.getWorkCenterCode(), 2, modelCached);
            }
        }
    }

    private String getKey(String organizeCode) {
        return new StringJoiner(MesPcnExtConstWords.COLON).add(organizeCode).add(MesPcnExtConstWords.ZJ_REMADE_DATA).toString();
    }

    public synchronized Object doHandleCachedData(String organizeCode, String workCenterCode, Integer flag, SxSubassemblyRemadeModel modelCached) {
        switch (flag) {
            case 1:
                return redisMesPcn.getHash(getKey(organizeCode), workCenterCode);
            case 2:
                return redisMesPcn.putHash(getKey(organizeCode), workCenterCode, modelCached, MesPcnEnumUtil.EXPIRE_TIME.NEVER.getValue());
            case 3:
                redisMesPcn.deleteHash(getKey(organizeCode), workCenterCode);
                return true;
            default:
                return null;
        }
    }
}
