package com.sw.mes.itemclean.clean.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.sw.mes.base.global.GlobalSwitch;
import com.sw.mes.base.global.SwitchConfig;
import com.sw.mes.core.annotation.Jinko;
import com.sw.mes.core.api.system.StoreFeignClients;
import com.sw.mes.core.api.system.YbFeignClients;
import com.sw.mes.core.context.BaseContextHandler;
import com.sw.mes.core.dto.*;
import com.sw.mes.core.dto.materialCleanDto.MaterialCleanModelDto;
import com.sw.mes.core.dto.sap.ReportSapChargItemDto;
import com.sw.mes.core.dto.sap.ReportSapGoodsmvtDto;
import com.sw.mes.core.dto.store.*;
import com.sw.mes.core.dto.wms.ReportSapWorkinghoursDto;
import com.sw.mes.core.enums.*;
import com.sw.mes.core.exception.BusinessException;
import com.sw.mes.core.response.Result;
import com.sw.mes.core.response.ResultCode;
import com.sw.mes.core.response.ResultGenerator;
import com.sw.mes.core.response.ResultWms;
import com.sw.mes.core.utils.IpUtils;
import com.sw.mes.core.utils.NumberUtil;
import com.sw.mes.core.utils.StringUtils;
import com.sw.mes.core.utils.ThreadManager;
import com.sw.mes.itemclean.clean.domain.*;
import com.sw.mes.itemclean.clean.dto.*;
import com.sw.mes.itemclean.clean.dto.boxNoOperate.BoxNoOperate;
import com.sw.mes.itemclean.clean.mapper.MaterialGroupMapper;
import com.sw.mes.itemclean.clean.mapping.ScanBoxNoMapping;
import com.sw.mes.itemclean.clean.service.*;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sw.mes.itemclean.clean.utils.MaterialGroupDateBatchNoGenerator;
import com.sw.mes.rabbitmq.constants.RoutingKey;
import com.sw.mes.rabbitmq.enums.ExchangeEnum;
import com.sw.mes.rabbitmq.enums.QueueEnum;
import com.sw.mes.rabbitmq.service.RabbitMqService;
import com.sw.mes.rabbitmq.service.RabbitMqService;
import io.swagger.models.auth.In;
import lombok.extern.slf4j.Slf4j;
import org.modelmapper.ModelMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.*;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.client.RestTemplate;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.sw.mes.base.global.GlobalSwitchNamespace.CODE_SWITCH;

/**
 * <p>
 * 组托入库 服务实现类
 * </p>
 *
 * @author liuzemin
 * @since 2022-11-10
 */
@Service
@Slf4j
public class MaterialGroupServiceImpl extends ServiceImpl<MaterialGroupMapper, MaterialGroup> implements MaterialGroupService, ScanBoxNoService {

    @Autowired
    private MaterialStoreSignBoxService materialStoreSignBoxService;

    @Autowired
    private MaterialGroupDetailService materialGroupDetailService;

    @Autowired
    private GenerateBusinessNoService generateBusinessNoService;

    @Autowired
    private MaterialBrokenDownService materialBrokenDownService;

    @Autowired
    private MaterialGroupMapper materialGroupMapper;

    @Autowired
    private MaterialApplyOrderService materialApplyOrderService;

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private RabbitMqService rabbitMqService;

    @Autowired
    private YbFeignClients ybFeignClients;

    @Autowired
    CleaningProcessFlowService cleaningProcessFlowService;

    @Autowired
    StoreFeignClients storeFeignClients;

    @Autowired
    private MaterialUnpackingService materialUnpackingService;

    @Autowired
    private MaterialGroupDateBatchNoGenerator materialGroupDateBatchNoGenerator;


    @Autowired
    private ModelMapper modelMapper;

    @Value("${spring.wmsServiceUrl}")
    private String wmsServiceUrl;
    @Value("${spring.profiles.active}")
    private String active;

    private final String recycleToWms = "/api/InboundRecycledMaterial";

    @Autowired
    @Qualifier(value = "cleanMesThreadPoolTaskExecutor")
    private ThreadPoolTaskExecutor cleanMesThreadPoolExecutor;

//todo: 临时替换破碎下料扫小箱码成托接口,之后再开启替换回来
//    @Override
//    public BoxPropertyDto scanBoxNo(GeneralScanBoxNoParam param) {
//        LambdaQueryWrapper<MaterialStoreSignBox> queryWrapper = new LambdaQueryWrapper<>();
//        queryWrapper.eq(MaterialStoreSignBox::getMaterialBoxNo, param.getBoxNo());
//        queryWrapper.eq(MaterialStoreSignBox::getPickingStatus, 1);
////        queryWrapper.in(MaterialStoreSignBox::getMaterialType, Arrays.asList(ItemCleanEnums.MaterialType.破碎料.getKey()));
//        queryWrapper.last("limit 1");
//        MaterialStoreSignBox box = materialStoreSignBoxService.getOne(queryWrapper);
//        if (box == null) {
//            throw new RuntimeException(param.getBoxNo() + "该箱号不存在！");
//        }
//        if (box.getIsEmptyBox() == 1) {
//            throw new RuntimeException(param.getBoxNo() + "该箱号为空箱！");
//        }
//        if (box.getMaterialType() == ItemCleanEnums.MaterialType.破碎料.getKey()){
//            return ScanBoxNoMapping.toDto(box);
//        }else {
//            throw new RuntimeException(param.getScanName()+":当前箱号"+param.getBoxNo()+"属于"+ItemCleanEnums.MaterialType.getText(box.getMaterialType()));
//        }
//    }


    //todo:暂时启用此方法替代破碎下料扫码
    @Override
    public BoxPropertyDto scanBoxNo(GeneralScanBoxNoParam param) {

        //重写组托扫箱码接口
        //先从小箱成箱查询（新逻辑）；查不到的话再从待领料表查询（老逻辑）
        LambdaQueryWrapper<MaterialBrokenDown> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(MaterialBrokenDown::getMaterialBoxNo, param.getBoxNo());
        queryWrapper.eq(MaterialBrokenDown::getStatus, 0);
        queryWrapper.eq(MaterialBrokenDown::getDelFlag, 0);
        queryWrapper.last("limit 1");
        MaterialBrokenDown materialBrokenDown = materialBrokenDownService.getOne(queryWrapper);
        if (null != materialBrokenDown) {
            return ScanBoxNoMapping.toGroupItemDto(materialBrokenDown);
        } else {
            LambdaQueryWrapper<MaterialStoreSignBox> queryWrapperForSignBox = new LambdaQueryWrapper<>();
            queryWrapperForSignBox.eq(MaterialStoreSignBox::getMaterialBoxNo, param.getBoxNo());
            queryWrapperForSignBox.eq(MaterialStoreSignBox::getPickingStatus, 1);
            queryWrapper.last("limit 1");
            MaterialStoreSignBox box = materialStoreSignBoxService.getOne(queryWrapperForSignBox);
            if (box == null) {
                throw new RuntimeException(param.getBoxNo() + "该箱号不存在！");
            }
            if (box.getIsEmptyBox() == 1) {
                throw new RuntimeException(param.getBoxNo() + "该箱号为空箱！");
            }
            if (box.getMaterialType() == ItemCleanEnums.MaterialType.破碎料.getKey()) {
                return ScanBoxNoMapping.toDto(box);
            } else {
                throw new RuntimeException(param.getScanName() + ":当前箱号" + param.getBoxNo() + "属于" + ItemCleanEnums.MaterialType.getText(box.getMaterialType()));
            }
        }
    }

    @Override
    @Transactional(rollbackFor = Throwable.class)
    public MaterialGroup groupStore(GroupStoreDto dtos) {
        //1 检验同一pp批次
        checkGroupStore(dtos);

        //2 构建组托、合计重量
        MaterialGroup materialGroup = buildMaterialGroup(dtos);
        materialGroup.setWarehousingUser(BaseContextHandler.getUsername());
        materialGroup.setWarehousingTime(LocalDateTime.now());
        saveOrUpdate(materialGroup);

        //3 更新组托明细状态
        List<Integer> idList = dtos.getDetailDtos().stream().map(GroupStoreDetailDto::getId).collect(Collectors.toList());
        materialGroupDetailService.updateStatusByIds(idList, materialGroup);
        //4 整托入库到线边仓,根据返回结果判断是否发起成功
//        String result = startSurplusWarehousing(materialGroup, ItemCleanEnums.MaterialInputWarehouse.清洗硅料成品入库.getValue());
//        if (StringUtils.isNotBlank(result)) {
//            materialGroup.setUpdateTime(LocalDateTime.now());
//            materialGroup.setStatus(ItemCleanEnums.MaterialGroupStatus.已入库.getKey());
//            baseMapper.updateById(materialGroup);
//        }
        return materialGroup;
    }


    /**
     * 构建组托详情
     *
     * @param dtos
     * @param materialGroup
     * @return
     */
    private List<MaterialGroupDetail> buildMaterialGroupDetail(GroupStoreDto dtos, MaterialGroup materialGroup) {
        List<MaterialGroupDetail> list = new ArrayList<>();
        List<MaterialBrokenDown> boxList = new ArrayList<>();
        for (GroupStoreDetailDto detailDto : dtos.getDetailDtos()) {
            MaterialGroupDetail materialGroupDetail = new MaterialGroupDetail();
            materialGroupDetail.setMaterialBoxNo(detailDto.getMaterialBoxNo());
            materialGroupDetail.setWeight(detailDto.getWeight());
            materialGroupDetail.setMaterialCode(detailDto.getMaterialCode());
            materialGroupDetail.setMaterialName(detailDto.getMaterialName());
            materialGroupDetail.setMaterialLevel(detailDto.getMaterialLevel());
            materialGroupDetail.setMaterialModel(detailDto.getMaterialModel());
            materialGroupDetail.setMaterialGear(detailDto.getMaterialGear());
            materialGroupDetail.setProductBatchNo(detailDto.getProductBatchNo());
            materialGroupDetail.setGroupId(materialGroup.getId());
            //查询对应的破碎下料箱号对应身份号
            LambdaQueryWrapper<MaterialBrokenDown> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(MaterialBrokenDown::getMaterialBoxNo, detailDto.getMaterialBoxNo());
            queryWrapper.last("limit 1");
            MaterialBrokenDown materialBrokenDown = materialBrokenDownService.getOne(queryWrapper);
            materialGroupDetail.setCreateUser(null == materialBrokenDown ? "" : materialBrokenDown.getDownUser());
            materialGroupDetail.setCreateTime(LocalDateTime.now());
            materialGroupDetail.setUpdateUser(BaseContextHandler.getUsername());
            materialGroupDetail.setUpdateTime(LocalDateTime.now());
            materialGroupDetail.setAufnr(materialGroup.getAufnr());
            list.add(materialGroupDetail);
            materialBrokenDown.setStatus(1);

            //  出库站点: 进站,不存在出库出站
            cleaningProcessFlowService.inStation(materialBrokenDown.getMaterialBoxNo(), materialBrokenDown.getProductBatchNo(), materialBrokenDown.getWeight(), materialBrokenDown.getWeight(), BigDecimal.ZERO, materialBrokenDown.getDeviceNo(), EnumValuesFactory.CleaningWipStorageCode.出库.getValue(), "", materialBrokenDown.getMaterialBoxNo(), EnumValuesFactory.CleaningWipStorageCode.出库.getValue(), EnumValuesFactory.CleaningWipStorageStatus.工序进站.getKey());

        }
        materialBrokenDownService.updateBatchById(boxList);

        return list;
    }

    private MaterialGroup buildMaterialGroup(GroupStoreDto dtos) {
        GroupStoreDetailDto detailDto = dtos.getDetailDtos().get(0);
        MaterialGroup materialGroup = new MaterialGroup();
        BigDecimal totalWeight = dtos.getDetailDtos().stream().map(GroupStoreDetailDto::getWeight).reduce(BigDecimal.ZERO, BigDecimal::add);
        materialGroup.setId(detailDto.getId());
        materialGroup.setWeight(totalWeight);
        materialGroup.setMaterialCode(detailDto.getMaterialCode());
        materialGroup.setMaterialName(detailDto.getMaterialName());
        materialGroup.setMaterialLevel(detailDto.getMaterialLevel());
        materialGroup.setMaterialModel(detailDto.getMaterialModel());
        materialGroup.setMaterialGear(detailDto.getMaterialGear());
        materialGroup.setDeviceNo(dtos.getDeviceNo());
        //详情的分选批次号可能不同,存在混批;所以组托主表的批次号不再需要
        materialGroup.setProductBatchNo(detailDto.getProductBatchNo());
        materialGroup.setGroupNo(generateBusinessNoService.generateForGroupNo());
        materialGroup.setCreateUser(BaseContextHandler.getUsername());
        materialGroup.setCreateTime(LocalDateTime.now());
        materialGroup.setUpdateUser(BaseContextHandler.getUsername());
        materialGroup.setUpdateTime(LocalDateTime.now());
        materialGroup.setRemark(dtos.getRemark());
        materialGroup.setSuppliesGrain(dtos.getSuppliesGrain());
        materialGroup.setStatus(ItemCleanEnums.MaterialGroupStatus.未入库.getKey());
        MaterialUnpacking fxProductBatchNo = materialUnpackingService.getFxProductBatchNo(detailDto.getProductBatchNo());
        if (null != fxProductBatchNo) {
            materialGroup.setAufnr(fxProductBatchNo.getAufnr());
        }
        //通过工单查找参数
        String materialGroupDateBatchNo = getMaterialGroupDateBatchNo(fxProductBatchNo.getAufnr());
        materialGroup.setDateProduceBatchNo(materialGroupDateBatchNo);
        return materialGroup;
    }

    private void checkGroupStore(GroupStoreDto dtos) {
        List<GroupStoreDetailDto> detailDtos = dtos.getDetailDtos();
        if (detailDtos == null || detailDtos.size() <= 0) {
            throw new RuntimeException("组托内容为空");
        }
        Set<String> set = detailDtos.stream().map(GroupStoreDetailDto::getProductBatchNo).collect(Collectors.toSet());
        if (set.size() != 1) {
            throw new RuntimeException("不是相同批次号不能组托");
        }
    }

    private void checkGroupOrder(List<GroupStoreDetailDto> detailDtos) {
        if (detailDtos == null || detailDtos.size() <= 0) {
            throw new RuntimeException("组托内容为空");
        }
        List<String> collect = detailDtos.stream().map(GroupStoreDetailDto::getProductBatchNo).collect(Collectors.toList());
        //通过批次号去material_unpacking里面查询aufnr
        List<MaterialUnpacking> materialUnpackingByPpProductBatchNo = materialUnpackingService.getMaterialUnpackingByPpProductBatchNo(collect);
        //校验工单、物料编号、档位、等级必须一致
        Set<String> set = materialUnpackingByPpProductBatchNo.stream().map(MaterialUnpacking::getMaterialCode).collect(Collectors.toSet());
        if (set.size() != 1) {
            throw new RuntimeException("不是相同料号不能组托");
        }
        Set<String> set1 = materialUnpackingByPpProductBatchNo.stream().map(MaterialUnpacking::getMaterialGear).collect(Collectors.toSet());
        if (set1.size() != 1) {
            throw new RuntimeException("不是相同档位不能组托");
        }
        Set<String> set2 = materialUnpackingByPpProductBatchNo.stream().map(MaterialUnpacking::getAufnr).collect(Collectors.toSet());
        if (set2.size() != 1) {
            //多工单的时候,校验工单对应片区以及定向标识是否相同
            checkOrderInfo(set2);
        }
        Set<String> set3 = materialUnpackingByPpProductBatchNo.stream().map(MaterialUnpacking::getMaterialLevel).collect(Collectors.toSet());
        if (set3.size() != 1) {
            throw new RuntimeException("不是相同等级不能组托");
        }
    }

    /**
     * 组装清洗过账到wms数据
     *
     * @param materialGroup
     * @return
     */
    private RecycledMaterialInDto buildRecycledMaterialInDto(MaterialGroup materialGroup) {
        RecycledMaterialInDto recycledMaterialInDto = new RecycledMaterialInDto();
        recycledMaterialInDto.setBatchNo(materialGroup.getProductBatchNo());
        recycledMaterialInDto.setInQty(materialGroup.getWeight());
        recycledMaterialInDto.setLevelID(materialGroup.getMaterialLevel());
        recycledMaterialInDto.setMaterialClassID("1");
        recycledMaterialInDto.setMaterialNo(materialGroup.getMaterialCode());
        recycledMaterialInDto.setOperateDT(new SimpleDateFormat("yyyy-MM-dd").format(new Date()));
        recycledMaterialInDto.setOperator(BaseContextHandler.getUsername());
        recycledMaterialInDto.setOutQty(BigDecimal.ZERO);
        recycledMaterialInDto.setPlanNo(materialGroup.getGroupNo());
        recycledMaterialInDto.setRemark(null);
        recycledMaterialInDto.setSpecification(materialGroup.getMaterialModel());
        recycledMaterialInDto.setUnit("KG");
        return recycledMaterialInDto;
    }


    /**
     * 通过物料名称获取清洗模型数据
     *
     * @param materialName
     * @return
     */
    public MaterialCleanModelDto findByMaterialCleanName(String materialName) {
        //查找原料清洗模型的 厂家来源,硅料种类,物料名称
        Result<List<MaterialCleanModelDto>> result = ybFeignClients.findByName(materialName);
        if (!result.getCode().equals(ResultCode.SUCCESS.code)) {
            throw new RuntimeException(result.getMessage());
        }
        if (result.getData() == null) {
            throw new RuntimeException("原料清洗产品模型不存在" + materialName);
        }
        MaterialCleanModelDto materialCleanModelDto = null;
        List<MaterialCleanModelDto> materialCleanModelDtos = Optional.of(result).map(Result::getData).orElse(new ArrayList<>());
        if (CollectionUtil.isNotEmpty(materialCleanModelDtos)) {
            materialCleanModelDto = materialCleanModelDtos.get(0);
        }
        if (null == materialCleanModelDto) {
            throw new RuntimeException("原料清洗产品模型不存在: " + materialName);
        }
        return materialCleanModelDto;
    }

    /**
     * 组装清洗组托详情发到wms数据
     *
     * @param list
     * @param materialGroup
     * @return
     */
    private List<RecycledMaterialInDto> buildRecycledMaterialInDtos(List<MaterialGroupDetail> list, MaterialGroup materialGroup) {
        List<RecycledMaterialInDto> recycledMaterialInDtos = new ArrayList<>();

        //查找原料清洗模型的 厂家来源,厂家编码,硅料种类,物料名称
        String factory = "";
        String factoryCode = "";
        String siliconType = "";
        String name = "";
        if (CollectionUtil.isNotEmpty(list)) {
            String materialName = list.get(0).getMaterialName();
            MaterialCleanModelDto materialCleanModelDto = findByMaterialCleanName(materialName);
            factory = materialCleanModelDto.getFactory();
            factoryCode = materialCleanModelDto.getFactoryCode();
            siliconType = materialCleanModelDto.getSiliconType();
            name = materialCleanModelDto.getName();
        }
        for (MaterialGroupDetail detail : list) {
            RecycledMaterialInDto recycledMaterialInDto = new RecycledMaterialInDto();
            recycledMaterialInDto.setBatchNo(detail.getMaterialBoxNo());
            recycledMaterialInDto.setInQty(detail.getWeight());
            recycledMaterialInDto.setLevelID(detail.getMaterialLevel());
            recycledMaterialInDto.setMaterialClassID("1");
            recycledMaterialInDto.setMaterialNo(detail.getMaterialCode());
            recycledMaterialInDto.setOperateDT(new SimpleDateFormat("yyyy-MM-dd").format(new Date()));
            recycledMaterialInDto.setOperator(detail.getCreateUser());
            recycledMaterialInDto.setOutQty(BigDecimal.ZERO);
            recycledMaterialInDto.setPlanNo(materialGroup.getGroupNo());
            recycledMaterialInDto.setRemark(null);
            recycledMaterialInDto.setSpecification(detail.getMaterialModel());
            recycledMaterialInDto.setUnit("KG");
            recycledMaterialInDto.setSupplierName(factory);
            recycledMaterialInDto.setSupplierNo(factoryCode);
            recycledMaterialInDto.setAufnr(detail.getProductBatchNo());
            recycledMaterialInDto.setSiliconMaterialCategory(siliconType);
            recycledMaterialInDto.setSiliconMaterialSubclass(name);
            recycledMaterialInDto.setSupplierBatchNo(detail.getMaterialBoxNo());
            recycledMaterialInDto.setSupplierBoxNo(materialGroup.getGroupNo());

            recycledMaterialInDtos.add(recycledMaterialInDto);
        }

        return recycledMaterialInDtos;
    }


    private ResultWms requestRecycleMaterialToWMS(String url, RecycledMaterialInDto recycledMaterialInDto, String requestName) {
        String resultJson;
        boolean isErr = false;
        try {
            HttpHeaders headers = new HttpHeaders();
            MediaType mediaType = MediaType.parseMediaType("application/json; charset=UTF-8");
            headers.setContentType(mediaType);
            headers.add("Accept", MediaType.APPLICATION_JSON.toString());
            List<RecycledMaterialInDto> recycledMaterialInDtos = new ArrayList<>();
            recycledMaterialInDtos.add(recycledMaterialInDto);
            HttpEntity<List<RecycledMaterialInDto>> httpEntity = new HttpEntity<>(recycledMaterialInDtos, headers);
            ResponseEntity<String> responseEntity = restTemplate.postForEntity(url, httpEntity, String.class);
            resultJson = responseEntity.getBody();
        } catch (Exception e) {
            e.printStackTrace();
            resultJson = e.getMessage();
            isErr = true;
        }
        if (isErr) {
            return null;
        } else {
            return JSON.parseObject(resultJson, new TypeReference<ResultWms>() {
            });
        }
    }


    private ResultWms requestRecycleMaterialsToWMS(String url, List<RecycledMaterialInDto> recycledMaterialInDtos, String requestName) {
        String resultJson;
        boolean isErr = false;
        try {
            HttpHeaders headers = new HttpHeaders();
            MediaType mediaType = MediaType.parseMediaType("application/json; charset=UTF-8");
            headers.setContentType(mediaType);
            headers.add("Accept", MediaType.APPLICATION_JSON.toString());

            HttpEntity<List<RecycledMaterialInDto>> httpEntity = new HttpEntity<>(recycledMaterialInDtos, headers);
            ResponseEntity<String> responseEntity = restTemplate.postForEntity(url, httpEntity, String.class);
            resultJson = responseEntity.getBody();
        } catch (Exception e) {
            e.printStackTrace();
            resultJson = e.getMessage();
            isErr = true;
        }
        if (isErr) {
            return null;
        } else {
            return JSON.parseObject(resultJson, new TypeReference<ResultWms>() {
            });
        }
    }

    /**
     * 手动触发组托到wms
     *
     * @param groupNo 托号
     */
    @Override
    public String fbGroupToWms(String groupNo) {
        LambdaQueryWrapper<MaterialGroup> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(MaterialGroup::getGroupNo, groupNo);
        queryWrapper.last("limit 1");
        MaterialGroup materialGroup = getOne(queryWrapper);

        HashMap<String, Object> map = new HashMap<>();
        map.put("group_id", materialGroup.getId());
        List<MaterialGroupDetail> list = materialGroupDetailService.listByMap(map);

        // 入库到wms
        List<RecycledMaterialInDto> recycledMaterialInDtos = buildRecycledMaterialInDtos(list, materialGroup);
        log.info("托号{},状态{}:准备补传数据:{}", materialGroup.getGroupNo(), materialGroup.getStatus(), recycledMaterialInDtos);
        String url = wmsServiceUrl + recycleToWms;
        try {
            // 入库到wms
            ResultWms resultJson = requestRecycleMaterialsToWMS(url, recycledMaterialInDtos, "清洗成品循环料补偿入库到WMS");
            doRecord("清洗成品补偿入库到wms", url, JSON.toJSONString(recycledMaterialInDtos), resultJson);
            if (resultJson != null) {
                log.info("托号:{}清洗成品补偿入库到wms,返回标识:{},返回信息:{}", materialGroup.getGroupNo(), resultJson.getCode(), resultJson.getMsg());
                if ("1".equals(resultJson.getCode())) {
                    materialGroup.setUpdateTime(LocalDateTime.now());
                    materialGroup.setStatus(1);
                    baseMapper.updateById(materialGroup);
                }
            } else {
                log.info("托号:{}清洗成品补偿入库到wms失败", materialGroup.getGroupNo());
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("清洗组托补偿入库到wms: ", e);
        }

        return "补偿清洗数据成功";
    }

    /**
     * 解托托号发送到wms
     *
     * @param groupNo 托号(wms的PlanNo)
     * @return
     */
    @Override
    public String openGroupToWms(String groupNo) {
        //String url = wmsServiceUrl + "api/OpenGroupToWms";
        LambdaQueryWrapper<MaterialGroup> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(MaterialGroup::getGroupNo, groupNo);
        queryWrapper.last("limit 1");
        MaterialGroup materialGroup = getOne(queryWrapper);
        if (null == materialGroup) {
            throw new BusinessException("托号:" + groupNo + "信息不存在");
        } else {
//            List<OpenToWmsDto> openToWmsDtos = new ArrayList<>();
//            OpenToWmsDto openToWmsDto = new OpenToWmsDto();
//            openToWmsDto.setPlanNo(groupNo);
//            openToWmsDtos.add(openToWmsDto);
//            //待确定对方返回信息,什么情况对数据做处理
//            ResultWms resultJson = requestOpenGroupToWms(url, openToWmsDtos, "清洗成品循环料解托");
//            if (resultJson != null) {
//                if ("1".equals(resultJson.getCode())) {
            //操作清洗成品本身信息:1释放暂存区小箱,可再次扫描 2本托数据主表逻辑删除
            HashMap<String, Object> map = new HashMap<>();
            map.put("group_id", materialGroup.getId());
            List<MaterialGroupDetail> materialGroupDetails = materialGroupDetailService.listByMap(map);

            if (!CollectionUtils.isEmpty(materialGroupDetails)) {
                materialGroupDetails.stream().forEach(t -> {
                    materialStoreSignBoxService.updateByBoxNo(t.getMaterialBoxNo());
                });
            }
            materialGroupMapper.deleteById(materialGroup.getId());
//                } else if ("0".equals(resultJson.getCode())) {
//                    log.info("托号:{}清洗成品信息解托失败", groupNo);
//               }
//                log.info("托号:{}清洗成品解托,返回标识:{},返回信息:{}", groupNo, resultJson.getCode(), resultJson.getMsg());
//            } else {
//                log.info("托号:{}清洗成品解托失败", groupNo);
//                throw new BusinessException("托号:" + groupNo + "解托失败");
//            }
        }
        return "解托成功";
    }

    @Override
    public void groupStorePc(MaterialGroup materialGroup) {
        GroupDetailParamQuery groupDetailParamQuery = new GroupDetailParamQuery();
        groupDetailParamQuery.setGroupNo(materialGroup.getGroupNo());
        List<MaterialGroupDetail> list = materialGroupDetailService.findList(groupDetailParamQuery);
        if (list.isEmpty()) {
            throw new BusinessException("组托明细不存在1");
        }
        if (!active.equals("prod")) {
            log.info("清洗非生产环境，不请求wms");
        } else {
            try {
                // 入库到wms
                List<RecycledMaterialInDto> recycledMaterialInDtos = buildRecycledMaterialInDtos(list, materialGroup);
                String url = wmsServiceUrl + recycleToWms;
                ResultWms resultJson = requestRecycleMaterialsToWMS(url, recycledMaterialInDtos, "清洗成品循环料入库到WMS");
                doRecord("清洗成品循环料入库到WMS", url, JSON.toJSONString(recycledMaterialInDtos), resultJson);

                if (resultJson != null) {
                    log.info("托号:{}清洗成品入库到wms,返回标识:{},返回信息:{}", materialGroup.getGroupNo(), resultJson.getCode(), resultJson.getMsg());
                    if ("1".equals(resultJson.getCode())) {
                        materialGroup.setUpdateTime(LocalDateTime.now());
                        materialGroup.setStatus(1);
                        baseMapper.updateById(materialGroup);
                    }
                } else {
                    throw new BusinessException("清洗成品入库到wms失败");
                }
            } catch (Exception e) {
                throw new BusinessException("清洗成品入库到wms失败:" + e.getMessage());
            }

        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public MaterialGroup confirmGroup(GroupStoreDto dtos) {
        //1 检验同一pp批次
//        checkGroupStore(dtos);

        //校验工单、物料编号、档位、等级,工单对应片区,定向标识必须一致
        checkGroupOrder(dtos.getDetailDtos());

        //2 构建组托、合计重量
        MaterialGroup materialGroup = buildMaterialGroup(dtos);
        saveOrUpdate(materialGroup);

        //3 更新组托明细状态
        List<Integer> idList = dtos.getDetailDtos().stream().map(GroupStoreDetailDto::getId).collect(Collectors.toList());
        materialGroupDetailService.updateStatusByIds(idList, materialGroup);

        return materialGroup;
    }

    /**
     * 编辑组托记录
     *
     * @param dto
     * @return
     */
    @Override
    public Result editGroup(MaterialGroupEditDto dto) {
        if (dto.getId() == null)
            return ResultGenerator.fail("id不能为空");
        MaterialGroup entity = this.getById(dto.getId());
        if (entity == null)
            return ResultGenerator.fail("原信息不存在");
        if (ItemCleanEnums.MaterialGroupStatus.已入库.getKey() == entity.getStatus()) {
            return ResultGenerator.fail("该托已入库");
        }
        try {
            //校验工单、物料编号、档位、等级,工单对应片区,定向标识必须一致
            List<GroupStoreDetailDto> dtos = dto.getDetails().stream()
                    .map(detail -> toLazyDto(detail))
                    .collect(Collectors.toList());
            this.checkGroupOrder(dtos);
            //更新组托主表
            entity.setWeight(dto.getWeight());
            entity.setUpdateTime(LocalDateTime.now());
            entity.setUpdateUser(BaseContextHandler.getUsername());
            entity.setSuppliesGrain(dto.getSuppliesGrain());
            entity.setRemark(dto.getRemark());
            this.saveOrUpdate(entity);
            //更新详情表信息,先释放小箱盒数据为未组托,后增加
            materialGroupDetailService.updateStatusByGroupId(entity.getId());
            List<Integer> idList = dto.getDetails().stream().map(MaterialGroupDetail::getId).collect(Collectors.toList());
            materialGroupDetailService.updateStatusByIds(idList, entity);
            return ResultGenerator.success();
        } catch (Exception e) {
            return ResultGenerator.fail(e.getMessage());
        }
    }

    public GroupStoreDetailDto toLazyDto(MaterialGroupDetail entity) {
        return modelMapper.map(entity, GroupStoreDetailDto.class);
    }

//
//    @Override
//    @Transactional(rollbackFor = Exception.class)
//    public MaterialGroup groupStoreById(Integer id) {
//        //1 查询组托数据
//        MaterialGroup materialGroup = this.getById(id);
//        if (materialGroup == null) {
//            throw new BusinessException("原信息不存在");
//        }
//        if (ItemCleanEnums.MaterialGroupStatus.已入库.getKey() == materialGroup.getStatus()) {
//            throw new BusinessException("该托已入库");
//        }
//        //2 查询组托明细
//        List<MaterialGroupDetail> detailDtos = materialGroupDetailService.getGroupDetailsByGroupId(id);
//        //3 更新组托明细状态
//        List<Integer> idList = detailDtos.stream().map(MaterialGroupDetail::getId).collect(Collectors.toList());
//        materialGroupDetailService.updateStatusByIds(idList, materialGroup);
//        //4 整托入库到线边仓
//        // 4.1通过工单获取库位
//        MaterialUnpacking materialUnpacking = materialUnpackingService.getFxProductBatchNo(materialGroup.getProductBatchNo());
//        String workOrderNo = materialGroup.getAufnr();
//        if (StringUtils.isBlank(workOrderNo) && null != materialUnpacking) {
//            workOrderNo = materialUnpacking.getAufnr();
//        }
//        //4.2 获取调拨库位
//        HashMap<String, String> wareHouseCodeMapByWorkOrderNo = getWareHouseCodeMapByWorkOrderNo(workOrderNo);
//        String warehouseCode = wareHouseCodeMapByWorkOrderNo.get("warehouseCode");
//        String nextWarehouseCode = wareHouseCodeMapByWorkOrderNo.get("nextWarehouseCode");
//        String workCenterId = wareHouseCodeMapByWorkOrderNo.get("workCenterId");
//        //4.3 发起入库到主产品成品仓
//        String result = startSurplusWarehousing(workOrderNo, materialGroup, ItemCleanEnums.MaterialInputWarehouse.清洗硅料成品入库.getValue(), warehouseCode);
//        if (StringUtils.isNotBlank(result)) {
//            //5 入成品仓成功后后再调拨到材料仓
//            Integer code = this.handleWarehouseCodeMoveNextWarehouseCode(workCenterId,workOrderNo, materialGroup, warehouseCode, nextWarehouseCode);
//            if (ResultCode.SUCCESS.getCode() == code) {
//                materialGroup.setUpdateTime(LocalDateTime.now());
//                materialGroup.setStatus(ItemCleanEnums.MaterialGroupStatus.已入库.getKey());
//                materialGroup.setReportedState(EnumValues.ReportedErp.已上报.getKey());
//                materialGroup.setWarehouseCode(warehouseCode);
//                materialGroup.setNextWarehouseCode(nextWarehouseCode);
//                baseMapper.updateById(materialGroup);
//            } else {
//                throw new BusinessException("硅料清洗成品调拨失败!");
//            }
//        }
//        return materialGroup;
//    }

    /**
     * 根据线边仓改造,新写硅料成品发起入库并调拨接口
     *
     * @param id
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public MaterialGroup groupStoreById(Integer id) {
        //1 查询组托数据
        MaterialGroup materialGroup = this.getById(id);
        if (materialGroup == null) {
            throw new BusinessException("原信息不存在");
        }
        if (ItemCleanEnums.MaterialGroupStatus.已入库.getKey() == materialGroup.getStatus()) {
            throw new BusinessException("该托已入库");
        }
        //2 查询组托明细
        List<MaterialGroupDetail> detailDtos = materialGroupDetailService.getGroupDetailsByGroupId(id);
        //3 更新组托明细状态
        List<Integer> idList = detailDtos.stream().map(MaterialGroupDetail::getId).collect(Collectors.toList());
        materialGroupDetailService.updateStatusByIds(idList, materialGroup);
        //4 整托入库到线边仓
        // 4.1通过工单获取库位
        String workOrderNo = materialGroup.getAufnr();
        //4.2 获取调拨库位(在组托过程已经校验了:片区,定向标识等,所以组托详情的不同工单号对应库位一定相同)
        HashMap<String, String> wareHouseCodeMapByWorkOrderNo = getWareHouseCodeMapByWorkOrderNo(workOrderNo);
        String warehouseCode = wareHouseCodeMapByWorkOrderNo.get("warehouseCode");
        String nextWarehouseCode = wareHouseCodeMapByWorkOrderNo.get("nextWarehouseCode");
        String workCenterId = wareHouseCodeMapByWorkOrderNo.get("workCenterId");
        //4.3 发起入库到主产品成品仓,成功后线边仓内部调拨到材料仓
        //封装硅料成品入库数据
        WarehouseInputBillBo warehouseInputBillBo = newInputBill(materialGroup, ItemCleanEnums.MaterialInputWarehouse.清洗硅料成品入库.getValue(), warehouseCode);
        String unitName = warehouseInputBillBo.getInputBillDetails().get(0).getUnitName();
//        //封装硅料成品调拨数据
//        WarehouseAllotBillDto warehouseAllotBillDto = newAllotBill(materialGroup, warehouseCode, nextWarehouseCode);
//        //合并对象
//        StoreDto storeDto = new StoreDto();
//        storeDto.setInputBillBo(warehouseInputBillBo);
//        storeDto.setWarehouseAllotBillDto(warehouseAllotBillDto);
//        //创建自动入库单AND人工审核调拨单
//        Result<String> result = storeFeignClients.createInputAndAllotAndOut(storeDto);
        //切换成入库申请接口(由线边仓人工创建调拨单到具体库位)
        Result<String> result = storeFeignClients.inputApply(warehouseInputBillBo);
        Integer code = result.getCode();

        if (ResultCode.SUCCESS.getCode() != code) {
            throw new BusinessException("清洗硅料成品入库异常，状态码：" + code + "，反馈信息：" + result.getMessage());
        } else {
            //发起入库,主产品产出报工SAP
            try {
                for (MaterialGroupDetail detail : detailDtos) {
                    //发送详情到SAP
                    this.handleReportSapGoodsmvtDto(detail.getAufnr(), materialGroup.getMaterialCode(), materialGroup.getDateProduceBatchNo(), detail.getWeight(), unitName, warehouseCode);
                }
            } catch (Exception e) {
                log.error("硅料收货异常:" + e.getMessage());
            }
            //1.2调拨sap(改成由线边仓审核调拨-硅料主产品)
            //  this.handleWarehouseToNextWarehouseCodeDto(workOrderNo, materialGroup.getMaterialCode(), materialGroup.getGroupNo(), materialGroup.getWeight(), unitName, warehouseCode, nextWarehouseCode);
            //1.3更新组托数据
            materialGroup.setUpdateTime(LocalDateTime.now());
            materialGroup.setWarehousingTime(LocalDateTime.now());
            materialGroup.setWarehousingUser(BaseContextHandler.getUsername());
            materialGroup.setStatus(ItemCleanEnums.MaterialGroupStatus.已入库.getKey());
            materialGroup.setReportedState(EnumValues.ReportedErp.已上报.getKey());
            materialGroup.setWarehouseCode(warehouseCode);
            materialGroup.setNextWarehouseCode(nextWarehouseCode);
            baseMapper.updateById(materialGroup);
            try {
                //1.4 这里有字典校验,选择异步上报工时 上报逻辑：根据产出物料编码，产出重量/额定重量*小时
                ThreadManager.getThreadPollProxy().execute(() -> {
                    try {
                        // SwitchConfig config = GlobalSwitch.switchConfig(CODE_SWITCH, "cleanGroupWorkHour", false);
                        // if (config.isEnable()) {
                        for (MaterialGroupDetail detail : detailDtos) {
                            //发送组托详情的,工时
                            this.sendSapWorkingHours(materialGroup.getGroupNo(), materialGroup.getDateProduceBatchNo(), materialGroup.getMaterialCode(), detail.getAufnr(), workCenterId, detail.getWeight());
                        }
                        // }
                    } catch (Exception e) {
                        log.error("上报sap硅料成品工时异常:" + e.getMessage());
                    }
                });
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return materialGroup;
    }

    /**
     * 手动根据材料组ID存储工作小时信息到 SAP。
     * 该方法首先查询指定ID的材料组信息，然后根据该信息上报工作小时到 SAP 系统。
     * 如果查询到的材料组不存在，则抛出业务异常。
     *
     * @param id 材料组的ID，不能为空。
     * @return 返回查询到的材料组信息，如果不存在则抛出异常。
     * @throws BusinessException 如果材料组不存在，则抛出此异常。
     */
    @Transactional(rollbackFor = Exception.class)
    public MaterialGroup groupStoreWorkHourById(Integer id) {
        //1 查询组托数据
        MaterialGroup materialGroup = this.getById(id);
        if (materialGroup == null) {
            throw new BusinessException("原信息不存在");
        }

        String workOrderNo = materialGroup.getAufnr();

        //4.2 获取调拨库位
        HashMap<String, String> wareHouseCodeMapByWorkOrderNo = getWareHouseCodeMapByWorkOrderNo(workOrderNo);
        String workCenterId = wareHouseCodeMapByWorkOrderNo.get("workCenterId");
        try {
            //1.4 这里有字典校验,选择异步上报工时 上报逻辑：根据产出物料编码，产出重量/额定重量*小时
            try {
                //    SwitchConfig config = GlobalSwitch.switchConfig(CODE_SWITCH, "cleanGroupWorkHour", false);
                //    if (config.isEnable()) {
                this.sendSapWorkingHours(materialGroup.getGroupNo(), materialGroup.getDateProduceBatchNo(), materialGroup.getMaterialCode(), workOrderNo, workCenterId, materialGroup.getWeight());
                //   }
            } catch (Exception e) {
                log.error("上报sap硅料成品工时异常:" + e.getMessage());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return materialGroup;
    }

    @Override
    public MaterialGroup getByGroupNo(String groupNo) {
        LambdaQueryWrapper<MaterialGroup> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(MaterialGroup::getGroupNo, groupNo);
        queryWrapper.last("limit 1");
        MaterialGroup materialGroup = getOne(queryWrapper);
        return materialGroup;
    }


    private ResultWms requestOpenGroupToWms(String url, List<OpenToWmsDto> wmsVo, String requestName) {
        String returnStr;
        boolean isErr = false;
        String requestId = UUID.randomUUID().toString();
        try {
            HttpHeaders headers = new HttpHeaders();
            MediaType mediaType = MediaType.parseMediaType("application/json; charset=UTF-8");
            headers.setContentType(mediaType);
            headers.add("Accept", MediaType.APPLICATION_JSON.toString());
            HttpEntity<List<OpenToWmsDto>> httpEntity = new HttpEntity<>(wmsVo, headers);
            ResponseEntity<String> exchange = restTemplate.exchange(url, HttpMethod.POST, httpEntity, String.class);
            returnStr = exchange.getBody();
        } catch (Exception e) {
            e.printStackTrace();
            returnStr = e.getMessage();
            isErr = true;
        }
        if (isErr) {
            return null;
        } else {
            return JSON.parseObject(returnStr, new TypeReference<ResultWms>() {
            });
        }
    }

    /**
     * 记录日志
     *
     * @param description 方法名
     * @param url         三方接口名称
     * @param parameter   入参
     * @param result      返回结果
     */
    public void doRecord(String description, String url, String parameter, Object result) {
        Date startTime = new Date();

        String requestId = UUID.randomUUID().toString();

        String resultJson = result == null ? null : JSON.toJSONString(result);
        //记录请求信息
        SysLogDto sysLog = new SysLogDto();
        sysLog.setName(description);
        long endTime = System.currentTimeMillis();
        StringBuilder content = new StringBuilder();
        if (StringUtils.isNotBlank(url)) {
            content.append("第三方方法名: ").append(url).append("  请求内容: ");
        }
        content.append(parameter);
        sysLog.setContent(content.toString());
        if (resultJson != null && resultJson.length() > 3000) {
            sysLog.setResult("数据过长不予记录");
        } else {
            sysLog.setResult(resultJson);
        }
        sysLog.setSpendTime((int) (endTime - startTime.getTime()));
        sysLog.setCreateTime(startTime);
        sysLog.setRequestId(requestId);
        sysLog.setCreateUserId(BaseContextHandler.getUserID());
        sysLog.setCreateUserName(BaseContextHandler.getUsername());
        String userAgent = "";
        //获取当前请求对象
        sysLog.setStatus(200);
        sysLog.setType("info");
        try {
            rabbitMqService.sendMessage(JSONObject.toJSONString(sysLog), ExchangeEnum.SYS_LOG_EXCHANGE, QueueEnum.SYS_LOG_QUEUE, RoutingKey.SYS_LOG_KEY);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 测试接口
     *
     * @param groupNo
     * @return
     */
    public String fbBatchGroupToWms(String groupNo) {
        LambdaQueryWrapper<MaterialGroup> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(MaterialGroup::getDelFlag, 0);
        queryWrapper.eq(MaterialGroup::getStatus, 0);
        List<MaterialGroup> list1 = materialGroupMapper.selectList(queryWrapper);
        for (MaterialGroup materialGroup : list1
        ) {

            HashMap<String, Object> map = new HashMap<>();
            map.put("group_id", materialGroup.getId());
            List<MaterialGroupDetail> list = materialGroupDetailService.listByMap(map);

            // 入库到wms
            List<RecycledMaterialInDto> recycledMaterialInDtos = buildRecycledMaterialInDtos(list, materialGroup);
            log.info("托号{},状态{}:准备补传数据:{}", materialGroup.getGroupNo(), materialGroup.getStatus(), recycledMaterialInDtos);
            String url = wmsServiceUrl + recycleToWms;
            try {
                // 入库到wms
                ResultWms resultJson = requestRecycleMaterialsToWMS(url, recycledMaterialInDtos, "清洗成品循环料补偿入库到WMS");
                doRecord("清洗成品补偿入库到wms", url, JSON.toJSONString(recycledMaterialInDtos), resultJson);
                if (resultJson != null) {
                    log.info("托号:{}清洗成品补偿入库到wms,返回标识:{},返回信息:{}", materialGroup.getGroupNo(), resultJson.getCode(), resultJson.getMsg());
                    if ("1".equals(resultJson.getCode())) {
                        materialGroup.setUpdateTime(LocalDateTime.now());
                        materialGroup.setStatus(1);
                        baseMapper.updateById(materialGroup);
                    }
                } else {
                    log.info("托号:{}清洗成品补偿入库到wms失败", materialGroup.getGroupNo());
                }
            } catch (Exception e) {
                e.printStackTrace();
                log.error("清洗组托补偿入库到wms: ", e);
            }
        }

        return "补偿清洗数据成功";
    }

    /**
     * 清洗硅料成品入库,主产品报给内部系统,供上报sap使用
     *
     * @param workOrderNo    工单
     * @param materialCode   物料编码
     * @param produceBatchNo 产品批次
     * @param weight         数量(重量)
     * @param unitName       单位(KG,PC)
     * @param warehouseCode  库存地点编码
     */
    @Jinko(author = "yyl", value = "清洗硅料成品入库,主产品报给内部系统,供上报sap使用")
    private void handleReportSapGoodsmvtDto(String workOrderNo, String materialCode, String produceBatchNo, BigDecimal weight, String unitName, String warehouseCode) {
        ReportSapGoodsmvtDto reportSapGoodsmvtDto = new ReportSapGoodsmvtDto();
        List<ReportSapChargItemDto> reportSapChargItemDtos = new ArrayList<>();
        ReportSapChargItemDto reportSapChargItemDto = new ReportSapChargItemDto();
//        reportSapChargItemDto.setAtnam("P01");
//        reportSapChargItemDto.setAtwrt("JK1");
        reportSapChargItemDtos.add(reportSapChargItemDto);
        reportSapGoodsmvtDto.setChargItems(reportSapChargItemDtos);
        reportSapGoodsmvtDto.setAufnr(workOrderNo);
        reportSapGoodsmvtDto.setCharg(produceBatchNo);
        reportSapGoodsmvtDto.setBwart("101");
        reportSapGoodsmvtDto.setWerks("5041");
        reportSapGoodsmvtDto.setMatnr(materialCode);
        reportSapGoodsmvtDto.setMenge(weight == null ? "0" : weight.toString());
        reportSapGoodsmvtDto.setMeins(unitName);
        reportSapGoodsmvtDto.setZfcpFlag("");
        reportSapGoodsmvtDto.setLgort(warehouseCode);
        reportSapGoodsmvtDto.setProcName(RecycleCleanSAPEnum.硅料收货.getDesc());
        ybFeignClients.sendSapGoodsmvt(reportSapGoodsmvtDto);
        //硅料收货记录
        materialStoreSignBoxService.doMesToSapRecord(reportSapGoodsmvtDto, MesToSapTypeEnum.硅料收货.getCode());
    }

    /**
     * 硅料成品发起入库后,直接审核通过并把主产品调拨线边仓和sap,供上报sap使用
     *
     * @param workCenterId      工作中心
     * @param workOrderNo       计划工单
     * @param entity            组托数据
     * @param warehouseCode     原始库位
     * @param nextWarehouseCode 调拨库位
     * @return
     */
    @Jinko(author = "yyl", value = "硅料成品发起入库后,直接审核通过并把主产品调拨线边仓和sap,供上报sap使用")
    private Integer handleWarehouseCodeMoveNextWarehouseCode(String workCenterId, String workOrderNo, MaterialGroup entity, String warehouseCode, String nextWarehouseCode) {
        //1.1封装硅料成品调拨数据
        WarehouseAllotBillDto warehouseAllotBillDto = newAllotBill(entity, warehouseCode, nextWarehouseCode);
        String unitName = warehouseAllotBillDto.getAllotBillDetailDtos().get(0).getUnitName();

        Result storeFeignClientsAllotBill = storeFeignClients.createAllotBill(warehouseAllotBillDto);
        if (storeFeignClientsAllotBill.getCode() != ResultCode.SUCCESS.getCode()) {
            log.error("硅料清洗成品调拨线边仓失败:{}" + storeFeignClientsAllotBill.getMessage());
            throw new BusinessException("硅料清洗成品调拨线边仓失败，" + storeFeignClientsAllotBill.getMessage());
        }
        //1.2调拨sap
        try {
            this.handleWarehouseToNextWarehouseCodeDto(workOrderNo, entity.getMaterialCode(), entity.getGroupNo(), entity.getWeight(), unitName, warehouseCode, nextWarehouseCode);
            //1.3 异步上报工时 上报逻辑：根据产出物料编码，产出重量/额定重量*小时
            ThreadManager.getThreadPollProxy().execute(() -> {
                try {
                    SwitchConfig config = GlobalSwitch.switchConfig(CODE_SWITCH, "cleanGroupWorkHour", false);
                    if (config.isEnable()) {
                        this.sendSapWorkingHours(entity.getGroupNo(),entity.getDateProduceBatchNo(), entity.getMaterialCode(), workOrderNo, workCenterId, entity.getWeight());
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
        }
        return storeFeignClientsAllotBill.getCode();
    }

    /**
     * 硅料发起入库后,自动审核通过并把主产品调拨,供上报sap使用
     *
     * @param workOrderNo       工单
     * @param materialCode      物料编码
     * @param groupNo           托号
     * @param weight            数量(重量)
     * @param unitName          单位(KG,PC)
     * @param warehouseCode     库存地点编码
     * @param nextWarehouseCode 去往库存地点编码
     */
    @Jinko(author = "yyl", value = "硅料发起入库后,自动审核通过并把主产品调拨,供上报sap使用")
    private void handleWarehouseToNextWarehouseCodeDto(String workOrderNo, String materialCode, String groupNo, BigDecimal weight, String unitName, String warehouseCode, String nextWarehouseCode) {
        ReportSapGoodsmvtDto reportSapGoodsmvtDto = new ReportSapGoodsmvtDto();
        List<ReportSapChargItemDto> reportSapChargItemDtos = new ArrayList<>();
        ReportSapChargItemDto reportSapChargItemDto = new ReportSapChargItemDto();
//        reportSapChargItemDto.setAtnam("P01");
//        reportSapChargItemDto.setAtwrt("JK1");
        reportSapChargItemDtos.add(reportSapChargItemDto);
        reportSapGoodsmvtDto.setChargItems(reportSapChargItemDtos);
        reportSapGoodsmvtDto.setAufnr(null);
        reportSapGoodsmvtDto.setCharg(groupNo);
        reportSapGoodsmvtDto.setBwart("311");
        reportSapGoodsmvtDto.setWerks("5041");
        reportSapGoodsmvtDto.setUmcha(groupNo);
        reportSapGoodsmvtDto.setUmwrk("5041");
        reportSapGoodsmvtDto.setMatnr(materialCode);
        reportSapGoodsmvtDto.setMenge(weight == null ? "0" : weight.toString());
        reportSapGoodsmvtDto.setMeins(unitName);
        reportSapGoodsmvtDto.setZfcpFlag("");
        reportSapGoodsmvtDto.setLgort(warehouseCode);
        reportSapGoodsmvtDto.setUmlgo(nextWarehouseCode);
        reportSapGoodsmvtDto.setProcName("硅料成品调拨");
        ybFeignClients.sendSapGoodsmvt(reportSapGoodsmvtDto);
    }

    /**
     * 获取硅料成品调拨库位
     * 成品仓 ->下个工序的材料仓
     *
     * @param workOrderNo 计划工单
     * @return
     */
    @Jinko(author = "yyl", value = "获取硅料成品调拨库位")
    private HashMap<String, String> getWareHouseCodeMapByWorkOrderNo(String workOrderNo) {
        //通过计划工单中的片区(成本中心)field4找到字典中的成品仓
        Result<ProductionOrderDto> productionOrder = ybFeignClients.getListByAufnr(workOrderNo);
        String field4 = "";
        String warehouseCode = "";
        String nextWarehouseCode = "";
        if (productionOrder.getCode() != ResultCode.SUCCESS.getCode() || null == productionOrder.getData()) {
            throw new RuntimeException("工单查询异常：" + productionOrder.getMessage());
        }
        ProductionOrderDto orderData = productionOrder.getData();
        if (null != orderData) {
            field4 = orderData.getField4();
        }
        if (null != field4) {
            Result<DictionaryItemDto> dictionaryItemDtoResult = ybFeignClients.findNameByThemeValueAndItemValue("ByProductWarehouseLocation", field4);
            if (null != dictionaryItemDtoResult && null != dictionaryItemDtoResult.getData() && ResultCode.SUCCESS.getCode() == dictionaryItemDtoResult.getCode()) {
                //成品仓
                warehouseCode = dictionaryItemDtoResult.getData().getExtendValue1();
                //下个工序的材料仓
                nextWarehouseCode = dictionaryItemDtoResult.getData().getExtendValue2();
            } else {
                throw new BusinessException(dictionaryItemDtoResult.getMessage() + "详情: 关键字[ByProductWarehouseLocation]中的[工作中心编码]: " + field4 + " 未配置");
            }
        }
        HashMap<String, String> stringStringHashMap = new HashMap<>();
        stringStringHashMap.put("workCenterId", field4);
        stringStringHashMap.put("warehouseCode", warehouseCode);
        stringStringHashMap.put("nextWarehouseCode", nextWarehouseCode);
        return stringStringHashMap;
    }

    /**
     * 硅料发起入库后,自动调拨成功后,上报sap硅料成品工时
     * 产出重量/额定重量*小时
     *
     * @param groupNo 托号
     * @param dateProduceBatchNo 日批次号
     * @param materialCode       物料编号
     * @param workOrderNo        工单
     * @param workCenterId       工作中心编码
     * @param weight             产出重量
     */
    @Jinko(author = "yyl", value = "硅料发起入库后,自动调拨成功后,上报sap硅料成品工时")
    private void sendSapWorkingHours(String groupNo, String dateProduceBatchNo, String materialCode, String workOrderNo, String workCenterId, BigDecimal weight) {

        Result<DictionaryItemDto> dictionaryItemDtoResult = ybFeignClients.findByThemeCodeAndItemName("CleanGroupWorkHours", materialCode);
        if (null != dictionaryItemDtoResult && null != dictionaryItemDtoResult.getData() && ResultCode.SUCCESS.getCode() == dictionaryItemDtoResult.getCode()) {
            String hour = dictionaryItemDtoResult.getData().getExtendValue();
            String rateWeight = dictionaryItemDtoResult.getData().getValue();
            if (!NumberUtil.isNumeric(hour) || !NumberUtil.isNumeric(rateWeight)) {
                log.info("字典配置中,小时和额定重量必须是数字!");
                throw new BusinessException("字典配置中,小时和额定重量必须是数字!");
            } else {
                BigDecimal cnfTime = weight.divide(new BigDecimal(rateWeight)).multiply(new BigDecimal(hour)).setScale(2, BigDecimal.ROUND_HALF_UP);
                log.info("硅料成品上报产出重量:{},额定重量:{},字典配置的工时:{},保留两位小时的计算后上报工时{}", weight, rateWeight, hour, cnfTime);
                ReportSapWorkinghoursDto reportSapWorkinghoursDto = new ReportSapWorkinghoursDto();
                reportSapWorkinghoursDto.setProductionOrderId(workOrderNo);
                reportSapWorkinghoursDto.setCrystalNo(groupNo);
                reportSapWorkinghoursDto.setPlant("5041");
                reportSapWorkinghoursDto.setWorkcenterId(workCenterId);
                reportSapWorkinghoursDto.setCnfTime(String.valueOf(cnfTime));
                reportSapWorkinghoursDto.setCnfTimeUom("H");
                reportSapWorkinghoursDto.setStatus(false);
                reportSapWorkinghoursDto.setCnfDate(new Date());
                reportSapWorkinghoursDto.setProcName("硅料工时");
                reportSapWorkinghoursDto.setRemark(dateProduceBatchNo);
                ybFeignClients.sendSapWorkinghours(reportSapWorkinghoursDto);
            }
        } else {
            log.info("字典关键字[CleanGroupWorkHours]中:物料编码:" + materialCode + "未维护,不能成功上报工时!");
            throw new BusinessException("字典CleanGroupWorkHours中:物料编码:" + materialCode + "未维护,不能成功上报工时!");
        }
    }

    /**
     * 组托发起入库到线边仓(废弃)
     *
     * @param workOrderNo   工单
     * @param materialGroup 数据
     * @param type          入库类型
     * @param warehouseCode 成品入库位
     * @return 入库单号
     *
     */
    private String startSurplusWarehousing(String workOrderNo, MaterialGroup materialGroup, String type, String warehouseCode) {
        try {
            WarehouseInputBillBo warehouseInputBillBo = newInputBill(materialGroup, type, warehouseCode);
            Result<String> result = storeFeignClients.inputApply(warehouseInputBillBo);
            Integer code = result.getCode();
            String data = result.getData();
            if (code != 200 || org.apache.commons.lang3.StringUtils.isBlank(data)) {
                throw new BusinessException("清洗硅料成品入库，状态码：" + code + "，反馈信息：" + result.getMessage());
            } else {
                //发起入库,主产品产出报工SAP
                String finalWorkOrderNo = workOrderNo;
                String finalWarehouseCode = warehouseCode;
                try {
                    this.handleReportSapGoodsmvtDto(finalWorkOrderNo, materialGroup.getMaterialCode(), materialGroup.getGroupNo(), materialGroup.getWeight(), warehouseInputBillBo.getInputBillDetails().get(0).getUnitName(), finalWarehouseCode);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            return data;
        } catch (Exception e) {
            throw new BusinessException("清洗硅料成品入库异常，" + e.getMessage());
        }
    }


    /**
     * 清洗硅料成品入库数据封装
     *
     * @param entity        组托数据
     * @param type          入库类型
     * @param warehouseCode 库位
     * @return
     */
    private WarehouseInputBillBo newInputBill(MaterialGroup entity, String type, String warehouseCode) {
        WarehouseInputBillBo inputBill = new WarehouseInputBillBo();
        inputBill.setType(type);
        //这里备注添加入库类型,相当于描述
        inputBill.setRemark(type);
        inputBill.setAutoAudit(true);
        inputBill.setAutomaticOut(false);
        inputBill.setWarehouseCode(warehouseCode);
        //入库物料明细
        List<WarehouseInputBillDetailBo> inputBillDetails = new ArrayList<>();
        WarehouseInputBillDetailBo dto = new WarehouseInputBillDetailBo();

        //组装物料明细数据
        //暂时使用固定编码,待后期业务完善
        dto.setSupplier("太原晶科");
        dto.setSupplierCode("TYJK");
        dto.setBatchNo(entity.getGroupNo());
        dto.setCrystalNo(entity.getDateProduceBatchNo());
        dto.setWarehouseCode(warehouseCode);
        dto.setCount(entity.getWeight());
        //字段需要与物料主数据对齐
        Result<MaterialModelDto> result = ybFeignClients.getMaterialByCode(entity.getMaterialCode());
        MaterialModelDto materialModel = result.getData();
        if (materialModel == null) {
            throw new BusinessException("【" + entity.getMaterialCode() + "】物料主数据不存在，请检查数据！");
        }

        dto.setMaterialModelId(Long.valueOf(materialModel.getId()));
        dto.setMaterialModelCode(materialModel.getCode());
        dto.setMaterialModelName(materialModel.getName());
        dto.setMaterialTypeName(materialModel.getMaterialTypeDto().getName());
        dto.setMaterialTh(materialModel.getNumberOfDrawings());
        dto.setUnitName(materialModel.getUnitDto().getName());
        dto.setSpecification(materialModel.getSpecification());
        //此工单号对于线边仓无用,不应该传
        dto.setWorkOrderNo(entity.getAufnr());
        dto.setMaterialGear(entity.getMaterialGear());
        dto.setMaterialLevel(entity.getMaterialLevel());
        dto.setSuppliesGrain(entity.getSuppliesGrain());
        inputBillDetails.add(dto);
        inputBill.setAutomaticOut(false);
        inputBill.setInputBillDetails(inputBillDetails);
        return inputBill;
    }

    /**
     * 清洗硅料成品调拨数据封装
     *
     * @param entity            组托数据
     * @param warehouseCode     原始库位
     * @param nextWarehouseCode 调拨库位
     * @return
     */
    private WarehouseAllotBillDto newAllotBill(MaterialGroup entity, String warehouseCode, String nextWarehouseCode) {
        Result<MaterialModelDto> result = ybFeignClients.getMaterialByCode(entity.getMaterialCode());
        MaterialModelDto materialModel = result.getData();
        if (materialModel == null) {
            throw new BusinessException("【" + entity.getMaterialCode() + "】物料主数据不存在，请检查数据！");
        }
        //封装线边仓调拨数据
        WarehouseAllotBillDto warehouseAllotBillDto = new WarehouseAllotBillDto();
        List<WarehouseAllotBillDetailDto> allotBillDetailDtos = new ArrayList<>();
        WarehouseAllotBillDetailDto warehouseAllotBillDetailDto = new WarehouseAllotBillDetailDto();
        warehouseAllotBillDetailDto.setSupplier("太原晶科");
//        warehouseAllotBillDetailDto.setSupplierCode("TYJK");
        warehouseAllotBillDetailDto.setWarehouseId(null);
        warehouseAllotBillDetailDto.setBatchNo(entity.getGroupNo());
        warehouseAllotBillDetailDto.setCrystalNo(entity.getDateProduceBatchNo());
        warehouseAllotBillDetailDto.setCount(entity.getWeight());
        // warehouseAllotBillDetailDto.setMaterialModelCode(entity.getMaterialCode());
        // warehouseAllotBillDetailDto.setMaterialModelName(entity.getMaterialName());
        warehouseAllotBillDetailDto.setMaterialModelId(Long.valueOf(materialModel.getId()));
        warehouseAllotBillDetailDto.setMaterialModelCode(materialModel.getCode());
        warehouseAllotBillDetailDto.setMaterialModelName(materialModel.getName());
        if (null != materialModel.getMaterialTypeDto()) {
            warehouseAllotBillDetailDto.setMaterialTypeName(materialModel.getMaterialTypeDto().getName());
        }
        if (null != materialModel.getUnitDto()) {
            warehouseAllotBillDetailDto.setUnitName(materialModel.getUnitDto().getName());
        }
        warehouseAllotBillDetailDto.setSupplier(materialModel.getSupplier());
        warehouseAllotBillDetailDto.setSpecification(materialModel.getSpecification());
        warehouseAllotBillDetailDto.setMaterialTh(materialModel.getNumberOfDrawings());
        warehouseAllotBillDetailDto.setWarehouseCode(warehouseCode);
        Result<WarehouseModelDto> codeResult = storeFeignClients.getCode(warehouseCode);
        if (codeResult.getCode() == ResultCode.SUCCESS.getCode()) {
            warehouseAllotBillDetailDto.setWarehouseId(codeResult.getData().getId());
        }
        warehouseAllotBillDetailDto.setAllotWarehouseCode(nextWarehouseCode);
        allotBillDetailDtos.add(warehouseAllotBillDetailDto);
        warehouseAllotBillDto.setAllotBillDetailDtos(allotBillDetailDtos);
        warehouseAllotBillDto.setCreateUserName(BaseContextHandler.getUsername());
        warehouseAllotBillDto.setCreateUserId(BaseContextHandler.getUserID());
        warehouseAllotBillDto.setAutoAudit(false);
        warehouseAllotBillDto.setAllotType(RecycleNoAutoAllotWarehouseEnum.硅料成品调拨.getDesc());
        return warehouseAllotBillDto;
    }

    /**
     * 生成硅料组托日批次号
     *
     * @param workOrderNo 工单号
     * @return
     */
    private String getMaterialGroupDateBatchNo(String workOrderNo) {
        String kostl = "";
        String workShopValue = "";
        String directionalSign = "";
        String directionalSignValue = "";
        Result<Map<String, String>> result = ybFeignClients.getWorkShopValueAndDirectionalSign(workOrderNo);
        if (null != result && null != result.getData() && ResultCode.SUCCESS.getCode() == result.getCode()) {
            kostl = result.getData().get("kostl");
            workShopValue = result.getData().get("workShopValue");
            directionalSign = result.getData().get("directionalSign");
            directionalSignValue = result.getData().get("directionalSignValue");
        } else {
            throw new BusinessException("调用[根据计划工单获取车间value和定向标识]接口异常:" + result.getMessage());
        }
        if (StringUtils.isBlank(kostl) || StringUtils.isBlank(workShopValue) || StringUtils.isBlank(directionalSign) || StringUtils.isBlank(directionalSignValue)) {
            log.info("生成硅料组托日批次号异常:成本中心{},车间Value{},定向标识{},定向标识转换值:{}", kostl, workShopValue, directionalSign, directionalSignValue);
            throw new BusinessException("生成硅料组托日批次号异常:" + result.getMessage());
        }
        String dataBatchNoGenerator = materialGroupDateBatchNoGenerator.dataBatchNoGenerator(directionalSign, directionalSignValue, workShopValue, kostl);
        return dataBatchNoGenerator;
    }

    /**
     * 检查订单信息，确保所有订单满足特定条件（如属于同一片区和具有相同的定向标识）。
     *
     * @param aufnrs 一个包含订单号的集合。
     */
    public void checkOrderInfo(Set<String> aufnrs) {
        List<String> list = new ArrayList<String>(aufnrs);
        Result<List<ProductionOrderDto>> listResult = ybFeignClients.getListByAufnrs(list);
        if (listResult.getCode() != ResultCode.SUCCESS.getCode() || null == listResult.getData()) {
            throw new RuntimeException("工单查询异常：" + listResult.getMessage());
        }
        List<ProductionOrderDto> data = listResult.getData();

        if (CollectionUtil.isEmpty(data)) {
            throw new RuntimeException("工单查询异常!");
        } else {
            // 遍历订单数据，如果定向标识为空，则设置为"非定向"。
            data.forEach(entity -> {
                if (StringUtils.isBlank(entity.getZorientationind())) {
                    entity.setZorientationind("非定向");
                }
            });
            Set<String> field4Set = data.stream().map(ProductionOrderDto::getField4).collect(Collectors.toSet());
            Set<String> directionalSignSet = data.stream().map(ProductionOrderDto::getZorientationind).collect(Collectors.toSet());
            // 如果field4或定向标识的集合大小不为1，意味着不满足条件，抛出异常。
            if (field4Set.size() != 1) {
                throw new RuntimeException("不是相同片区不能组托");
            }
            if (directionalSignSet.size() != 1) {
                throw new RuntimeException("不是相同定向标识不能组托");
            }
        }
    }
}
