package com.cf.imes.module.executor.service.optimizeplan;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.FieldValue;
import co.elastic.clients.elasticsearch.core.*;
import co.elastic.clients.elasticsearch.core.search.Hit;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.cf.imes.framework.common.enums.PlanStatusEnum;
import com.cf.imes.framework.common.enums.RemainTypeEnum;
import com.cf.imes.framework.common.exception.ServiceException;
import com.cf.imes.framework.common.util.json.JsonUtils;
import com.cf.imes.framework.common.util.object.BeanUtils;
import com.cf.imes.framework.es.core.service.ESDocumentService;
import com.cf.imes.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.cf.imes.framework.mybatis.core.query.MPJLambdaWrapperX;
import com.cf.imes.framework.mybatis.core.query.QueryWrapperX;
import com.cf.imes.framework.organ.core.context.OrganContextHolder;
import com.cf.imes.framework.security.core.util.SecurityFrameworkUtils;
import com.cf.imes.module.executor.controller.admin.plan.bo.OrderSource;
import com.cf.imes.module.executor.controller.admin.plan.dto.Material;
import com.cf.imes.module.executor.controller.admin.plan.saveOptimize.OptimizeRemainPlate;
import com.cf.imes.module.executor.controller.admin.plan.saveOptimize.SavePlanPlateResultReqVO;
import com.cf.imes.module.executor.controller.admin.plan.vo.*;
import com.cf.imes.module.executor.controller.admin.plate.vo.PlateDetailVO;
import com.cf.imes.module.executor.dal.dataobject.goods.GoodsDO;
import com.cf.imes.module.executor.dal.dataobject.goods.OptimizeBoardModelDO;
import com.cf.imes.module.executor.dal.dataobject.order.OrderDO;
import com.cf.imes.module.executor.dal.dataobject.orderItem.OrderItemDO;
import com.cf.imes.module.executor.dal.dataobject.ordermodel.OrderModelDO;
import com.cf.imes.module.executor.dal.dataobject.plan.PlanDO;
import com.cf.imes.module.executor.dal.dataobject.planitem.PlanItemDO;
import com.cf.imes.module.executor.dal.dataobject.plate.PlateDO;
import com.cf.imes.module.executor.dal.dataobject.remainplaten.OutlineDTO;
import com.cf.imes.module.executor.dal.dataobject.remainplaten.PointDTO;
import com.cf.imes.module.executor.dal.dataobject.remainplaten.RemainPlateDO;
import com.cf.imes.module.executor.dal.mysql.goods.GoodsMapper;
import com.cf.imes.module.executor.dal.mysql.order.OrderMapper;
import com.cf.imes.module.executor.dal.mysql.orderItem.OrderItemMapper;
import com.cf.imes.module.executor.dal.mysql.plan.PlanMapper;
import com.cf.imes.module.executor.dal.mysql.plate.PlateMapper;
import com.cf.imes.module.executor.dal.mysql.remainplaten.RemainPlateMapper;
import com.cf.imes.module.executor.enums.ErrorCodeConstants;
import com.cf.imes.module.executor.util.RandomUtils;
import com.cf.imes.module.executor.util.RectangleChecker;
import com.cf.imes.module.infra.api.file.FileApi;
import com.cf.imes.module.infra.api.file.dto.FileCreateReqDTO;
import com.cf.imes.module.system.api.dataSource.DataSourceApi;
import com.cf.imes.module.system.api.machine.MachineApi;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

import static com.cf.imes.framework.common.exception.enums.GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR;
import static com.cf.imes.framework.common.exception.enums.GlobalErrorCodeConstants.UNKNOWN;
import static com.cf.imes.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.cf.imes.framework.organ.core.context.OrganContextHolder.getOrganId;
import static com.cf.imes.framework.security.core.util.SecurityFrameworkUtils.getUserOrganId;
import static com.cf.imes.module.executor.enums.ErrorCodeConstants.ORDER_NOT_EXISTS;
import static com.cf.imes.module.executor.enums.ErrorCodeConstants.PLAN_NOT_EXISTS;
import org.springframework.transaction.annotation.Transactional;

/**
 * @author there
 */
@Service
@Slf4j
public class OptimizePlanServiceImpl implements OptimizePlanService {

    @Resource
    private PlanMapper planMapper;

    @Resource
    private RemainPlateMapper remainPlateMapper;

    @Resource
    private FileApi fileApi;

    @Resource
    private PlateMapper plateMapper;

    @Resource
    private GoodsMapper goodsMapper;

    @Resource
    private DataSourceApi dataSourceApi;

    @Resource
    private OrderMapper orderMapper;

    @Resource
    private ElasticsearchClient elasticsearchClient;

    private Snowflake snowflake = IdUtil.getSnowflake();

    @Resource
    private MachineApi machineApi;

    @Resource
    private ESDocumentService esDocumentService;

    @Resource
    private OrderItemMapper orderItemMapper;

    @Override
    public List<PlateOptimize> getPlateListByPlanId(Long planId) {
        return planMapper.selectPlateListByPlanId(planId,getUserOrganId());
    }

    @Override
    public Boolean addRemain(AddRemainReqVO vo) {
        PlanDO planDO = planMapper.selectById(vo.getPlanId());
        if (Objects.isNull(planDO)) {
            throw exception(PLAN_NOT_EXISTS);
        }
        RemainPlateDO remainPlateDO = RemainPlateDO.builder()
                .width(vo.getWidth())
                .length(vo.getLength())
                .planId(vo.getPlanId())
                .initPlanId(vo.getPlanId())
                .goodsId(vo.getGoodsId())
                .name(vo.getGoodsName())
                .material(vo.getMaterial())
                .color(vo.getColor())
                .count(vo.getCount())
                .build();
        remainPlateMapper.insert(remainPlateDO);
        return Boolean.TRUE;
    }

    @Override
    public Boolean savePlanPlateResult(SavePlanPlateResult result) {
        PlanDO planDO = planMapper.selectById(result.getPlanId());
        if (Objects.isNull(planDO)) {
            throw exception(PLAN_NOT_EXISTS);
        }
        try {
            String placeDateFileUrlSource = planDO.getPlaceDateFileUrl();
            String placeOrderFileUrlSource = planDO.getPlaceOrderFileUrl();
            if (StrUtil.isNotBlank(placeDateFileUrlSource)) {
                fileApi.deleteFileByPath(placeDateFileUrlSource).checkError();
            }
            if (StrUtil.isNotBlank(placeOrderFileUrlSource)) {
                fileApi.deleteFileByPath(placeOrderFileUrlSource).checkError();
            }
            FileCreateReqDTO fileCreateReqDTO = new FileCreateReqDTO();

            fileCreateReqDTO.setContent(result.getPlaceData().getBytes());
            fileCreateReqDTO.setName(result.getPlaceData().getOriginalFilename());
            String placeDateFileUrl = fileApi.createFile(fileCreateReqDTO).getCheckedData();

            fileCreateReqDTO.setContent(result.getPlaceOrder().getBytes());
            fileCreateReqDTO.setName(result.getPlaceOrder().getOriginalFilename());
            String placeOrderFileUrl = fileApi.createFile(fileCreateReqDTO).getCheckedData();

            PlanDO updateDO = PlanDO.builder()
                    .id(result.getPlanId())
                    .placeDateFileUrl(placeOrderFileUrl)
                    .placeOrderFileUrl(placeDateFileUrl)
                    .build();
            planMapper.updateById(updateDO);

        } catch (IOException e) {
            throw exception(UNKNOWN);
        }
        return Boolean.TRUE;
    }

    @Override
    public Boolean commit(Long planId) {
        PlanDO planDO = planMapper.selectById(planId);
        if (Objects.isNull(planDO)) {
            throw exception(PLAN_NOT_EXISTS);
        }
        planMapper.updateById(PlanDO.builder()
                .id(planId)
                .status(PlanStatusEnum.DURINGCUTTING.getStatus())
                .build());
        return Boolean.TRUE;
    }

    @Override
    public OptimizeParamResVO getOptimizeParam(Long planId) {
        //小板列表
        List<PlateParam> plates = plateMapper.selectPlateList(planId);
        //余料规格列表
        List<RemainPlateDO> remainPlateDOS = remainPlateMapper.selectList(new LambdaQueryWrapperX<RemainPlateDO>().eq(RemainPlateDO::getPlanId, planId));
        //余料板规格
        List<OptimizeParamResVO.RawSize> rawSizes = new ArrayList<>();
        List<Integer> remainPlateCount = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(remainPlateDOS)) {
            Map<String, List<OutlineDTO>> map = remainPlateDOS.stream()
                    .map(e -> OutlineDTO.builder()
                            .list(JsonUtils.parseArray(e.getOutLineJson(), PointDTO.class))
                            .length(e.getLength())
                            .width(e.getWidth())
                            .build()
                    )
                    .filter(e -> e.getList().size() == 4 && RectangleChecker.checkRectangle(e.getList()))
                    .collect(Collectors.groupingBy(e -> e.getLength() + "," + e.getWidth()));
            map.entrySet().forEach(e -> {
                String[] split = e.getKey().split(",");
                BigDecimal length = new BigDecimal(split[0]);
                BigDecimal width = new BigDecimal(split[1]);
                List<OutlineDTO> outlines = e.getValue();
                rawSizes.add(OptimizeParamResVO.RawSize.builder()
                        .length(length)
                        .width(width)
                        .x(new BigDecimal(outlines.get(0).getList().get(0).getX()))
                        .y(new BigDecimal(outlines.get(0).getList().get(0).getY()))
                        .build());
                remainPlateCount.add(outlines.size());
            });
        }

        //大板规格
        /**
         * select * from order_goods a
         * left join order_plate b on a.goods_id = b.goods_id
         * left join order_item c on b.id = c.plate_id
         * left join order_plan_item d on c.id = d.item_id
         * where d.plan_id = 1769638780093857792
         */
        MPJLambdaWrapper<GoodsDO> wrapper = new MPJLambdaWrapperX<GoodsDO>()
                .distinct()
                .select(GoodsDO::getId, GoodsDO::getWidth, GoodsDO::getHeight)
                .leftJoin(PlateDO.class, PlateDO::getGoodsId, GoodsDO::getGoodsId)
                .leftJoin(OrderItemDO.class, OrderItemDO::getPlanId, PlateDO::getPlateNo)
                .leftJoin(PlanItemDO.class, PlanItemDO::getItemId, OrderItemDO::getId)
                .eq(PlanItemDO::getPlanId, planId);

        GoodsDO goodsDO = goodsMapper.selectJoinOne(GoodsDO.class, wrapper);
        rawSizes.add(OptimizeParamResVO.RawSize.builder()
                .width(goodsDO.getWidth())
                .length(goodsDO.getHeight())
                .x(new BigDecimal("0"))
                .y(new BigDecimal("0"))
                .build());
        return OptimizeParamResVO.builder()
                .plates(plates)
                .rawSizes(rawSizes)
                .remainPlateCount(remainPlateCount)
                .build();

    }

    @Override
    public OptimizeParamRespVO getOptimizePlanParam(Long planId) {

        OptimizeParamRespVO optimizePlanParam = planMapper.getOptimizePlanParam(planId);

        if (Objects.isNull(optimizePlanParam)) {
            return RandomUtils.randomPojo(OptimizeParamRespVO.class);
        }
        //暂时先mock
        ArrayList<Material> list = new ArrayList<>();
        list.add(RandomUtils.randomPojo(Material.class));
        list.add(RandomUtils.randomPojo(Material.class));
        ArrayList<PlateDetailVO> plateDetailVOS = new ArrayList<>();
        plateDetailVOS.add(RandomUtils.randomPojo(PlateDetailVO.class));
        plateDetailVOS.add(RandomUtils.randomPojo(PlateDetailVO.class));
        plateDetailVOS.add(RandomUtils.randomPojo(PlateDetailVO.class));
        if (CollectionUtil.isNotEmpty(optimizePlanParam.getPlateList())) {
            optimizePlanParam.getPlateList().get(0).setPlateDetailList(plateDetailVOS);
        }
        optimizePlanParam.setMaterialList(list);
        return optimizePlanParam;
    }

    @Override
    public Map<String, Object> getLabelDataSourceValue(GetSourceDataReq req) {
        String sqlStr = dataSourceApi.getSqlById(req.getDataSourceId()).getCheckedData();
        String sql = sqlStr.replace("#{orderId}", req.getOrderId() + "");
        if (!Objects.isNull(req.getPackageId())) {
            sql = sql.replace("#{packageId}", req.getPackageId() + "");
        }
        if (!Objects.isNull(req.getPlanId())) {
            sql = sql.replace("#{planId}", req.getPlanId() + "");
        }
        return orderMapper.selectDynamicSqlString(sql);
    }

    @Override
    public OrderSource getOrderSource(Long orderId, Long planId, Long machineId) {
        if (!Objects.isNull(orderId) && !Objects.isNull(planId)) {
            throw exception(154112, "生产单id与排单id只能传一个");
        }
        OrderSource orderSource;
        if (ObjectUtil.isNotNull(orderId)) {
            orderSource = getOrderSourceByOrderId(orderId);
            orderSource.setMachineDTO(machineApi.getMachineDetail(machineId).getCheckedData());
            return orderSource;
        }
        if (ObjectUtil.isNotNull(planId)) {
            orderSource = getOrderSourceByPlanId(planId);
            orderSource.setMachineDTO(machineApi.getMachineDetail(machineId).getCheckedData());
            return orderSource;
        }
        throw exception(ErrorCodeConstants.SOURCE_NOT_EXITS_ERROR);

    }

    @Override
    public OrderSource getOrderSourceByOrderId(Long orderId) {
        OrderDO orderDO = orderMapper.selectById(orderId);
        if (Objects.isNull(orderDO)) {
            throw exception(ORDER_NOT_EXISTS);
        }
        OrderSource orderSource = new OrderSource();


//        List<GoodsDO> goodsDOS = goodsMapper.selectList(new LambdaQueryWrapperX<GoodsDO>()
//                .eq(GoodsDO::getOrderId, orderId)
//        );

        List<GoodsReqVO> goodsDOS = goodsMapper.selectGoodsList(orderId,getUserOrganId());

//        PlanDO planDO = planMapper.selectByOrderId(orderId);

        List<PlateDetialRespVO> plateDOS = plateMapper.selectPlateDetialList(orderId,getUserOrganId());


        // 造型数据的尺寸长度
        Integer orderModelSize = plateDOS.size();


        List<OrderModelDO> orderModelDOS = buildRespByOrderId(orderId, ORDER_PLATE_MODEL, orderModelSize);


        // 大板数据的尺寸长度
        Integer boardSize = goodsDOS.stream().map(m -> m.getId()).collect(Collectors.toList()).size();


        List<OptimizeBoardModelDO> optimizeBoardModelDOS = buildBoardByOrderId(orderId, ORDER_REMAIN_PLATE_MODEL, boardSize);



//        orderSource.setPlan(planDO);
        orderSource.setOrderList(Collections.singletonList(orderDO));
        orderSource.setPlateList(plateDOS);
        orderSource.setGoodsList(goodsDOS);
        orderSource.setPlateModels(orderModelDOS);
        orderSource.setOptimizeBoardModelDOS(optimizeBoardModelDOS);
        return orderSource;
    }



    @Override
    public OrderSource getOrderSourceByPlanId(Long planId) {
        PlanDO planDO = planMapper.selectById(planId);
        if (Objects.isNull(planDO)) {
            throw exception(PLAN_NOT_EXISTS);
        }
        MPJLambdaWrapper<OrderItemDO> wrapper = new MPJLambdaWrapperX<OrderItemDO>()
                .select(OrderItemDO::getOrderId)
                .rightJoin(PlanItemDO.class, PlanItemDO::getItemId, OrderItemDO::getId)
                .eq(PlanItemDO::getPlanId, planId)
                .isNotNull(OrderItemDO::getId);
        List<OrderItemDO> orderItemDOS = orderItemMapper.selectJoinList(OrderItemDO.class, wrapper);
        List<Long> orderIds = orderItemDOS.stream().map(OrderItemDO::getOrderId).collect(Collectors.toList());


        List<OrderDO> orderDOS = orderMapper.selectBatchIds(orderIds);

//        List<GoodsDO> goodsDOS = goodsMapper.selectList(new LambdaQueryWrapperX<GoodsDO>().in(GoodsDO::getOrderId, orderIds));

        List<GoodsReqVO> goodsDOS = goodsMapper.selectGoodsListByOrderIds(orderIds,getUserOrganId());

        List<PlateDetialRespVO> plateDOS = plateMapper.selectPlateDetialListByIds(orderIds,getUserOrganId());

        // 造型数据的尺寸长度
        Integer orderModelSize = plateDOS.size();

        List<OrderModelDO> orderModelDOS = buildRespByOrderIds(orderIds, ORDER_PLATE_MODEL, orderModelSize);


        // 大板数据的尺寸长度
        Integer boardSize = goodsDOS.stream().map(m -> m.getId()).collect(Collectors.toList()).size();

        List<OptimizeBoardModelDO> optimizeBoardModelDOS = buildBoardByOrderIds(orderIds, ORDER_REMAIN_PLATE_MODEL, boardSize);


        List<RemainPlateDO> remainPlateDOS = remainPlateMapper.selectByPlanId(planId,getUserOrganId());

        List<OptimizeRemainPlate> optimizeRemainPlates = BeanUtils.toBean(remainPlateDOS, OptimizeRemainPlate.class);


        return OrderSource.builder()
                .orderList(orderDOS)
                .plan(planDO)
                .goodsList(goodsDOS)
                .plateList(plateDOS)
                .plateModels(orderModelDOS)
                .optimizeBoardModelDOS(optimizeBoardModelDOS)
                .optimizeRemainPlates(optimizeRemainPlates)
                .build();
    }


    // 保存优化后的数据
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveOptimizationResults(SavePlanPlateResultReqVO req) {


//        // 判断当前的大板数据是否已经优化，有优化，更新，没有，新增
//        if (req.getOptimizeBoardModelDOS().get(0).getIsOptimized().equals(true)) {
//            try {
//                // 更新大板的优化数据
//                BulkResponse bulkResponse = esDocumentService.bulkUpdate(ORDER_REMAIN_PLATE_MODEL, req.getOptimizeBoardModelDOS());
//                boolean errors = bulkResponse.errors();
//                if (errors) {
//                    log.error(bulkResponse.items().toString());
//                    throw new ServiceException(500, "未知异常");
//                }
//            } catch (Exception e) {
//                e.printStackTrace();
//                log.error(e.getMessage());
//                throw new RuntimeException(e);
//            }
//        }
//        else {

            // 保存优化后的大板数据
            batchSaveBoardModel(req.getOptimizeBoardModelDOS());

//        }


        if (CollectionUtil.isNotEmpty(req.getOptimizeRemainPlates())) {
            ArrayList<RemainPlateDO> remainPlateDOS = new ArrayList();

            for (OptimizeRemainPlate optimizeRemainPlate : req.getOptimizeRemainPlates()) {
                remainPlateDOS.add(RemainPlateDO.builder()
                        .id(optimizeRemainPlate.getId())
                        .planId(optimizeRemainPlate.getPlanId())
                        .initPlanId(optimizeRemainPlate.getInitPlanId())
                        .organId(getUserOrganId())
                        .status(optimizeRemainPlate.getStatus())
                        .isCutting(true)
                        .useType(RemainTypeEnum.PATCHINGPLATE.getType())
                        .goodsId(optimizeRemainPlate.getGoodsId().toString())
                        .name(optimizeRemainPlate.getGoodsName())
                        .material(optimizeRemainPlate.getMaterial())
                        .color(optimizeRemainPlate.getColor())
                        .width(optimizeRemainPlate.getWidth())
                        .length(optimizeRemainPlate.getLength())
                        .thickness(optimizeRemainPlate.getThickness())
                        .brand(optimizeRemainPlate.getBrand())
                        .placeStyle(optimizeRemainPlate.getPlaceStyle())
                        .store("")
                        .count(optimizeRemainPlate.getCount())
                        .remark(optimizeRemainPlate.getRemark())
                        .outLineJson(optimizeRemainPlate.getOutLineJson())
                        .build());
            }

            remainPlateMapper.updateBatch(remainPlateDOS);
//            for (RemainPlateDO remainPlateDO : remainPlateDOS) {
//
////                if(remainPlateDO.getId() != null){
////                    remainPlateMapper.insert(remainPlateDO);
////
////                }
//
//                remainPlateMapper.updateById(remainPlateDO);
//
//            }


        }


        // 有新增小板，往数据库新增小板
        if (CollectionUtil.isNotEmpty(req.getAddPlateIds())) {

            List<PlateDetialRespVO> addPlates = req.getPlateList().stream()
                    .filter(m -> req.getAddPlateIds().contains(m.getId()))
                    .map(m -> m.setId(null))
                    .collect(Collectors.toList());


//            List<Long> collect = req.getPlateList().stream().map(m -> m.getId()).collect(Collectors.toList());
//            List<Long> addPlateIds = req.getAddPlateIds().stream().filter(m -> !collect.contains(m)).collect(Collectors.toList());
//            List<PlateDetialRespVO> addPlates = req.getPlateList().stream()
//                    .filter(m -> addPlateIds.contains(m.getId()))
//                    .map(m->m.setId(null))
//                    .collect(Collectors.toList());
            // 批量新增小板信息
            List<PlateDO> plateDOS = BeanUtils.toBean(addPlates, PlateDO.class);

            plateMapper.insertBatch(plateDOS);

            // 批量新增生产单明细表信息
            ArrayList<OrderItemDO> orderItemDOS = new ArrayList();

            for (PlateDetialRespVO plateDetialRespVO : req.getPlateList()) {
                orderItemDOS.add(OrderItemDO.builder()
                        .id(plateDetialRespVO.getItemId())
                        .bodyId(plateDetialRespVO.getBodyId())
                        .groupId(plateDetialRespVO.getProcessGroupId())
                        .orderId(plateDetialRespVO.getOrderId())
                        .type(1)
                        .plateId(plateDetialRespVO.getId())
                        .build());
            }


            orderItemMapper.insertBatch(orderItemDOS);

        }

        // 有删除的小板ID，删除小板
        if (CollectionUtil.isNotEmpty(req.getDeletePlateIds())) {

            // 删除生产单板件表的小板
            plateMapper.deleteBatchIds(req.getDeletePlateIds());

            // 删除生产单明细表中对应的小板数据
            orderItemMapper.deleteByPlateId(req.getDeletePlateIds());


        }


    }


    private List<OrderModelDO> buildRespByOrderIds(Collection<Long> orderIds, String index, Integer size) {
        List<FieldValue> fieldValues = orderIds.stream().map(FieldValue::of).toList();
        SearchRequest.Builder builder = new SearchRequest.Builder();
        builder.index(index);
        builder.size(size);
        builder.query(q -> q.terms(b -> b.field("orderId").terms(e -> e.value(fieldValues))));
        try {
            SearchResponse<OrderModelDO> search = elasticsearchClient.search(builder.build(), OrderModelDO.class);
            List<Hit<OrderModelDO>> hits = search.hits().hits();
            if (CollectionUtil.isNotEmpty(hits)) {
                return hits.stream().map(Hit::source).collect(Collectors.toList());
            }
            return new ArrayList<>();
        } catch (IOException e) {
            log.error(e.getMessage());
            throw new ServiceException(INTERNAL_SERVER_ERROR);
        }
    }

    private List<OrderModelDO> buildRespByOrderId(Long orderId, String index,Integer size) {
        SearchRequest.Builder builder = new SearchRequest.Builder();
        builder.index(index);
        builder.size(size);
        builder.query(q -> q.term(b -> b.field("orderId").value(orderId)));
        try {
            SearchResponse<OrderModelDO> search = elasticsearchClient.search(builder.build(), OrderModelDO.class);
            List<Hit<OrderModelDO>> hits = search.hits().hits();
            if (CollectionUtil.isNotEmpty(hits)) {
                return hits.stream().map(Hit::source).collect(Collectors.toList());
            }
            return new ArrayList<>();
        } catch (IOException e) {
            log.error(e.getMessage());
            throw new ServiceException(INTERNAL_SERVER_ERROR);
        }
    }


    /**
     * 保存优化后的大板的数据
     *
     * @param optimizeBoardModelDOS
     */
    @Transactional(rollbackFor = Exception.class)
    public void batchSaveBoardModel(List<OptimizeBoardModelDO> optimizeBoardModelDOS) {
        try {
            BulkResponse bulkResponse = esDocumentService.bulkCreate(ORDER_REMAIN_PLATE_MODEL, optimizeBoardModelDOS);
            boolean errors = bulkResponse.errors();
            if (errors) {
                log.error(bulkResponse.items().toString());
                throw new ServiceException(500, "未知异常");
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error(e.getMessage());
            throw new RuntimeException(e);
        }

    }


    private List<OptimizeBoardModelDO> buildBoardByOrderId(Long orderId, String index,Integer size) {
        SearchRequest.Builder builder = new SearchRequest.Builder();
        builder.index(index);
        builder.size(size);
        builder.query(q -> q.term(b -> b.field("orderId").value(orderId)));
        try {
            SearchResponse<OptimizeBoardModelDO> search = elasticsearchClient.search(builder.build(), OptimizeBoardModelDO.class);
            List<Hit<OptimizeBoardModelDO>> hits = search.hits().hits();
            if (CollectionUtil.isNotEmpty(hits)) {
                return hits.stream().map(Hit::source).collect(Collectors.toList());
            }
            return new ArrayList<>();
        } catch (IOException e) {
            log.error(e.getMessage());
            throw new ServiceException(INTERNAL_SERVER_ERROR);
        }
    }



    private List<OptimizeBoardModelDO> buildBoardByOrderIds(Collection<Long> orderIds, String index, Integer size) {
        List<FieldValue> fieldValues = orderIds.stream().map(FieldValue::of).toList();
        SearchRequest.Builder builder = new SearchRequest.Builder();
        builder.index(index);
        builder.size(size);
        builder.query(q -> q.terms(b -> b.field("orderId").terms(e -> e.value(fieldValues))));
        try {
            SearchResponse<OptimizeBoardModelDO> search = elasticsearchClient.search(builder.build(), OptimizeBoardModelDO.class);
            List<Hit<OptimizeBoardModelDO>> hits = search.hits().hits();
            if (CollectionUtil.isNotEmpty(hits)) {
                return hits.stream().map(Hit::source).collect(Collectors.toList());
            }
            return new ArrayList<>();
        } catch (IOException e) {
            log.error(e.getMessage());
            throw new ServiceException(INTERNAL_SERVER_ERROR);
        }
    }



}
