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

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Pair;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.PageDTO;
import com.cf.imes.framework.common.enums.DeletedCodeEnum;
import com.cf.imes.framework.common.enums.OrderPackageStatusEnum;
import com.cf.imes.framework.common.exception.ServiceException;
import com.cf.imes.framework.common.pojo.CommonResult;
import com.cf.imes.framework.common.pojo.PageParam;
import com.cf.imes.framework.common.pojo.PageResult;
import com.cf.imes.framework.common.util.Assert.AssertUtils;
import com.cf.imes.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.cf.imes.framework.security.core.util.SecurityFrameworkUtils;
import com.cf.imes.module.executor.controller.admin.order.vo.order.OrderPrintDetailRespVO;
import com.cf.imes.module.executor.controller.admin.order.vo.order.OrderPrintListReqVO;
import com.cf.imes.module.executor.controller.admin.order.vo.order.OrderPrintRespVO;
import com.cf.imes.module.executor.controller.admin.order.vo.order.OrdersViewCadViewInfoRespVO;
import com.cf.imes.module.executor.controller.admin.ordercomponent.vo.OrderComponentRespVO;
import com.cf.imes.module.executor.controller.admin.ordercomponent.vo.OrderDetailComponentListReqVO;
import com.cf.imes.module.executor.controller.admin.ordercomponent.vo.OrderDetailComponentPageReqVO;
import com.cf.imes.module.executor.controller.admin.plate.vo.OrderViewPartPageRespVO;
import com.cf.imes.module.executor.controller.admin.plate.vo.OrderViewPlatePageRespVO;
import com.cf.imes.module.executor.dal.dataobject.orderGroup.OrderGroupDO;
import com.cf.imes.module.executor.dal.dataobject.orderItem.OrderItemDO;
import com.cf.imes.module.executor.dal.dataobject.orderItem.OrderItemTempDO;
import com.cf.imes.module.executor.dal.dataobject.orderItem.OrdersViewPartsPageDO;
import com.cf.imes.module.executor.dal.dataobject.orderItem.OrdersViewPlatesPageDO;
import com.cf.imes.module.executor.dal.dataobject.ordercomponent.OrderComponentDO;
import com.cf.imes.module.executor.dal.dataobject.order.OrderDO;
import com.cf.imes.module.executor.dal.dataobject.orderBody.OrderBodyDO;
import com.cf.imes.module.executor.dal.mysql.order.OrderMapper;
import com.cf.imes.module.executor.dal.mysql.orderBody.OrderBodyMapper;
import com.cf.imes.module.executor.dal.mysql.orderGroup.OrderGroupMapper;
import com.cf.imes.module.executor.dal.mysql.orderItem.OrderItemBatchMapper;
import com.cf.imes.module.executor.dal.mysql.orderItem.OrderItemMapper;
import com.cf.imes.module.executor.dal.mysql.orderItem.OrderItemTempBatchMapper;
import com.cf.imes.module.executor.dal.mysql.orderItem.OrderItemTempMapper;
import com.cf.imes.module.executor.dal.mysql.ordercomponent.OrderComponentMapper;
import com.cf.imes.module.executor.dal.mysql.plate.PlateMapper;
import com.cf.imes.module.executor.service.manage.pack.PackService;
import com.cf.imes.module.executor.service.order.OrderInfoVersionControlService;
import com.cf.imes.module.executor.service.order.OrderService;
import com.cf.imes.module.infra.api.file.FileApi;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.cf.imes.module.executor.enums.ErrorCodeConstants.ORDER_BODY_NOT_EXISTS;
import static com.cf.imes.module.executor.enums.ErrorCodeConstants.ORDER_COMPONENT_DELETE_PLATE_IN_PLAN;
import static com.cf.imes.module.executor.enums.ErrorCodeConstants.ORDER_COMPONENT_NOT_EXIST;
import static com.cf.imes.module.executor.enums.ErrorCodeConstants.ORDER_COMPONENT_NO_NEED_TO_RESTORE;
import static com.cf.imes.module.executor.enums.ErrorCodeConstants.ORDER_COMPONENT_RESTORE_PARENT_IS_DELETED;
import static com.cf.imes.module.executor.enums.ErrorCodeConstants.ORDER_NOT_EXISTS;

/**
 * 订单部件服务实现类 ServiceImpl
 *
 * @author Gqr
 * @since 2026/1/27 14:33
 */
@Service
@Slf4j
public class OrderComponentServiceImpl implements OrderComponentService {
    @Resource
    private OrderComponentMapper orderComponentMapper;

    @Resource
    private OrderItemMapper orderItemMapper;

    @Resource
    private OrderMapper orderMapper;

    @Resource
    private PlateMapper plateMapper;

    @Resource
    private OrderBodyMapper orderBodyMapper;

    @Resource
    private OrderItemTempMapper orderItemTempMapper;

    @Resource
    private OrderItemBatchMapper orderItemBatchMapper;

    @Resource
    private OrderItemTempBatchMapper orderItemTempBatchMapper;

    @Resource
    private OrderGroupMapper orderGroupMapper;

    @Resource
    private FileApi fileApi;

    @Resource
    private OrderInfoVersionControlService orderInfoVersionControlService;

    @Resource
    private PackService packService;

    @Resource
    private OrderService orderService;

    @Override
    public List<OrderComponentRespVO> getOrderComponentList(OrderDetailComponentListReqVO reqVO) {
        List<OrderComponentRespVO> resultList = new ArrayList<>();
        // 查询所有满足条件的部件id
        Set<Long> orderDetailComponentIds = orderComponentMapper.getOrderDetailComponentIds(reqVO);
        if (CollUtil.isEmpty(orderDetailComponentIds)) {
            return resultList;
        }

        // 拆分每500条查询一次
        List<OrderComponentDO> resultDOList = new ArrayList<>();
        List<List<Long>> partitions = CollUtil.split(orderDetailComponentIds, 500);
        for (List<Long> partIds : partitions) {
            // 根据部件id查询顶级节点
            List<OrderComponentDO> topComponentList = orderComponentMapper.getTopComponentListIn(partIds, reqVO.isDeleted());
            if (CollUtil.isNotEmpty(topComponentList)) {
                resultDOList.addAll(topComponentList);
            }
        }

        // 结果根据id去重和排序
        Map<Long, OrderComponentDO> uniqueMap = resultDOList.stream()
                .collect(Collectors.toMap(
                        OrderComponentDO::getId,
                        Function.identity(),
                        (a, b) -> a,
                        LinkedHashMap::new
                ));

        List<OrderComponentDO> uniqueList = new ArrayList<>(uniqueMap.values());
        uniqueList.sort(Comparator
                .comparing(OrderComponentDO::getId));

        return BeanUtil.copyToList(uniqueList, OrderComponentRespVO.class);
    }

    @Override
    public List<OrderComponentRespVO> getComponentListOnTopId(OrderDetailComponentListReqVO reqVO) {
        Set<Long> componentIds = reqVO.getComponentIds();
        if(CollUtil.isEmpty(componentIds)) {
            return Collections.emptyList();
        }
        // 校验订单
        Long orderId = reqVO.getOrderId();
        OrderDO orderDO = orderMapper.selectById(orderId);
        AssertUtils.notEmpty(orderDO,ORDER_NOT_EXISTS);

        // 1、查询部件
        List<OrderComponentDO> componentDOS = orderComponentMapper.selectByIds(componentIds);
        if (CollUtil.isEmpty(componentDOS) || componentDOS.size() != componentIds.size()) {
            throw new ServiceException(ORDER_COMPONENT_NOT_EXIST);
        }
        List<Long> topIds = componentDOS.stream()
                .map(OrderComponentDO::getTopId)
                .toList();

        // 2、根据部件的topid查询树上所有节点
        List<OrderComponentDO> topComponentList = orderComponentMapper.selectList(new LambdaQueryWrapperX<OrderComponentDO>()
                .in(OrderComponentDO::getTopId, topIds)
                .eq(OrderComponentDO::getOrderId, orderId)
                .eqIfPresent(OrderComponentDO::getDeleted, reqVO.isDeleted())
                .likeIfPresent(OrderComponentDO::getName, reqVO.getName())
                .eqIfPresent(OrderComponentDO::getProcessStatus, reqVO.getProcessStatus())
                .select(OrderComponentDO::getId, OrderComponentDO::getPid, OrderComponentDO::getName, OrderComponentDO::getCadViewFileId, OrderComponentDO::getProcessStatus));

        List<OrderComponentRespVO> orderComponentRespVOS = BeanUtil.copyToList(topComponentList, OrderComponentRespVO.class);
        // 3、获取文件 fileHash
        Set<Long> cadViewFileIds = orderComponentRespVOS.stream().map(OrderComponentRespVO::getCadViewFileId).collect(Collectors.toSet());
        if (CollUtil.isNotEmpty(cadViewFileIds)) {
            CommonResult<Map<Long, String>> fileHashMapByIdsResult = fileApi.getFileHashMapByIds(cadViewFileIds);
            if (fileHashMapByIdsResult.isSuccess()) {
                Map<Long, String> fileHashMapByIds = fileHashMapByIdsResult.getData();
                orderComponentRespVOS.forEach(component ->
                        component.setFileHash(fileHashMapByIds.get(component.getCadViewFileId()))
                );
            }
        }
        return orderComponentRespVOS;
    }

    @Override
    public PageResult<OrderViewPlatePageRespVO> getViewComponentPlatesPage(OrderDetailComponentPageReqVO pageReqVO) {
        Set<Long> componentIds = pageReqVO.getComponentIds();
        if (CollUtil.isEmpty(componentIds)) {
            return new PageResult<>(Collections.emptyList(), 0L);
        }
        // 校验订单
        List<Long> orderIds = pageReqVO.getOrderIds();
        List<OrderDO> orderDOS = orderMapper.selectByIds(orderIds);
        AssertUtils.notEmpty(orderDOS, ORDER_NOT_EXISTS);

        // 查询部件
        List<OrderComponentDO> componentDOS = orderComponentMapper.selectByIds(componentIds);
        if (CollUtil.isEmpty(componentDOS) || componentDOS.size() != componentIds.size()) {
            throw new ServiceException(ORDER_COMPONENT_NOT_EXIST);
        }

        if (CollUtil.isEmpty(componentDOS)) {
            return new PageResult<>();
        }

        Set<Long> queryComponentIds = componentDOS.stream().map(OrderComponentDO::getId).collect(Collectors.toSet());
        // 整理出id和cadViewFileId的对应关系
        Map<Long, OrderComponentDO> cadViewFileIdMap = componentDOS.stream()
                .filter(item -> item.getCadViewFileId() != null)
                .collect(Collectors.toMap(
                        OrderComponentDO::getId,
                        Function.identity(),
                        (a, b) -> a
                ));

        Boolean deleted = pageReqVO.getDeleted();

        // 分页获取板件id
        IPage<Long> plateIdPage = orderItemMapper.selectOrdersViewPlateIdsPage(new PageDTO<>(pageReqVO.getPageNo(), pageReqVO.getPageSize()),
                orderIds, null, null, queryComponentIds, deleted);

        List<Long> plateIdPageRecords = plateIdPage.getRecords();
        if(CollUtil.isEmpty(plateIdPageRecords)) {
            return new PageResult<>(Collections.emptyList(), 0L);
        }

        // 查询板件+板材信息
        List<OrdersViewPlatesPageDO> ordersViewPlatesPageDOS = plateMapper.selectPlateGoodsByIds(orderIds, plateIdPageRecords, deleted);


        if (CollUtil.isEmpty(plateIdPageRecords)) {
            return new PageResult<>();
        }
        // 查询关联关系
        List<OrderItemDO> orderItemRelation;
        // 查询关联关系
        if (!deleted) {
           orderItemRelation = orderItemMapper.selectList(new LambdaQueryWrapper<OrderItemDO>()
                    .in(OrderItemDO::getOrderId, pageReqVO.getOrderIds())
                    .in(OrderItemDO::getPlateId, plateIdPageRecords)
                    .select(OrderItemDO::getPlateId, OrderItemDO::getRoomId, OrderItemDO::getBodyId, OrderItemDO::getCadViewId, OrderItemDO::getGroupId, OrderItemDO::getCompId)
            );
        } else {
            List<OrderItemTempDO> orderItemTempDOS = orderItemTempMapper.selectList(new LambdaQueryWrapperX<OrderItemTempDO>()
                    .in(OrderItemTempDO::getOrderId, pageReqVO.getOrderIds())
                    .in(OrderItemTempDO::getPlateId, plateIdPageRecords)
                    .select(OrderItemTempDO::getPlateId, OrderItemTempDO::getRoomId, OrderItemTempDO::getBodyId, OrderItemTempDO::getCadViewId, OrderItemTempDO::getGroupId, OrderItemTempDO::getCompId)
            );
            orderItemRelation = BeanUtil.copyToList(orderItemTempDOS, OrderItemDO.class);
        }


        // 暂存plateId:set<groupId>
        Map<Long, Set<Long>> plateGroupMap = new HashMap<>();
        // 暂存plateId:set<compId>
        Map<Long, Set<Long>> plateCompMap = new HashMap<>();
        // 暂存plateId:bodyId
        Map<Long, Long> plateBodyMap = new HashMap<>();

        Map<Long, OrdersViewPlatesPageDO> plateMap =
                ordersViewPlatesPageDOS.stream()
                        .collect(Collectors.toMap(
                                OrdersViewPlatesPageDO::getId,
                                Function.identity(),
                                (a, b) -> a
                        ));

        for (OrderItemDO item : orderItemRelation) {

            Long plateId = item.getPlateId();

            // 累计plateId下group_id
            if (item.getGroupId() != null) {
                plateGroupMap
                        .computeIfAbsent(plateId, k -> new HashSet<>())
                        .add(item.getGroupId());
            }

            // 记录plateId下body_id
            if (item.getBodyId() != null) {
                plateBodyMap
                        .computeIfAbsent(plateId, k -> item.getBodyId());
            }

            // 累计plateId下comp_id
            if (item.getCompId() != null) {
                plateCompMap
                        .computeIfAbsent(plateId, k -> new HashSet<>())
                        .add(item.getCompId());
            }

            // 单值字段
            OrdersViewPlatesPageDO plate = plateMap.get(plateId);
            if (plate != null) {
                plate.setCadViewId(item.getCadViewId());
            }
        }

        // 查询加工组
        Set<Long> allGroupIds = plateGroupMap.values()
                .stream()
                .flatMap(Set::stream)
                .collect(Collectors.toSet());

        Map<Long, String> groupNameMap = Collections.emptyMap();

        if (!allGroupIds.isEmpty()) {
            groupNameMap = orderGroupMapper.selectByIds(allGroupIds)
                    .stream()
                    .collect(Collectors.toMap(
                            OrderGroupDO::getId,
                            OrderGroupDO::getName,
                            (a, b) -> a
                    ));
        }

        // 查询房间
        Set<Long> allBodyIds = plateBodyMap.values()
                .stream()
                .collect(Collectors.toSet());

        Map<Long, OrderBodyDO> bodyMap = Collections.emptyMap();

        if (!allBodyIds.isEmpty()) {
            bodyMap = orderBodyMapper.selectByIds(allBodyIds)
                    .stream()
                    .collect(Collectors.toMap(
                            OrderBodyDO::getId,
                            Function.identity(),
                            (a, b) -> a
                    ));
        }

        List<OrderViewPlatePageRespVO> records = new ArrayList<>();
        for (Map.Entry<Long, OrdersViewPlatesPageDO> entry : plateMap.entrySet()) {

            Long plateId = entry.getKey();
            OrdersViewPlatesPageDO plate = entry.getValue();

            OrderViewPlatePageRespVO plateRespVO = BeanUtil.copyProperties(plate, OrderViewPlatePageRespVO.class);

            // 用逗号拼接加工组名称
            Set<Long> groupIds = plateGroupMap.getOrDefault(plateId, Collections.emptySet());
            List<String> groupNames = groupIds.stream()
                    .map(groupNameMap::get)
                    .filter(Objects::nonNull)
                    .toList();

            plateRespVO.setProcessGroupName(String.join(",", groupNames));

            // 用逗号拼接部件组名称
            Set<Long> compIds = plateCompMap.getOrDefault(plateId, Collections.emptySet());
            List<OrderComponentDO> comps = compIds.stream()
                    .map(cadViewFileIdMap::get)
                    .filter(Objects::nonNull)
                    .toList();

            if (CollUtil.isNotEmpty(comps)) {
                plateRespVO.setComponentName(String.join(",", comps.stream().map(OrderComponentDO::getName).collect(Collectors.toSet())));
                plateRespVO.setCadViewFileId(comps.get(0).getCadViewFileId());
            }

            // 匹配房间名、柜体名、图纸id
            Long bodyId = plateBodyMap.get(plateId);
            OrderBodyDO orderBodyDO = bodyMap.get(bodyId);
            if(ObjectUtil.isNotNull(orderBodyDO)) {
                plateRespVO.setBodyName(orderBodyDO.getName());
                plateRespVO.setRoomName(orderBodyDO.getRoomName());
            }

            records.add(plateRespVO);
        }
        return new PageResult<>(records, plateIdPage.getTotal());
    }

    @Override
    public PageResult<OrderViewPartPageRespVO> getViewComponentPartsPage(OrderDetailComponentPageReqVO pageReqVO) {
        Set<Long> componentIds = pageReqVO.getComponentIds();
        if (CollUtil.isEmpty(componentIds)) {
            return new PageResult<>(Collections.emptyList(), 0L);
        }
        // 校验订单
        List<Long> orderIds = pageReqVO.getOrderIds();
        List<OrderDO> orderDOS = orderMapper.selectByIds(orderIds);
        AssertUtils.notEmpty(orderDOS, ORDER_NOT_EXISTS);

        // 查询部件
        List<OrderComponentDO> componentDOS = orderComponentMapper.selectByIds(componentIds);
        if (CollUtil.isEmpty(componentDOS) || componentDOS.size() != componentIds.size()) {
            throw new ServiceException(ORDER_COMPONENT_NOT_EXIST);
        }

        if (CollUtil.isEmpty(componentDOS)) {
            return new PageResult<>();
        }

        Set<Long> queryComponentIds = componentDOS.stream().map(OrderComponentDO::getId).collect(Collectors.toSet());
        boolean deleted = Boolean.TRUE.equals(pageReqVO.getDeleted());

        List<OrdersViewPartsPageDO> rows = orderItemMapper.selectOrdersViewPartsList(
                orderIds,
                null,
                null,
                queryComponentIds,
                null,
                deleted
        );

        if (CollUtil.isEmpty(rows)) {
            return new PageResult<>(Collections.emptyList(), 0L);
        }

        Collection<OrderViewPartPageRespVO> records = buildComponentViewPartsGroup(rows);
        return new PageResult<>(new ArrayList<>(records), (long) records.size());
    }

    /**
     * 组装配件的加工组和部件
     *
     * @param rows
     * @return
     */
    private Collection<OrderViewPartPageRespVO> buildComponentViewPartsGroup(List<OrdersViewPartsPageDO> rows) {
        Map<String, OrderViewPartPageRespVO> records = new LinkedHashMap<>();
        Map<String, Set<String>> processGroupNameMap = new HashMap<>();
        Map<String, Set<String>> componentNameMap = new HashMap<>();

        for (OrdersViewPartsPageDO row : rows) {
            String key = buildComponentViewPartKey(row);

            OrderViewPartPageRespVO vo = records.computeIfAbsent(key, k -> {
                OrderViewPartPageRespVO newVo = BeanUtil.copyProperties(row, OrderViewPartPageRespVO.class);
                newVo.setNum(BigDecimal.ZERO);
                newVo.setProcessGroupName("");
                newVo.setComponentName("");
                return newVo;
            });

            if (row.getNum() != null && ObjectUtil.isNotNull(row.getCompId())) {
                vo.setNum(row.getNum());
            }

            if (row.getCadViewFileId() != null && vo.getCadViewFileId() == null) {
                vo.setCadViewFileId(row.getCadViewFileId());
            }

            if (row.getProcessGroupName() != null && !row.getProcessGroupName().isBlank()) {
                processGroupNameMap.computeIfAbsent(key, k -> new LinkedHashSet<>()).add(row.getProcessGroupName());
            }

            if (row.getComponentName() != null && !row.getComponentName().isBlank()) {
                componentNameMap.computeIfAbsent(key, k -> new LinkedHashSet<>()).add(row.getComponentName());
            }
        }

        for (Map.Entry<String, OrderViewPartPageRespVO> entry : records.entrySet()) {
            String key = entry.getKey();
            OrderViewPartPageRespVO vo = entry.getValue();

            vo.setProcessGroupName(String.join(",", processGroupNameMap.getOrDefault(key, Collections.emptySet())));
            vo.setComponentName(String.join(",", componentNameMap.getOrDefault(key, Collections.emptySet())));
        }

        return records.values();
    }

    /**
     * 配件id+cadviewid key
     *
     * @param row
     * @return
     */
    private String buildComponentViewPartKey(OrdersViewPartsPageDO row) {
        return row.getId() + "_" + row.getCadViewId();
    }

    @Override
    public OrdersViewCadViewInfoRespVO getViewComponentCadInfo(OrderDetailComponentPageReqVO pageReqVO) {
        OrdersViewCadViewInfoRespVO ordersViewCadViewInfoRespVO = new OrdersViewCadViewInfoRespVO();
        Set<Long> componentIds = pageReqVO.getComponentIds();
        Long orderId = pageReqVO.getOrderId();

        // 1、查询顶级
        List<OrderComponentDO> componentDOS = orderComponentMapper.selectByIds(componentIds);
        if (CollUtil.isEmpty(componentDOS) || componentDOS.size() != componentIds.size()) {
            throw new ServiceException(ORDER_COMPONENT_NOT_EXIST);
        }
        List<Long> topIds = componentDOS.stream()
                .map(OrderComponentDO::getTopId)
                .toList();

        // 2、把下级部件id作为查询条件
        Boolean deleted = pageReqVO.getDeleted();
        List<OrderComponentDO> orderComponentDOS = orderComponentMapper.selectList(new LambdaQueryWrapperX<OrderComponentDO>()
                .in(OrderComponentDO::getTopId, topIds)
                .eq(OrderComponentDO::getOrderId, orderId)
                .eqIfPresent(OrderComponentDO::getDeleted, deleted)
                .select(OrderComponentDO::getId, OrderComponentDO::getCadViewFileId, OrderComponentDO::getPid));
        Set<Long> queryComponentIds = new HashSet<>();
        if (CollUtil.isNotEmpty(orderComponentDOS)) {
            for (OrderComponentDO co : componentDOS) {
                queryComponentIds.addAll(getChildrenIds(co, orderComponentDOS));
            }
        } else {
            return ordersViewCadViewInfoRespVO;
        }

        // 整理出id和cadViewFileId的对应关系
        Map<Long, Long> cadViewFileIdMap = orderComponentDOS.stream()
                .filter(item -> item.getCadViewFileId() != null)
                .collect(Collectors.toMap(OrderComponentDO::getId, OrderComponentDO::getCadViewFileId));

        // 查询所有板件的视图所需信息
        PageDTO<OrdersViewPlatesPageDO> page1 = new PageDTO<>(1, PageParam.PAGE_SIZE_NONE);
        IPage<OrdersViewPlatesPageDO> viewPlatesPage = orderItemMapper.selectOrdersViewPlatesPage(page1, List.of(orderId), null,
                null, queryComponentIds, true, deleted);
        List<OrdersViewPlatesPageDO> plateRecords = viewPlatesPage.getRecords();

        List<OrderViewPlatePageRespVO> plateResult = new ArrayList<>();
        if(CollUtil.isNotEmpty(plateRecords)) {
            plateResult = plateRecords.stream().map(item -> {
                OrderViewPlatePageRespVO vo = new OrderViewPlatePageRespVO();
                BeanUtil.copyProperties(item, vo);

                vo.setCadViewFileId(cadViewFileIdMap.get(item.getCompId()));
                return vo;
            }).toList();
        }

        // 查询所有配件的视图所需信息
        PageDTO<OrdersViewPartsPageDO> page2 = new PageDTO<>(1, PageParam.PAGE_SIZE_NONE);
        IPage<OrdersViewPartsPageDO> viewPartsPage = orderItemMapper.selectOrdersViewPartsPage(page2, List.of(orderId), null,
                null, queryComponentIds, null, true, deleted);
        List<OrdersViewPartsPageDO> partRecords = viewPartsPage.getRecords();

        List<OrderViewPartPageRespVO> partResult = new ArrayList<>();
        if (CollUtil.isNotEmpty(partRecords)) {
            partResult = partRecords.stream().map(item -> {
                OrderViewPartPageRespVO vo = new OrderViewPartPageRespVO();
                BeanUtil.copyProperties(item, vo);

                vo.setCadViewFileId(cadViewFileIdMap.get(item.getCompId()));
                return vo;
            }).toList();
        }

        ordersViewCadViewInfoRespVO.setPlates(plateResult);
        ordersViewCadViewInfoRespVO.setParts(partResult);
        return ordersViewCadViewInfoRespVO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteOrderComponent(Long orderId, Long id) {
        // 校验自身
        OrderDO orderDO = orderMapper.selectById(orderId);
        AssertUtils.notEmpty(orderDO, ORDER_NOT_EXISTS);

        Long organId = orderDO.getOrganId();

        OrderComponentDO componentDO = orderComponentMapper.selectById(id);
        AssertUtils.notEmpty(componentDO, ORDER_COMPONENT_NOT_EXIST);

        // 查询top树下层级以下的所有部件
        List<OrderComponentDO> componentOnTop = orderComponentMapper.getComponentListOnTopLevel(orderId, componentDO.getTopId(), componentDO.getLevel(), false);

        // 整理出所属的下级，整理成id集合
        Set<Long> deleteIds = getChildrenIds(componentDO, componentOnTop);

        // 板件id Set
        Set<Long> plateIdSet = new HashSet<>();
        // 需要重新累计数量的order_goods id
        Set<Long> deleteGoodsIds = new HashSet<>();
        // 需要重新累计数量的柜体 id
        Set<Long> deleteBodyIds = new HashSet<>();
        // 需要处理的包裹编号
        Set<Long> packageIdSet = new HashSet<>();

        // 👉 批量更新缓存
        List<Long> plateBuffer = new ArrayList<>(1000);

        // 分批处理：删除对应的板件
        List<List<Long>> split = CollUtil.split(deleteIds, 10);
        for (List<Long> ids : split) {
            // 部件下是否有在排单中的板件，有则终止删除
            boolean plateInPlan = orderComponentMapper.selectOrderComponentPlateInPlan(ids);
            if (plateInPlan) {
                throw new ServiceException(ORDER_COMPONENT_DELETE_PLATE_IN_PLAN);
            }

            // 查询明细表数据准备删除
            List<OrderItemDO> orderItemDOS = orderItemMapper.selectList(new LambdaQueryWrapper<OrderItemDO>()
                    .eq(OrderItemDO::getOrderId, orderId)
                    .in(OrderItemDO::getCompId, ids)
                    .ne(OrderItemDO::getPlateId, 0L)
                    .select(OrderItemDO::getPlateId, OrderItemDO::getBodyId, OrderItemDO::getPackageId)
            );

            // 删除前累计goods_id
            List<Long> batchDeleteGoodsIds = plateMapper.selectOrderComponentGoodsIds(orderId, ids, false);
            deleteGoodsIds.addAll(batchDeleteGoodsIds);

            // 累计必要数据：更新order_plate、累计bodyid和packageid
            cumulativeNecessaryDeleteData(orderId, organId, orderItemDOS, plateIdSet, plateBuffer, deleteBodyIds, packageIdSet);

            // 删除部件
            orderComponentMapper.updateOrderComponentsDeletedStatus(orderId, ids, true);

        }

        // 👉 处理最后不足1000的
        if (CollUtil.isNotEmpty(plateBuffer)) {
            plateMapper.updateDeletedPlate(plateBuffer, organId, orderId);
        }

        // order_item记录临时表
        copyOrderItemToTemp(orderId, organId, deleteIds);

        // order_item删除记录
        orderItemMapper.delete(new LambdaQueryWrapper<OrderItemDO>()
                .eq(OrderItemDO::getOrderId, orderId)
                .in(OrderItemDO::getCompId, deleteIds)
        );

        // 删除包裹
        if (CollUtil.isNotEmpty(packageIdSet)) {
            unpackAndDeletePack(orderId, organId, packageIdSet);
        }

        // 更新影响的柜体的板件数量和面积
        if (CollUtil.isNotEmpty(deleteBodyIds)) {
            orderInfoVersionControlService.updateOrderBodyPlateNum(deleteBodyIds, true);
        }
        // 更新影响的订单商品的板件数量和面积
        if (CollUtil.isNotEmpty(deleteGoodsIds)) {
            orderInfoVersionControlService.updateOrderGoodsPlateNumAndArea(deleteGoodsIds, true);
        }
        // 更新订单的板件数量和面积
        orderInfoVersionControlService.updateOrderPlateNumAndArea(orderDO, true);
    }

    /**
     * 复制订单明细到临时表
     *
     * @param orderId
     * @param organId
     * @param deleteIds
     */
    private void copyOrderItemToTemp(Long orderId, Long organId, Set<Long> deleteIds) {
        int pageSize = 1000;
        long lastId = 0L;

        while (true) {

            List<OrderItemDO> list =
                    orderItemMapper.selectOrderItemForTemp(orderId, organId, lastId, pageSize, null, deleteIds);

            if (CollUtil.isEmpty(list)) {
                break;
            }

            orderItemTempBatchMapper.insertBatchSomeColumn(BeanUtil.copyToList(list, OrderItemTempDO.class));

            lastId = list.get(list.size() - 1).getId();
        }
    }


    /**
     * 解包和删除包裹
     *
     * @param orderId
     * @param organId
     * @param packIds
     */
    private void unpackAndDeletePack(Long orderId, Long organId, Set<Long> packIds) {
        // 包裹号和订单号的对应关系
        Map<String, List<Long>> orderIdsByPackageNo = packService.getOrderIdsByPackageNo(packIds);

        Set<String> needDeletePackNoSet = new HashSet<>();

        for (Map.Entry<String, List<Long>> entry : orderIdsByPackageNo.entrySet()) {
            String packNoKey = entry.getKey();
            List<Long> orderIds = entry.getValue();

            // 当这个包裹只属于这个订单时，删除这个包裹
            if (CollUtil.isNotEmpty(orderIds) && orderIds.size() == 1 && ObjectUtil.equals(orderId, orderIds.get(0))) {
                needDeletePackNoSet.add(packNoKey);
            }
        }

        if (CollUtil.isNotEmpty(needDeletePackNoSet)) {
            // 删除订单下对应编号的包裹
            packService.deletePackageByPackageNo(orderId, needDeletePackNoSet);

            // 重置明细的包裹编号
            orderItemMapper.deletePackList(orderId, needDeletePackNoSet, organId);

            OrderDO orderDO = orderMapper.selectById(orderId);

            AssertUtils.notEmpty(orderDO, ORDER_NOT_EXISTS);

            orderDO.setPackaged(OrderPackageStatusEnum.UNPACKED.getStatus());

            orderMapper.updateById(orderDO);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void restoreOrderComponent(Long orderId, Long id) {
        // 校验自身
        OrderDO orderDO = orderMapper.selectById(orderId);
        AssertUtils.notEmpty(orderDO, ORDER_NOT_EXISTS);

        Long organId = orderDO.getOrganId();

        OrderComponentDO componentDO = orderComponentMapper.selectByIdIgnoreDeleted(id);
        AssertUtils.notEmpty(componentDO, ORDER_COMPONENT_NOT_EXIST);
        AssertUtils.equals(componentDO.getDeleted(), false, ORDER_COMPONENT_NO_NEED_TO_RESTORE);

        // 非顶级需要校验上级是否恢复
        if (OrderComponentDO.PARENT_ID_ROOT.equals(componentDO.getPid())) {
            OrderComponentDO parent = orderComponentMapper.selectByIdIgnoreDeleted(componentDO.getPid());
            if(parent != null && parent.getDeleted()) {
                throw new ServiceException(ORDER_COMPONENT_RESTORE_PARENT_IS_DELETED);
            }
        }

        // 查询top树下层级以下的所有部件
        List<OrderComponentDO> componentOnTop = orderComponentMapper.getComponentListOnTopLevel(orderId, componentDO.getTopId(), componentDO.getLevel(), true);

        // 板件id Set
        Set<Long> plateIdSet = new HashSet<>();
        // 整理出所属的下级，整理成id集合
        Set<Long> restoreIds = getChildrenIds(componentDO, componentOnTop);
        // 需要重新累计数量的order_goods id
        Set<Long> restoreGoodsIds = new HashSet<>();
        // 需要重新累计数量的柜体 id
        Set<Long> restoreBodyIds = new HashSet<>();

        // 👉 批量更新缓存
        List<Long> plateBuffer = new ArrayList<>(1000);

        // 分批处理：删除对应的板件
        List<List<Long>> split = CollUtil.split(restoreIds, 10);
        for (List<Long> ids : split) {
            // 部件下是否有在排单中的板件，有则终止恢复
            boolean plateInPlan = orderComponentMapper.selectOrderComponentPlateInPlan(ids);
            if (plateInPlan) {
                throw new ServiceException(ORDER_COMPONENT_DELETE_PLATE_IN_PLAN);
            }

            // 查询临时表数据准备恢复
            List<OrderItemTempDO> orderItemDOS = orderItemTempMapper.selectList(new LambdaQueryWrapper<OrderItemTempDO>()
                    .eq(OrderItemTempDO::getOrderId, orderId)
                    .in(OrderItemTempDO::getCompId, ids)
                    .ne(OrderItemTempDO::getPlateId, 0L)
                    .select(OrderItemTempDO::getPlateId, OrderItemTempDO::getBodyId)
            );

            // 恢复前累计goods_id
            List<Long> batchDeleteGoodsIds = plateMapper.selectOrderComponentGoodsIds(orderId, ids, true);
            restoreGoodsIds.addAll(batchDeleteGoodsIds);

            // 累计必要数据：更新order_plate、累计bodyid
            cumulativeNecessaryRestoreData(orderId, organId, orderItemDOS, plateIdSet, plateBuffer, restoreBodyIds);

            // 恢复部件
            orderComponentMapper.updateOrderComponentsDeletedStatus(orderId, ids, false);
        }

        // 👉 处理最后不足1000的
        if (CollUtil.isNotEmpty(plateBuffer)) {
            plateMapper.updateNoDeletedPlate(plateBuffer, organId, orderId);
        }

        // order_item恢复临时记录
        copyTempToOrderItem(orderId, organId, restoreIds);

        // order_item_temp删除记录
        orderItemTempMapper.delete(new LambdaQueryWrapper<OrderItemTempDO>()
                .eq(OrderItemTempDO::getOrderId, orderId)
                .in(OrderItemTempDO::getCompId, restoreIds)
        );

        // 更新影响的柜体
        if (CollUtil.isNotEmpty(restoreBodyIds)) {
            orderInfoVersionControlService.updateOrderBodyPlateNum(restoreBodyIds, false);
        }
        // 更新影响的订单商品
        if (CollUtil.isNotEmpty(restoreGoodsIds)) {
            orderInfoVersionControlService.updateOrderGoodsPlateNumAndArea(restoreGoodsIds, false);
        }
        // 更新订单
        orderInfoVersionControlService.updateOrderPlateNumAndArea(orderDO, false);
    }


    /**
     * 复制订单明细到临时表
     *
     * @param orderId
     * @param organId
     * @param deleteIds
     */
    private void copyTempToOrderItem(Long orderId, Long organId, Set<Long> deleteIds) {
        int pageSize = 1000;
        long lastId = 0L;

        while (true) {

            List<OrderItemTempDO> list =
                    orderItemTempMapper.selectOrderItemTempForItem(orderId, organId, lastId, pageSize, null, deleteIds);

            if (CollUtil.isEmpty(list)) {
                break;
            }

            orderItemBatchMapper.insertBatchSomeColumn(BeanUtil.copyToList(list, OrderItemDO.class));

            lastId = list.get(list.size() - 1).getId();
        }
    }

    /**
     * 累计必要数据
     *
     * @param orderId
     * @param organId
     * @param orderItemDOS  明细列表
     * @param plateIdSet    影响的板件id
     * @param plateBuffer   每1000条更新一次的plateid缓存
     * @param deleteBodyIds 影响的柜体id
     * @param packageIdSet  影响的包裹id
     */
    private void cumulativeNecessaryDeleteData(Long orderId, Long organId, List<OrderItemDO> orderItemDOS, Set<Long> plateIdSet, List<Long> plateBuffer, Set<Long> deleteBodyIds, Set<Long> packageIdSet) {
        for (OrderItemDO item : orderItemDOS) {

            Long plateId = item.getPlateId();

            if (plateId != null && plateIdSet.add(plateId)) {
                    plateBuffer.add(plateId);

                    // 满1000就更新
                    if (plateBuffer.size() >= 1000) {
                        // 删除小板
                        plateMapper.updateDeletedPlate(plateBuffer, organId, orderId);
                        plateBuffer.clear();
                    }
                }


            // 累计柜体id
            if (item.getBodyId() != null) {
                deleteBodyIds.add(item.getBodyId());
            }

            // 累计包裹id
            if (item.getPackageId() != null) {
                packageIdSet.add(item.getPackageId());
            }
        }
    }

    /**
     * 累计必要数据
     *
     * @param orderId
     * @param organId
     * @param orderItemTempDOS 临时明细
     * @param plateIdSet       影响的板件id
     * @param plateBuffer      每1000条更新一次的plateid缓存
     * @param restoreBodyIds   影响的柜体id
     */
    private void cumulativeNecessaryRestoreData(Long orderId, Long organId, List<OrderItemTempDO> orderItemTempDOS, Set<Long> plateIdSet, List<Long> plateBuffer, Set<Long> restoreBodyIds) {
        for (OrderItemTempDO item : orderItemTempDOS) {

            Long plateId = item.getPlateId();

            if (plateId != null && plateIdSet.add(plateId)) {
                    plateBuffer.add(plateId);

                    // 满1000就更新
                    if (plateBuffer.size() >= 1000) {
                        // 删除小板
                        plateMapper.updateNoDeletedPlate(plateBuffer, organId, orderId);
                        plateBuffer.clear();
                    }
                }


            // 累计柜体id
            if (item.getBodyId() != null) {
                restoreBodyIds.add(item.getBodyId());
            }
        }
    }

    /**
     * 整理出需要删除的部件id列表
     *
     * @param componentDO
     * @param componentOnTop
     * @return
     */
    private Set<Long> getChildrenIds(OrderComponentDO componentDO, List<OrderComponentDO> componentOnTop) {
        // 3、整理出真正的下级（通过 pid 逐级确认）
        Set<Long> needDeleteIds = new HashSet<>();
        needDeleteIds.add(componentDO.getId());

        if (CollUtil.isNotEmpty(componentOnTop)) {

            // 先构建 pid -> children 映射
            Map<Long, List<OrderComponentDO>> childrenMap = componentOnTop.stream()
                    .collect(Collectors.groupingBy(OrderComponentDO::getPid));

            // 用队列逐级往下找
            Deque<Long> queue = new ArrayDeque<>();
            queue.add(componentDO.getId());

            while (!queue.isEmpty()) {
                Long parentId = queue.poll();

                List<OrderComponentDO> children = childrenMap.get(parentId);
                if (CollUtil.isEmpty(children)) {
                    continue;
                }

                for (OrderComponentDO child : children) {
                    Long childId = child.getId();
                    if (needDeleteIds.add(childId)) { // 防重复
                        queue.add(childId);           // 继续往下找
                    }
                }
            }
        }
        return needDeleteIds;
    }

    @Override
    public List<OrderComponentRespVO> getOrderComponentListOnOrders(List<Long> orderIds, Long... organIds) {
        Long userOrganId = SecurityFrameworkUtils.getUserOrganId();
        if (ObjectUtil.isNull(userOrganId) && ArrayUtil.isNotEmpty(organIds)) {
            userOrganId = organIds[0];
        }
        List<OrderComponentDO> orderComponentDOS = orderComponentMapper.selectList(new LambdaQueryWrapper<OrderComponentDO>()
                .in(OrderComponentDO::getOrderId, orderIds)
                .eq(OrderComponentDO::getDeleted, false)
                .eq(OrderComponentDO::getOrganId, userOrganId)
                .select(OrderComponentDO::getId, OrderComponentDO::getName, OrderComponentDO::getPid));
        return BeanUtil.copyToList(orderComponentDOS, OrderComponentRespVO.class);
    }

    @Override
    public List<OrderComponentRespVO> getChildComponentListOnTopId(Long id) {
        List<OrderComponentDO> orderComponentDOS = orderComponentMapper.selectList(new LambdaQueryWrapperX<OrderComponentDO>()
                .eq(OrderComponentDO::getOrganId, SecurityFrameworkUtils.getUserOrganId())
                .eq(OrderComponentDO::getTopId, id)
                .eq(OrderComponentDO::getDeleted, DeletedCodeEnum.NOT_DELETED.getStatus())
                .select(OrderComponentDO::getId, OrderComponentDO::getName, OrderComponentDO::getPid)
        );
        return BeanUtil.copyToList(orderComponentDOS, OrderComponentRespVO.class);
    }

    @Override
    public List<Long> getChildComponentIdsOnTopId(Long id) {
        List<Long> result = new ArrayList<>();
        OrderComponentDO componentDO = orderComponentMapper.selectById(id);
        if (ObjectUtil.isNotNull(componentDO)) {
            // 查询顶级下所有部件
            result = orderComponentMapper.selectList(new LambdaQueryWrapperX<OrderComponentDO>()
                    .eq(OrderComponentDO::getTopId, componentDO.getTopId())
                    .eq(OrderComponentDO::getDeleted, DeletedCodeEnum.NOT_DELETED.getStatus())
                    .select(OrderComponentDO::getId)
            ).stream().map(OrderComponentDO::getId).toList();
        }
        return result;
    }

    @Override
    public List<Long> getChildComponentIdsOnMy(Long id) {
        List<Long> result = new ArrayList<>();
        OrderComponentDO componentDO = orderComponentMapper.selectById(id);
        if (ObjectUtil.isNotNull(componentDO)) {
            // 查询顶级下所有部件
            List<OrderComponentDO> componentListOnTop = orderComponentMapper.selectList(new LambdaQueryWrapperX<OrderComponentDO>()
                    .eq(OrderComponentDO::getTopId, componentDO.getTopId())
                    .eq(OrderComponentDO::getDeleted, DeletedCodeEnum.NOT_DELETED.getStatus())
                    .select(OrderComponentDO::getId, OrderComponentDO::getPid)
            );

            return getChildrenIds(componentDO, componentListOnTop).stream().toList();
        }
        return result;
    }

    @Override
    public OrdersViewCadViewInfoRespVO getOrderViewCadInfo(OrderDetailComponentPageReqVO pageReqVO) {
        OrdersViewCadViewInfoRespVO ordersViewCadViewInfoRespVO = new OrdersViewCadViewInfoRespVO();
        Long orderId = pageReqVO.getOrderId();

        boolean useBodyKey = true;
        // cad图纸id对应map
        Map<Long, Long> cadViewFileIdMap = new HashMap<>();
        Set<Long> bodyIds = pageReqVO.getBodyIds();
        if(CollUtil.isNotEmpty(bodyIds)) {
            List<OrderBodyDO> orderBodyDOList = orderBodyMapper.selectByIds(bodyIds);
            if (CollUtil.isEmpty(orderBodyDOList)) {
                throw new ServiceException(ORDER_BODY_NOT_EXISTS);
            }

            // 整理出body_id和cad图纸id的关系
            cadViewFileIdMap = orderBodyDOList.stream()
                    .filter(item -> item.getCadViewFileId() != null)
                    .collect(Collectors.toMap(OrderBodyDO::getId, OrderBodyDO::getCadViewFileId));
        }


        Set<Long> componentIds = pageReqVO.getComponentIds();
        // 要查询的部件id列表
        Set<Long> queryComponentIds = new HashSet<>();
        if(CollUtil.isNotEmpty(componentIds)) {
            // 1、查询顶级
            List<OrderComponentDO> componentDOS = orderComponentMapper.selectByIds(componentIds);
            if (CollUtil.isEmpty(componentDOS) || componentDOS.size() != componentIds.size()) {
                throw new ServiceException(ORDER_COMPONENT_NOT_EXIST);
            }
            List<Long> topIds = componentDOS.stream()
                    .map(OrderComponentDO::getTopId)
                    .toList();

            // 2、把下级部件id作为查询条件
            List<OrderComponentDO> orderComponentDOS = orderComponentMapper.selectList(new LambdaQueryWrapperX<OrderComponentDO>()
                    .in(OrderComponentDO::getTopId, topIds)
                    .eq(OrderComponentDO::getOrderId, orderId)
                    .eqIfPresent(OrderComponentDO::getDeleted, pageReqVO.getDeleted())
                    .select(OrderComponentDO::getId, OrderComponentDO::getCadViewFileId, OrderComponentDO::getPid));
            if (CollUtil.isNotEmpty(orderComponentDOS)) {
                for (OrderComponentDO co : componentDOS) {
                    queryComponentIds.addAll(getChildrenIds(co, orderComponentDOS));
                }
            } else {
                return ordersViewCadViewInfoRespVO;
            }
            // 有柜体图纸对应关系就不用部件的了
            if (MapUtil.isEmpty(cadViewFileIdMap)) {
                useBodyKey = false;
                // 整理出id和cadViewFileId的对应关系
                cadViewFileIdMap = orderComponentDOS.stream()
                        .filter(item -> item.getCadViewFileId() != null)
                        .collect(Collectors.toMap(OrderComponentDO::getId, OrderComponentDO::getCadViewFileId));
            }
        }

        // 查询所有板件的视图所需信息
        List<OrdersViewPlatesPageDO> plateRecords = orderItemMapper.selectOrderViewPlates(orderId, bodyIds, queryComponentIds);

        List<OrderViewPlatePageRespVO> plateResult = new ArrayList<>();
        if (CollUtil.isNotEmpty(plateRecords)) {
            for (OrdersViewPlatesPageDO plateRecord : plateRecords) {
                OrderViewPlatePageRespVO vo = new OrderViewPlatePageRespVO();
                BeanUtil.copyProperties(plateRecord, vo);
                vo.setCadViewFileId(cadViewFileIdMap.get(useBodyKey ? plateRecord.getBodyId() : plateRecord.getCompId()));
                plateResult.add(vo);
            }
        }

        // 查询所有配件的视图所需信息
        List<OrdersViewPartsPageDO> partRecords = orderItemMapper.selectOrderViewParts(orderId, bodyIds, queryComponentIds);

        List<OrderViewPartPageRespVO> partResult = new ArrayList<>();
        if (CollUtil.isNotEmpty(partRecords)) {
            for (OrdersViewPartsPageDO partRecord : partRecords) {
                OrderViewPartPageRespVO vo = new OrderViewPartPageRespVO();
                BeanUtil.copyProperties(partRecord, vo);
                vo.setCadViewFileId(cadViewFileIdMap.get(useBodyKey ? partRecord.getBodyId() : partRecord.getCompId()));
                partResult.add(vo);
            }
        }

        ordersViewCadViewInfoRespVO.setPlates(plateResult);
        ordersViewCadViewInfoRespVO.setParts(partResult);
        return ordersViewCadViewInfoRespVO;
    }

    @Override
    public OrderPrintRespVO getOrderComponentPrintList(OrderPrintListReqVO pageReqVO) {
        OrderPrintRespVO result = new OrderPrintRespVO();
        List<Long> orderIds = pageReqVO.getOrderIds();
        Set<Long> componentIds = pageReqVO.getComponentIds();

        // 订单信息
        result.setOrderPrintInfoVOList(orderService.getOrderPrintInfo(orderIds));

        // 组合板件+配件
        List<OrderPrintDetailRespVO> ordersPrintPlatesList = orderItemMapper.selectOrdersPrintPlatesList(orderIds, componentIds);
        ordersPrintPlatesList.addAll(orderItemMapper.selectOrdersPrintPartsList(orderIds, componentIds));

        result.setOrderPrintDetailRespVOList(ordersPrintPlatesList);
        return result;
    }
}