package com.cf.imes.module.executor.util.fileConversion.admin.files.xml;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.excel.util.ListUtils;
import com.alibaba.fastjson.JSON;
import com.cf.imes.framework.common.exception.ServiceException;
import com.cf.imes.framework.common.util.json.JsonUtils;
import com.cf.imes.framework.dict.core.util.DictFrameworkUtils;
import com.cf.imes.framework.mybatis.core.generator.SnowFlakeGenerator;
import com.cf.imes.framework.organ.core.context.OrganContextHolder;
import com.cf.imes.framework.web.core.util.WebFrameworkUtils;
import com.cf.imes.module.executor.dal.dataobject.order.OrderDO;
import com.cf.imes.module.executor.dal.dataobject.orderImport.OrderImportTempDataDO;
import com.cf.imes.module.executor.dal.dataobject.plate.PlateGoodDO;
import com.cf.imes.module.executor.dal.mysql.orderImport.OrderImportTempDataMapper;
import com.cf.imes.module.executor.dal.mysql.plate.PlateGoodMapper;
import com.cf.imes.module.executor.enums.DataTypeEnum;
import com.cf.imes.module.executor.enums.DictTypeConstants;
import com.cf.imes.module.executor.enums.OrderImportTmpDataTypeEnum;
import com.cf.imes.module.executor.enums.OrderStatusEnum;
import com.cf.imes.module.executor.enums.OrderTypeEnum;
import com.cf.imes.module.executor.service.order.OrderService;
import com.cf.imes.module.executor.util.fileConversion.admin.ToolUtil;
import com.cf.imes.module.executor.util.fileConversion.admin.files.xml.VO.AssemblyXmlVO;
import com.cf.imes.module.executor.util.fileConversion.admin.files.xml.VO.BlockXmlVO;
import com.cf.imes.module.executor.util.fileConversion.admin.files.xml.VO.CorrelationXmlVO;
import com.cf.imes.module.executor.util.fileConversion.admin.files.xml.VO.CorrelationXmlVOS;
import com.cf.imes.module.executor.util.fileConversion.admin.files.xml.VO.CuttingOutlineXmlVO;
import com.cf.imes.module.executor.util.fileConversion.admin.files.xml.VO.EdgingXmlVO;
import com.cf.imes.module.executor.util.fileConversion.admin.files.xml.VO.GrooveXmlVO;
import com.cf.imes.module.executor.util.fileConversion.admin.files.xml.VO.GrooveXmlVOS;
import com.cf.imes.module.executor.util.fileConversion.admin.files.xml.VO.GroupXmlVO;
import com.cf.imes.module.executor.util.fileConversion.admin.files.xml.VO.HardwareXmlVO;
import com.cf.imes.module.executor.util.fileConversion.admin.files.xml.VO.HoleXmlVO;
import com.cf.imes.module.executor.util.fileConversion.admin.files.xml.VO.HoleXmlVOS;
import com.cf.imes.module.executor.util.fileConversion.admin.files.xml.VO.IsletXmlVO;
import com.cf.imes.module.executor.util.fileConversion.admin.files.xml.VO.IsletXmlVOS;
import com.cf.imes.module.executor.util.fileConversion.admin.files.xml.VO.OffsetLineXmlVO;
import com.cf.imes.module.executor.util.fileConversion.admin.files.xml.VO.OffsetLineXmlVOS;
import com.cf.imes.module.executor.util.fileConversion.admin.files.xml.VO.OrderXmlVO;
import com.cf.imes.module.executor.util.fileConversion.admin.files.xml.VO.OutlineXmlVO;
import com.cf.imes.module.executor.util.fileConversion.admin.files.xml.VO.PlateXmlVO;
import com.cf.imes.module.executor.util.fileConversion.admin.files.xml.VO.PointXmlVO;
import com.cf.imes.module.executor.util.fileConversion.admin.files.xml.VO.PointXmlVOS;
import com.cf.imes.module.executor.util.fileConversion.admin.files.xml.VO.RawOutlineXmlVO;
import com.cf.imes.module.executor.util.fileConversion.admin.files.xml.VO.RemarkXmlVO;
import com.cf.imes.module.executor.util.fileConversion.admin.files.xml.VO.RemarkXmlVOS;
import com.cf.imes.module.executor.util.fileConversion.admin.files.xml.VO.RoomBoxXmlVO;
import com.cf.imes.module.executor.util.fileConversion.admin.files.xml.VO.RoomXmlVO;
import jodd.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.xml.stream.Location;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.events.Attribute;
import javax.xml.stream.events.EndElement;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Supplier;

import static com.cf.imes.module.executor.enums.ErrorCodeConstants.ORDER_IMPORT_BODYCODE_NOT_EXIST_VALID_ERROR;
import static com.cf.imes.module.executor.enums.ErrorCodeConstants.ORDER_IMPORT_DETAIL_VALID_ERROR;
import static com.cf.imes.module.executor.enums.ErrorCodeConstants.ORDER_IMPORT_GROUPCODE_NOT_EXIST_VALID_ERROR;
import static com.cf.imes.module.executor.enums.ErrorCodeConstants.ORDER_IMPORT_ORDER_CUSTOMERNO_EXISTS;
import static com.cf.imes.module.executor.enums.ErrorCodeConstants.ORDER_IMPORT_ORDER_FIELD_DATE_FORMAT_ERROR;
import static com.cf.imes.module.executor.enums.ErrorCodeConstants.ORDER_IMPORT_ORDER_FIELD_EMPTY;
import static com.cf.imes.module.executor.enums.ErrorCodeConstants.ORDER_IMPORT_ORDER_FIELD_LENGTH_ERROR;
import static com.cf.imes.module.executor.enums.ErrorCodeConstants.ORDER_IMPORT_ORDER_FIELD_MOBILE_FORMAT_ERROR;
import static com.cf.imes.module.executor.enums.ErrorCodeConstants.ORDER_IMPORT_PLATEGOODS_NOT_EXIST_VALID_ERROR;
import static com.cf.imes.module.executor.enums.ErrorCodeConstants.ORDER_IMPORT_ROOMCODE_NOT_EXIST_VALID_ERROR;
import static com.cf.imes.module.executor.enums.OrderImportConstants.NOT_ASSIGNED;
import static com.cf.imes.module.executor.enums.OrderImportConstants.NOT_ASSIGNED_CODE;

/**
 * 默认订单xml解析处理器
 *
 * @author Gqr
 * @since 2025/2/18 16:31
 */
@Slf4j
public class DefaultOrderImportXmlParserHandler {

    /**
     * 存放订单orders信息
     */
    private OrderDO orderDO;
    /**
     * 每500条操作入库一次
     */
    private static final int BATCH_COUNT = 500;
    private List<OrderImportTempDataDO> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);

    private static final String TYPE_ERR = "%s数据类型错误";
    private static final String POSITIVE_ERR = "%s数据需为正数";
    private static final String LONG_ERR = "%s数据长度不符合（共8位整数位5位，小数位3位）";
    private static final String IS_NULL = "%s不能为空";
    private static final String INVALID = "%s数据无效";

    // todo 对编码去重，如果后期性能瓶颈需要改造到消费后用数据库
    private Set<String> groupCodeSet = new HashSet<>();
    private Map<String, RoomXmlVO> roomMap = new HashMap<>();
    private Map<String, RoomBoxXmlVO> roomBodyMap = new HashMap<>();
    private Map<String, OrderImportTempDataDO> existGoodsMap = new HashMap<>(); // 物料编码：已创建的innerGoodsId

    /**
     * 补板用
     */
    private Long orderId;

    /**
     * 是否补板
     */
    boolean addToExistsOrder;

    /**
     * 导入任务id
     */
    private Long taskId;

    private int sort = 1;

    // 全局对象，一般用于下级获取属性或者信息提示
    private RoomXmlVO roomXmlVO;
    private BlockXmlVO blockXmlVO;
    private PointXmlVOS pointXmlVOS;
    private PointXmlVOS groovePointXmlVOS;
    private OutlineXmlVO outlineXmlVO;
    private OutlineXmlVO grooveOutlineXmlVO;
    private CuttingOutlineXmlVO cuttingOutlineXmlVO;
    private RawOutlineXmlVO rawOutlineXmlVO;
    private HoleXmlVOS holeXmlVOS;
    private GrooveXmlVOS grooveXmlVOS;
    private GrooveXmlVO grooveXmlVO;
    private IsletXmlVOS isletXmlVOS;
    private IsletXmlVO isletXmlVO;
    private CorrelationXmlVOS correlationXmlVOS;
    private RemarkXmlVOS remarkXmlVOS;
    private OffsetLineXmlVOS offsetLineXmlVOS;
    private StartElement se;

    // 全局编码标记，如进入plate时设置plateItemCode，结束时设为null
    private String plateItemCode;
    private String partItemCode;

    // 全局标签标记，如进入outLine时inOutline设为true，结束时设为false
    private boolean inOutline;
    private boolean inCuttingOutline;
    private boolean inRawOutline;
    private boolean inGroove;
    private boolean inPoints;
    private boolean inIslet;

    private OrderImportTempDataMapper orderImportTempDataMapper;

    private OrderService orderService;

    private SnowFlakeGenerator snowFlakeGenerator;

    private PlateGoodMapper plateGoodMapper;

    // 基础常量
    private static final String FIELD_AXISXROTATE = "axisXrotate";
    private static final String FIELD_AXISYROTATE = "axisYrotate";
    private static final String FIELD_AXISZROTATE = "axisZrotate";
    private static final String FIELD_BASEPOINTX = "basePointX";
    private static final String FIELD_BASEPOINTY = "basePointY";
    private static final String FIELD_BASEPOINTZ = "basePointZ";
    private static final String FIELD_MULTIPLENUMBER = "multipleNumber";
    private static final String FIELD_MULTIPLENO = "multipleNo";
    private static final String FIELD_HEIGHT = "height";
    private static final String FIELD_DEPTH = "depth";
    private static final String FIELD_COLOR = "color";
    private static final String FIELD_WIDTH = "width";
    private static final String FIELD_LENGTH = "length";
    private static final String FIELD_UNITCOUNT = "unitCount";
    private static final String FIELD_COUNT = "count";
    private static final String FIELD_THICKNESS = "thickness";
    private static final String FIELD_ORDERCODE = "orderCode";

    public DefaultOrderImportXmlParserHandler(OrderImportTempDataMapper orderImportTempDataMapper, OrderService orderService, SnowFlakeGenerator snowFlakeGenerator, PlateGoodMapper plateGoodMapper, Long orderId, Long taskId) {
        this.orderImportTempDataMapper = orderImportTempDataMapper;
        this.orderService = orderService;
        this.snowFlakeGenerator = snowFlakeGenerator;
        this.plateGoodMapper = plateGoodMapper;
        this.orderId = orderId;
        this.taskId = taskId;
        // 是否补板
        addToExistsOrder = ObjectUtil.isNotNull(orderId);
    }

    /**
     * 解析文件
     *
     * @param file
     * @throws Exception
     */
    public void analyzeFile(MultipartFile file) throws Exception {
        InputStream is = null;
        try {
            //初始化工厂类以创建出XMLEventReader对象
            XMLInputFactory factory =
                    XMLInputFactory.newFactory();
            factory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);
            factory.setProperty(XMLInputFactory.SUPPORT_DTD, false);
            is = file.getInputStream();
            //通过读取XML文件创建XMLEventReader
            XMLEventReader reader = factory.createXMLEventReader(is);

            //判断是否还有下一个元素
            while (reader.hasNext()) {
                //获取元素对象XMLEvent
                XMLEvent en = reader.nextEvent();
                //判断是否是元素的开始
                if (en.isStartElement()) {
                    // 根据标签tag选择对应的解析方式
                    se = en.asStartElement();
                    String qName = se.getName().getLocalPart();
                    // 单个标签上的属性
                    Iterator<Attribute> attributes = se.getAttributes();
                    switch (qName) {
                        case "Order":
                            analyzeOrderXml(attributes);
                            break;
                        case "Room":
                            analyzeRoomXml(attributes);
                            break;
                        case "Box":
                            analyzeBodyXml(attributes);
                            break;
                        case "Group":
                            analyzeGroupXml(attributes);
                            break;
                        case "Plate":
                            analyzePlateXml(attributes, en.getLocation());
                            break;
                        case "Block":
                            analyzeBlockXml(attributes);
                            break;
                        case "Edging":
                            analyzeEdgingXml(attributes);
                            break;
                        case "Hardware":
                            analyzeHardwareXml(attributes);
                            break;
                        case "Assemblie":
                            analyzeAssemblieXml(attributes);
                            break;
                        case "Outline":
                            analyzeOutline(attributes);
                            break;
                        case "CuttingOutline":
                            analyzeCuttingOutline(attributes);
                            break;
                        case "RawOutline":
                            analyzeRawOutline(attributes);
                            break;
                        case "Holes":
                            analyzeHoles(attributes);
                            break;
                        case "Hole":
                            analyzeHole(attributes);
                            break;
                        case "Grooves":
                            analyzeGrooves(attributes);
                            break;
                        case "Groove":
                            analyzeGroove(attributes);
                            break;
                        case "Points":
                            analyzePoints(attributes);
                            break;
                        case "offsetLines":
                            analyzeOffsetLines(attributes);
                            break;
                        case "offsetLine":
                            analyzeOffsetLine(attributes);
                            break;
                        case "Islets":
                            analyzeIslets(attributes);
                            break;
                        case "Islet":
                            analyzeIslet(attributes);
                            break;
                        case "Correlations":
                            analyzeCorrelations(attributes);
                            break;
                        case "Correlation":
                            analyzeCorrelation(attributes);
                            break;
                        case "Point":
                            analyzePoint(attributes);
                            break;
                        case "Remarks":
                            analyzeRemarks(attributes);
                            break;
                        case "Remark":
                            analyzeRemark(attributes);
                            break;
                        default:
                    }
                    // 达到BATCH_COUNT了，需要去存储一次数据库，防止数据几万条数据在内存，容易OOM
                    if (cachedDataList.size() >= BATCH_COUNT) {
                        saveData();
                        // 存储完成清理 list
                        cachedDataList.clear();
                    }
                } else if (en.isEndElement()) {
                    // 根据标签tag选择对应的解析方式
                    EndElement endElement = en.asEndElement();
                    String qName = endElement.getName().getLocalPart();
                    // 清空全局se
                    se = null;
                    // 单个标签上的属性
                    switch (qName) {
                        case "Plate":
                            endPlate();
                            break;
                        case "Block":
                            endBlockXml();
                            break;
                        case "Edging":
                            endEdgingXml();
                            break;
                        case "Hardware":
                            endHardware();
                            break;
                        case "Assemblie":
                            endAssemblie();
                            break;
                        case "Outline":
                            endOutline();
                            break;
                        case "CuttingOutline":
                            endCuttingOutline();
                            break;
                        case "RawOutline":
                            endRawOutline();
                            break;
                        case "Groove":
                            endGroove();
                            break;
                        case "Points":
                            endPoints();
                            break;
                        case "Islet":
                            endIslet();
                            break;
                        case "Plates":
                            endPlates();
                            break;
                        default:
                    }
                }
            }
            // 文件解析结束了保存一次，确保最后遗留的数据也存储到数据库
            saveData();
            // 保存订单数据
            saveOrderTempData();
            log.info("所有数据解析完成！");
        } catch (Exception e) {
            throw e;
        } finally {
            IOUtils.closeQuietly(is);
        }
    }

    /**
     * 保存临时表
     */
    private void saveData() {
        if (CollUtil.isNotEmpty(cachedDataList)) {
            log.info("{}条数据，开始保存临时板材数据！", cachedDataList.size());
            orderImportTempDataMapper.insertBatchSomeColumn(cachedDataList);
            log.info("xml导入临时板材数据保存成功！");
        }
    }

    /**
     * xml标签属性转java对象
     *
     * @param clazz
     * @param attributes
     * @param <T>
     * @return 没有任何属性匹配上返回null
     */
    private <T> T getJavaBeanFromXmlAttr(Class<T> clazz, Iterator<Attribute> attributes) throws Exception {
        Field[] declaredFields = clazz.getDeclaredFields();
        T object = clazz.getDeclaredConstructor().newInstance();
        // 没有任何属性匹配上就返回null
        boolean match = false;
        while (attributes.hasNext()) {
            Attribute attribute = attributes.next();
            for (int j = 0; j < declaredFields.length; j++) {
                String fieldname = declaredFields[j].getName();
                if (attribute.getName().getLocalPart().equals(fieldname)) {
                    declaredFields[j].setAccessible(true);
                    declaredFields[j].set(object, attribute.getValue());
                    if (!match) {
                        match = true;
                    }
                    break;
                }
            }
        }
        if (!match) {
            return null;
        } else {
            return object;
        }
    }

    /**
     * 解析xml中的订单
     *
     * @param attributes
     * @throws Exception
     */
    private void analyzeOrderXml(Iterator<Attribute> attributes) throws Exception {
        // 从标签属性生成order对象
        OrderXmlVO orderXmlVO = getJavaBeanFromXmlAttr(OrderXmlVO.class, attributes);
        if (ObjectUtil.isNull(orderXmlVO)) {
            throw new ServiceException(ORDER_IMPORT_DETAIL_VALID_ERROR, "订单内容为空");
        }
        // 校验属性
        validOrderInfo(orderXmlVO);
    }


    /**
     * 解析xml中的房间列表
     *
     * @param attributes
     */
    private void analyzeRoomXml(Iterator<Attribute> attributes) throws Exception {
        // 从标签属性生成room对象
        RoomXmlVO roomXml = getJavaBeanFromXmlAttr(RoomXmlVO.class, attributes);
        if (ObjectUtil.isNull(roomXml)) {
            roomXml = new RoomXmlVO();
        }
        // 每解析到一次room，设置一次全局roomcode给box列表用
        // room完接下来都认为是box，直到新的room出现
        roomXmlVO = roomXml;
        // 校验房间属性
        checkRoom(roomXmlVO);

        // 缓存房间
        roomMap.put(roomXmlVO.getCode(), roomXmlVO);
    }

    /**
     * 校验房间数据
     *
     * @param room
     */
    private void checkRoom(RoomXmlVO room) {
        isEmptyAndAdd(room::getCode, room::setCode, NOT_ASSIGNED_CODE);
        isEmptyAndAdd(room::getName, room::setName, NOT_ASSIGNED);

        isEmptyAddAndPositiveNumberSize(room::getHeight, room::setHeight, "0", Double.class, FIELD_HEIGHT, 8, 3);
        isEmptyAddAndPositiveNumberSize(room::getLength, room::setLength, "0", Double.class, FIELD_LENGTH, 8, 3);
        isEmptyAddAndPositiveNumberSize(room::getWidth, room::setWidth, "0", Double.class, FIELD_WIDTH, 8, 3);
        isEmptyAddAndPositiveNumber(room::getMultipleNumber, room::setMultipleNumber, "1", Integer.class, FIELD_MULTIPLENUMBER);
        isEmptyAddAndPositiveNumber(room::getMultipleNo, room::setMultipleNo, "1", Integer.class, FIELD_MULTIPLENO);
        isEmptyAddAndNumber(room::getBasePointX, room::setBasePointX, "0", Double.class, FIELD_BASEPOINTX);
        isEmptyAddAndNumber(room::getBasePointY, room::setBasePointY, "0", Double.class, FIELD_BASEPOINTY);
        isEmptyAddAndNumber(room::getBasePointZ, room::setBasePointZ, "0", Double.class, FIELD_BASEPOINTZ);
        isEmptyAddAndNumber(room::getAxisXrotate, room::setAxisXrotate, "0", Double.class, FIELD_AXISXROTATE);
        isEmptyAddAndNumber(room::getAxisYrotate, room::setAxisYrotate, "0", Double.class, FIELD_AXISYROTATE);
        isEmptyAddAndNumber(room::getAxisZrotate, room::setAxisZrotate, "0", Double.class, FIELD_AXISZROTATE);
    }

    /**
     * 解析xml中的柜体列表
     *
     * @param attributes
     */
    private void analyzeBodyXml(Iterator<Attribute> attributes) throws Exception {
        // 从标签属性生成room对象
        RoomBoxXmlVO boxXmlVO = getJavaBeanFromXmlAttr(RoomBoxXmlVO.class, attributes);
        if (ObjectUtil.isNull(boxXmlVO)) {
            return;
        }
        // 校验柜体属性
        checkBox(boxXmlVO);
        // 设置房间基准属性
        boxXmlVO.setRoomAxisXrotate(roomXmlVO.getAxisXrotate());
        boxXmlVO.setRoomAxisYrotate(roomXmlVO.getAxisYrotate());
        boxXmlVO.setRoomAxisZrotate(roomXmlVO.getAxisZrotate());
        boxXmlVO.setRoomBasePointX(roomXmlVO.getBasePointX());
        boxXmlVO.setRoomBasePointY(roomXmlVO.getBasePointY());
        boxXmlVO.setRoomBasePointZ(roomXmlVO.getBasePointZ());

        String bodyCode = boxXmlVO.getCode();
        String roomCode = roomXmlVO.getCode();
        String roomName = roomXmlVO.getName();

        // 缓存roomCode+bodyCode
        String roomBodyKey = roomCode + bodyCode;
        RoomBoxXmlVO roomBoxXmlVO = roomBodyMap.get(roomBodyKey);
        if (ObjectUtil.isNull(roomBoxXmlVO)) {
            roomBodyMap.put(roomBodyKey, boxXmlVO);
        }

        // 添加到临时表
        cachedDataList.add(OrderImportTempDataDO.builder()
                .id((Long) snowFlakeGenerator.nextId(null))
                .orderId(orderId)
                .taskId(taskId)
                .type(OrderImportTmpDataTypeEnum.BODY.getType())
                .roomName(roomName)
                .roomCode(roomCode)
                .bodyName(boxXmlVO.getName())
                .bodyCode(bodyCode)
                .createTime(LocalDateTime.now())
                .detail(JsonUtils.zipString(JSON.toJSONString(boxXmlVO)))
                .sort(sort++)
                .build());
    }

    /**
     * 校验柜体数据
     *
     * @param box
     */
    public void checkBox(RoomBoxXmlVO box) {
        isEmptyAddAndPositiveNumberSize(box::getHeight, box::setHeight, "0", Double.class, FIELD_HEIGHT, 8, 3);
        isEmptyAddAndPositiveNumberSize(box::getLength, box::setLength, "0", Double.class, FIELD_LENGTH, 8, 3);
        isEmptyAddAndPositiveNumberSize(box::getWidth, box::setWidth, "0", Double.class, FIELD_WIDTH, 8, 3);
        isEmptyAddAndPositiveNumber(box::getMultipleNumber, box::setMultipleNumber, "1", Integer.class, FIELD_MULTIPLENUMBER);
        isEmptyAddAndPositiveNumber(box::getMultipleNo, box::setMultipleNo, "1", Integer.class, FIELD_MULTIPLENO);
        isEmptyAddAndNumber(box::getBasePointX, box::setBasePointX, "0", Double.class, FIELD_BASEPOINTX);
        isEmptyAddAndNumber(box::getBasePointY, box::setBasePointY, "0", Double.class, FIELD_BASEPOINTY);
        isEmptyAddAndNumber(box::getBasePointZ, box::setBasePointZ, "0", Double.class, FIELD_BASEPOINTZ);
        isEmptyAddAndNumber(box::getAxisXrotate, box::setAxisXrotate, "0", Double.class, FIELD_AXISXROTATE);
        isEmptyAddAndNumber(box::getAxisYrotate, box::setAxisYrotate, "0", Double.class, FIELD_AXISYROTATE);
        isEmptyAddAndNumber(box::getAxisZrotate, box::setAxisZrotate, "0", Double.class, FIELD_AXISZROTATE);
        isEmptyAndAdd(box::getCode, box::setCode, NOT_ASSIGNED_CODE);
        isEmptyAndAdd(box::getName, box::setName, NOT_ASSIGNED);
    }

    /**
     * 解析xml中的加工组列表
     *
     * @param attributes
     * @throws Exception
     */
    private void analyzeGroupXml(Iterator<Attribute> attributes) throws Exception {
        // 从标签属性生成group对象
        GroupXmlVO groupXmlVO = getJavaBeanFromXmlAttr(GroupXmlVO.class, attributes);
        if (ObjectUtil.isNull(groupXmlVO)) {
            groupXmlVO = new GroupXmlVO();
        }
        // 校验加工组数据
        checkGroup(groupXmlVO);

        String groupXmlVOCode = groupXmlVO.getCode();
        if (!groupCodeSet.contains(groupXmlVOCode)) {
            groupCodeSet.add(groupXmlVOCode);
        }

        // 添加到临时表
        cachedDataList.add(OrderImportTempDataDO.builder()
                .id((Long) snowFlakeGenerator.nextId(null))
                .orderId(orderId)
                .taskId(taskId)
                .type(OrderImportTmpDataTypeEnum.GROUP.getType())
                .groupName(groupXmlVO.getName())
                .groupCode(groupXmlVOCode)
                .createTime(LocalDateTime.now())
                .detail(JsonUtils.zipString(JSON.toJSONString(groupXmlVO)))
                .sort(sort++)
                .build());

    }

    /**
     * 校验加工组
     *
     * @param group
     */
    private void checkGroup(GroupXmlVO group) {
        isEmptyAndAdd(group::getCode, group::setCode, NOT_ASSIGNED_CODE);
        isEmptyAndAdd(group::getName, group::setName, NOT_ASSIGNED);
        isEmptyAddAndPositiveNumberSize(group::getLength, group::setLength, "0", Double.class, FIELD_LENGTH, 8, 3);
        isEmptyAddAndPositiveNumberSize(group::getWidth, group::setWidth, "0", Double.class, FIELD_WIDTH, 8, 3);
        isEmptyAddAndPositiveNumberSize(group::getHeight, group::setHeight, "0", Double.class, FIELD_HEIGHT, 8, 3);
    }

    /**
     * 解析xml中的板材列表
     *
     * @param attributes
     * @throws Exception
     */
    private void analyzePlateXml(Iterator<Attribute> attributes, Location location) throws Exception {
        // 从标签属性生成plate对象
        PlateXmlVO plateXmlVO = getJavaBeanFromXmlAttr(PlateXmlVO.class, attributes);
        if (ObjectUtil.isNull(plateXmlVO)) {
            plateXmlVO = new PlateXmlVO();
        }
        // 校验板材数据
        checkPlate(plateXmlVO, location);

        // 每解析到一次plate，设置一次全局plateItemCode给block列表用
        plateItemCode = String.valueOf(snowFlakeGenerator.nextId(null));

        // 相同的itemCode不重复创建临时Goods数据
        String currentItemCode = plateXmlVO.getItemCode();
        OrderImportTempDataDO existInnerGoods = existGoodsMap.get(currentItemCode);
        if(ObjectUtil.isNull(existInnerGoods)) {
            plateItemCode = String.valueOf(snowFlakeGenerator.nextId(null));

            // 暂存goods
            OrderImportTempDataDO goods = OrderImportTempDataDO.builder()
                    .id((Long) snowFlakeGenerator.nextId(null))
                    .orderId(orderId)
                    .taskId(taskId)
                    .type(OrderImportTmpDataTypeEnum.GOODS.getType())
                    .innerGoodsId(plateItemCode)
                    .createTime(LocalDateTime.now())
                    .detail(JsonUtils.zipString(JSON.toJSONString(plateXmlVO)))
                    .sort(sort++)
                    .num(0.0)
                    .area(BigDecimal.ZERO)
                    .build();
            existGoodsMap.put(plateItemCode, goods);
        } else {
            plateItemCode = existInnerGoods.getInnerGoodsId();
        }

    }

    /**
     * 校验板材
     *
     * @param plate
     */
    private void checkPlate(PlateXmlVO plate, Location location) {
        isEmptyAndBack(plate::getName, "plate:name");
        isEmptyAndBack(plate::getMaterial, "plate:material");

        // 纹理有效性判断
        isEmptyAddAndInvalid(plate::getGrained, plate::setGrained, "有", DictTypeConstants.PLATE_TEXTURE_TYPE, "grained");
        isEmptyAddAndInvalid(plate::getCoating, plate::setCoating, "双面", DictTypeConstants.COATING_TYPE, "coating");
        isEmptyAndPositiveNumber(plate::getLength, FIELD_LENGTH, plate::setLength, Double.class, 5, 3);
        isEmptyAndPositiveNumber(plate::getWidth, FIELD_WIDTH, plate::setWidth, Double.class, 5, 3);
        isEmptyAndPositiveNumber(plate::getThickness, FIELD_THICKNESS, plate::setThickness, Double.class, 5, 3);
        notEmptyPositiveNumberSize(plate::getOddmentLength, plate::setOddmentLength, "oddmentLength", Double.class, 5, 3);
        notEmptyPositiveNumberSize(plate::getOddmentWidth, plate::setOddmentWidth, "oddmentWidth", Double.class, 5, 3);
        notEmptyPositiveNumberSize(plate::getOddmentThickness, plate::setOddmentThickness, "oddmentThickness", Double.class, 5, 3);
        isEmptyAddAndInvalid(plate::getOddment, plate::setOddment, "否", DictTypeConstants.ODDMENT_MARK, "oddment");
        isEmptyAddAndInvalid(plate::getUnitLength, plate::setUnitLength, "mm", DictTypeConstants.UNIT_LENGTH, "unitLength");
        notEmptyPositiveNumber(plate::getArea, "area", Double.class);
        isEmptyAddAndInvalid(plate::getUnitArea, plate::setUnitArea, "m²", DictTypeConstants.UNIT_AREA, "unitArea");
        notEmptyPositiveNumber(plate::getCount, FIELD_COUNT, Integer.class);

        // 查询板材库是否有对应
        queryPlateExists(plate,location);
    }

    /**
     * 查询板材库是否有对应物料编码
     * 不存在再使用material、coatingObverse、thickness、brand、name查询是否存在匹配，还是不存在提示板材库先添加
     *
     * @param plate
     */
    private void queryPlateExists(PlateXmlVO plate, Location location) {
        PlateGoodDO plateGoodDO = null;
        List<PlateGoodDO> plateGoodDOs;
        if (StringUtils.isNotEmpty(plate.getItemCode())) {
            plateGoodDOs = plateGoodMapper.selectGoodOne(plate.getItemCode(), OrganContextHolder.getOrganId());
        } else {
            plateGoodDOs = plateGoodMapper.selectGood(plate.getMaterial(), plate.getCoatingObverse(),
                    BigDecimal.valueOf(Long.parseLong(plate.getThickness())),
                    plate.getBrand(), plate.getName(), OrganContextHolder.getOrganId());
        }

        if (CollUtil.isNotEmpty(plateGoodDOs)) {
            plateGoodDO = plateGoodDOs.get(0);
        }

        if (ObjectUtil.isNotNull(plateGoodDO)) {
            plate.setItemCode(plateGoodDO.getGoodsId());
        } else {
            throw new ServiceException(ORDER_IMPORT_PLATEGOODS_NOT_EXIST_VALID_ERROR, location.getLineNumber());
        }
    }

    /**
     * plate标签结束
     */
    private void endPlate() {
        // 设置全局参数
        plateItemCode = null;
    }

    /**
     * 解析xml中的加工组列表
     *
     * @param attributes
     * @throws Exception
     */
    private void analyzeBlockXml(Iterator<Attribute> attributes) throws Exception {
        // 从标签属性生成block对象
        blockXmlVO = getJavaBeanFromXmlAttr(BlockXmlVO.class, attributes);
        if (ObjectUtil.isNull(blockXmlVO)) {
            blockXmlVO = new BlockXmlVO();
        }
        // 校验板件
        checkBlock(blockXmlVO);

        // 如果有未命名的房间编号、柜体编号，直接在临时表创建这个柜体
        String roomCode = blockXmlVO.getRoomCode();
        String boxCode = blockXmlVO.getBoxCode();
        String groupCodes = blockXmlVO.getGroupCodes();
        // 校验检查房间、柜体、加工组编号
        checkRoomBoxGroup(se, roomCode, boxCode, groupCodes);

        blockXmlVO.setRoomCode(roomCode);
        blockXmlVO.setBoxCode(boxCode);
        blockXmlVO.setGroupCodes(groupCodes);

    }

    /**
     * 校验板件
     *
     * @param blockXmlVO
     */
    private void checkBlock(BlockXmlVO blockXmlVO) {
        isEmptyAndAdd(blockXmlVO::getName, blockXmlVO::setName, NOT_ASSIGNED);
        isEmptyAddAndInvalid(blockXmlVO::getType, blockXmlVO::setType, "背板", DictTypeConstants.PLATE_TYPE, "type");
        isEmptyAddAndInvalid(blockXmlVO::getOpenDirection, blockXmlVO::setOpenDirection, "非门板", DictTypeConstants.DOOR_OPENING_DIRECTIONS, "openDirection");
        isEmptyAddAndInvalid(blockXmlVO::getTexture, blockXmlVO::setTexture, "正纹", DictTypeConstants.GRAIN_TYPE, "texture");
        isEmptyAddAndInvalid(blockXmlVO::getLayoutFace, blockXmlVO::setLayoutFace, "随意面", DictTypeConstants.TYPOGRAPHY_TYPE, "layoutFace");
        isEmptyAddAndInvalid(blockXmlVO::getCoatingType, blockXmlVO::setCoatingType, "双面", DictTypeConstants.COATING_TYPE, "coatingType");
        isEmptyAndPositiveNumber(blockXmlVO::getLength, FIELD_LENGTH, blockXmlVO::setLength, Double.class, 5, 3);
        isEmptyAndPositiveNumber(blockXmlVO::getWidth, FIELD_WIDTH, blockXmlVO::setWidth, Double.class, 5, 3);
        isEmptyAndPositiveNumber(blockXmlVO::getThickness, FIELD_THICKNESS, blockXmlVO::setThickness, Double.class, 5, 3);
        isEmptyAndPositiveNumber(blockXmlVO::getCuttingLength, "cuttingLength", blockXmlVO::setCuttingLength, Double.class, 5, 3);
        isEmptyAndPositiveNumber(blockXmlVO::getCuttingWidth, "cuttingWidth", blockXmlVO::setCuttingWidth, Double.class, 5, 3);
        isEmptyAndPositiveNumber(blockXmlVO::getCuttingThickness, "cuttingThickness", blockXmlVO::setCuttingThickness, Double.class, 5, 3);
        isEmptyAndPositiveNumber(blockXmlVO::getRawLength, "rawLength", blockXmlVO::setRawLength, Double.class, 5, 3);
        isEmptyAndPositiveNumber(blockXmlVO::getRawWidth, "rawWidth", blockXmlVO::setRawWidth, Double.class, 5, 3);
        isEmptyAndPositiveNumber(blockXmlVO::getRawThickness, "rawThickness", blockXmlVO::setRawThickness, Double.class, 5, 3);
        isEmptyAddAndInvalid(blockXmlVO::getUnit, blockXmlVO::setUnit, "mm", DictTypeConstants.UNIT_LENGTH, "unit");
        isEmptyAddAndPositiveNumber(blockXmlVO::getMultipleNumber, blockXmlVO::setMultipleNumber, "1", Integer.class, FIELD_MULTIPLENUMBER);
        isEmptyAddAndPositiveNumber(blockXmlVO::getMultipleNo, blockXmlVO::setMultipleNo, "1", Integer.class, FIELD_MULTIPLENO);
        isEmptyAddAndInvalid(blockXmlVO::getUnregular, blockXmlVO::setUnregular, "否", DictTypeConstants.IS_NOT_MARK, "unregular");
        isEmptyAddAndInvalid(blockXmlVO::getDrilling, blockXmlVO::setDrilling, "否", DictTypeConstants.IS_NOT_MARK, "drilling");
        isEmptyAddAndInvalid(blockXmlVO::getSlotting, blockXmlVO::setSlotting, "否", DictTypeConstants.IS_NOT_MARK, "slotting");

        isEmptyAndAdd(blockXmlVO::getRoomCode, blockXmlVO::setRoomCode, NOT_ASSIGNED_CODE);
        isEmptyAndAdd(blockXmlVO::getRoomName, blockXmlVO::setRoomName, NOT_ASSIGNED);
        isEmptyAndAdd(blockXmlVO::getBoxCode, blockXmlVO::setBoxCode, NOT_ASSIGNED_CODE);
        isEmptyAndAdd(blockXmlVO::getBoxName, blockXmlVO::setBoxName, NOT_ASSIGNED);

        isEmptyAddAndNumber(blockXmlVO::getBasePointX, blockXmlVO::setBasePointX, "0", Double.class, FIELD_BASEPOINTX);
        isEmptyAddAndNumber(blockXmlVO::getBasePointY, blockXmlVO::setBasePointY, "0", Double.class, FIELD_BASEPOINTY);
        isEmptyAddAndNumber(blockXmlVO::getBasePointZ, blockXmlVO::setBasePointZ, "0", Double.class, FIELD_BASEPOINTZ);
        isEmptyAddAndNumber(blockXmlVO::getAxisXrotate, blockXmlVO::setAxisXrotate, "0", Double.class, FIELD_AXISXROTATE);
        isEmptyAddAndNumber(blockXmlVO::getAxisYrotate, blockXmlVO::setAxisYrotate, "0", Double.class, FIELD_AXISYROTATE);
        isEmptyAddAndNumber(blockXmlVO::getAxisZrotate, blockXmlVO::setAxisZrotate, "0", Double.class, FIELD_AXISZROTATE);
    }

    /**
     * block标签结束
     */
    private void endBlockXml() {
        blockXmlVO.setOutlineXmlVO(outlineXmlVO);
        blockXmlVO.setCuttingOutlineXmlVO(cuttingOutlineXmlVO);
        blockXmlVO.setRawOutlineXmlVO(rawOutlineXmlVO);
        blockXmlVO.setHoleXmlVOS(holeXmlVOS);
        blockXmlVO.setGrooveXmlVOS(grooveXmlVOS);
        blockXmlVO.setIsletXmlVOS(isletXmlVOS);
        blockXmlVO.setCorrelationXmlVOS(correlationXmlVOS);
        blockXmlVO.setRemarkXmlVOS(remarkXmlVOS);

        // 添加到临时表
        cachedDataList.add(OrderImportTempDataDO.builder()
                .id((Long) snowFlakeGenerator.nextId(null))
                .orderId(orderId)
                .taskId(taskId)
                .type(OrderImportTmpDataTypeEnum.PLATE.getType())
                .innerGoodsId(plateItemCode)
                .roomCode(blockXmlVO.getRoomCode())
                .bodyCode(blockXmlVO.getBoxCode())
                .groupCode(blockXmlVO.getGroupCodes())
                .createTime(LocalDateTime.now())
                .detail(JsonUtils.zipString(JSON.toJSONString(blockXmlVO)))
                .sort(sort++)
                .build());

        // 累加板材下板件数量、面积
        OrderImportTempDataDO orderImportTempDataDO = existGoodsMap.get(plateItemCode);
        if (orderImportTempDataDO != null) {
            orderImportTempDataDO.setNum(orderImportTempDataDO.getNum() + 1);
            Double length = changeUnit(blockXmlVO.getLength(), blockXmlVO.getUnit(), DictTypeConstants.UNIT_LENGTH);
            Double width = changeUnit(blockXmlVO.getWidth(), blockXmlVO.getUnit(), DictTypeConstants.UNIT_LENGTH);
            BigDecimal area = BigDecimal.valueOf((length / 1000) * (width / 1000));
            orderImportTempDataDO.setArea(orderImportTempDataDO.getArea().add(area));
        }

        // 清空全局参数
        points.clear();
    }

    /**
     * 解析xml中的封边条列表
     *
     * @param attributes
     * @throws Exception
     */
    private void analyzeEdgingXml(Iterator<Attribute> attributes) throws Exception {
        // 从标签属性生成edging对象
        EdgingXmlVO edgingXmlVO = getJavaBeanFromXmlAttr(EdgingXmlVO.class, attributes);
        if (ObjectUtil.isNull(edgingXmlVO)) {
            edgingXmlVO = new EdgingXmlVO();
        }
        // 校验封边条
        checkEdging(edgingXmlVO);

        // 如果有未命名的房间编号、柜体编号，直接在临时表创建这个柜体
        String roomCode = edgingXmlVO.getRoomCode();
        String boxCode = edgingXmlVO.getBoxCode();
        String groupCodes = edgingXmlVO.getGroupCode();
        // 校验检查房间、柜体、加工组编号
        checkRoomBoxGroup(se, roomCode, boxCode, groupCodes);

        // 设置全局参数
        String itemCode = edgingXmlVO.getItemCode();
        partItemCode = StringUtils.isNotEmpty(itemCode) ? itemCode : edgingXmlVO.getManufacturer() + edgingXmlVO.getBrand() + edgingXmlVO.getName() + edgingXmlVO.getMaterial() +
                edgingXmlVO.getSpecs() + edgingXmlVO.getColor() + edgingXmlVO.getWidth() + edgingXmlVO.getThickness();
        edgingXmlVO.setItemCode(partItemCode);

        // 添加到临时表
        cachedDataList.add(OrderImportTempDataDO.builder()
                .id((Long) snowFlakeGenerator.nextId(null))
                .orderId(orderId)
                .taskId(taskId)
                .type(OrderImportTmpDataTypeEnum.PART_EDGING.getType())
                .outerId(partItemCode)
                .roomCode(roomCode)
                .bodyCode(boxCode)
                .groupCode(groupCodes)
                .createTime(LocalDateTime.now())
                .detail(JsonUtils.zipString(JSON.toJSONString(edgingXmlVO)))
                .num(NumberUtil.parseDouble(edgingXmlVO.getCount()))
                .sort(sort++)
                .build());
    }

    /**
     * Edging标签结束
     */
    private void endEdgingXml() {
        // 设置全局参数
        partItemCode = null;
    }

    /**
     * 校验封边条配件
     *
     * @param edgingXmlVO
     */
    private void checkEdging(EdgingXmlVO edgingXmlVO) {
        isEmptyAndAdd(edgingXmlVO::getRoomCode, edgingXmlVO::setRoomCode, NOT_ASSIGNED_CODE);
        isEmptyAndAdd(edgingXmlVO::getRoomName, edgingXmlVO::setRoomName, NOT_ASSIGNED);
        isEmptyAndAdd(edgingXmlVO::getBoxCode, edgingXmlVO::setBoxCode, NOT_ASSIGNED_CODE);
        isEmptyAndAdd(edgingXmlVO::getBoxName, edgingXmlVO::setBoxName, NOT_ASSIGNED);
        isEmptyAndBack(edgingXmlVO::getName, "name");
        isEmptyAndBack(edgingXmlVO::getMaterial, "material");
        isEmptyAndBack(edgingXmlVO::getColor, FIELD_COLOR);
        isEmptyBackAndPositiveNumber(edgingXmlVO::getLength, FIELD_LENGTH, Double.class);
        isEmptyBackAndPositiveNumber(edgingXmlVO::getWidth, FIELD_WIDTH, Double.class);
        isEmptyBackAndPositiveNumber(edgingXmlVO::getThickness, FIELD_THICKNESS, Double.class);
        isEmptyAddAndInvalid(edgingXmlVO::getUnit, edgingXmlVO::setUnit, "mm", DictTypeConstants.UNIT_LENGTH, "unit");
        isEmptyBackAndPositiveNumber(edgingXmlVO::getCount, FIELD_COUNT, Double.class);
        isEmptyAddAndInvalid(edgingXmlVO::getUnitCount, edgingXmlVO::setUnitCount, "m", DictTypeConstants.UNIT_LENGTH, FIELD_UNITCOUNT);
    }

    /**
     * 解析xml中的五金列表
     *
     * @param attributes
     * @throws Exception
     */
    private void analyzeHardwareXml(Iterator<Attribute> attributes) throws Exception {
        // 从标签属性生成hardware对象
        HardwareXmlVO hardwareXmlVO = getJavaBeanFromXmlAttr(HardwareXmlVO.class, attributes);
        if (ObjectUtil.isNull(hardwareXmlVO)) {
            hardwareXmlVO = new HardwareXmlVO();
        }
        // 校验五金
        checkHardware(hardwareXmlVO);

        // 如果有未命名的房间编号、柜体编号，直接在临时表创建这个柜体
        String roomCode = hardwareXmlVO.getRoomCode();
        String boxCode = hardwareXmlVO.getBoxCode();
        String groupCodes = hardwareXmlVO.getGroupCode();
        // 校验检查房间、柜体、加工组编号
        checkRoomBoxGroup(se, roomCode, boxCode, groupCodes);

        // 设置全局参数
        String itemCode = hardwareXmlVO.getItemCode();
        partItemCode = StringUtils.isNotEmpty(itemCode) ? itemCode : hardwareXmlVO.getManufacturer() + hardwareXmlVO.getBrand() + hardwareXmlVO.getName() + hardwareXmlVO.getMaterial() +
                hardwareXmlVO.getSpecs() + hardwareXmlVO.getColor() + hardwareXmlVO.getType();
        hardwareXmlVO.setItemCode(partItemCode);

        // 添加到临时表
        cachedDataList.add(OrderImportTempDataDO.builder()
                .id((Long) snowFlakeGenerator.nextId(null))
                .orderId(orderId)
                .taskId(taskId)
                .type(OrderImportTmpDataTypeEnum.PART_HARDWARE.getType())
                .outerId(partItemCode)
                .roomCode(roomCode)
                .bodyCode(boxCode)
                .groupCode(groupCodes)
                .createTime(LocalDateTime.now())
                .detail(JsonUtils.zipString(JSON.toJSONString(hardwareXmlVO)))
                .num(NumberUtil.parseDouble(hardwareXmlVO.getCount()))
                .sort(sort++)
                .build());
    }

    /**
     * 校验五金
     *
     * @param hardwareXmlVO
     */
    private void checkHardware(HardwareXmlVO hardwareXmlVO) {
        isEmptyAndAdd(hardwareXmlVO::getRoomCode, hardwareXmlVO::setRoomCode, NOT_ASSIGNED_CODE);
        isEmptyAndAdd(hardwareXmlVO::getRoomName, hardwareXmlVO::setRoomName, NOT_ASSIGNED);
        isEmptyAndAdd(hardwareXmlVO::getBoxCode, hardwareXmlVO::setBoxCode, NOT_ASSIGNED_CODE);
        isEmptyAndAdd(hardwareXmlVO::getBoxName, hardwareXmlVO::setBoxName, NOT_ASSIGNED);
        isEmptyAndBack(hardwareXmlVO::getName, "name");
        isEmptyAndBack(hardwareXmlVO::getMaterial, "material");
        isEmptyAndBack(hardwareXmlVO::getColor, FIELD_COLOR);
        isEmptyBackAndPositiveNumber(hardwareXmlVO::getCount, FIELD_COUNT, Double.class);
        isEmptyAndBack(hardwareXmlVO::getUnitCount, FIELD_UNITCOUNT);
        isEmptyAndBack(hardwareXmlVO::getUnit, "unit");
    }

    /**
     * Hardware标签结束
     */
    private void endHardware() {
        // 设置全局参数
        partItemCode = null;
    }

    /**
     * 解析xml中的组件列表
     *
     * @param attributes
     * @throws Exception
     */
    private void analyzeAssemblieXml(Iterator<Attribute> attributes) throws Exception {
        // 从标签属性生成assembly对象
        AssemblyXmlVO assemblyXmlVO = getJavaBeanFromXmlAttr(AssemblyXmlVO.class, attributes);
        if (ObjectUtil.isNull(assemblyXmlVO)) {
            assemblyXmlVO = new AssemblyXmlVO();
        }
        // 校验组件
        checkAssemblie(assemblyXmlVO);

        // 如果有未命名的房间编号、柜体编号，直接在临时表创建这个柜体
        String roomCode = assemblyXmlVO.getRoomCode();
        String boxCode = assemblyXmlVO.getBoxCode();
        String groupCodes = assemblyXmlVO.getGroupCode();
        // 校验检查房间、柜体、加工组编号
        checkRoomBoxGroup(se, roomCode, boxCode, groupCodes);

        // 设置全局参数
        String itemCode = assemblyXmlVO.getItemCode();
        partItemCode = StringUtils.isNotEmpty(itemCode) ? itemCode : assemblyXmlVO.getManufacturer() + assemblyXmlVO.getBrand() + assemblyXmlVO.getName() + assemblyXmlVO.getMaterial() +
                assemblyXmlVO.getSpecs() + assemblyXmlVO.getColor() + assemblyXmlVO.getType();
        assemblyXmlVO.setItemCode(partItemCode);

        // 添加到临时表
        cachedDataList.add(OrderImportTempDataDO.builder()
                .id((Long) snowFlakeGenerator.nextId(null))
                .orderId(orderId)
                .taskId(taskId)
                .type(OrderImportTmpDataTypeEnum.PART_HARDWARE.getType())
                .outerId(partItemCode)
                .roomCode(roomCode)
                .bodyCode(boxCode)
                .groupCode(groupCodes)
                .createTime(LocalDateTime.now())
                .detail(JsonUtils.zipString(JSON.toJSONString(assemblyXmlVO)))
                .num(NumberUtil.parseDouble(assemblyXmlVO.getCount()))
                .sort(sort++)
                .build());

    }

    /**
     * 校验组件
     *
     * @param assemblyXmlVO
     */
    private void checkAssemblie(AssemblyXmlVO assemblyXmlVO) {
        isEmptyAndBack(assemblyXmlVO::getName, "name");
        isEmptyAndBack(assemblyXmlVO::getMaterial, "name");
        isEmptyAndBack(assemblyXmlVO::getColor, FIELD_COLOR);
        isEmptyBackAndPositiveNumber(assemblyXmlVO::getCount, FIELD_COUNT, Double.class);
        isEmptyAndBack(assemblyXmlVO::getUnitCount, FIELD_UNITCOUNT);
        isEmptyAndAdd(assemblyXmlVO::getRoomCode, assemblyXmlVO::setRoomCode, NOT_ASSIGNED_CODE);
        isEmptyAndAdd(assemblyXmlVO::getRoomName, assemblyXmlVO::setRoomName, NOT_ASSIGNED);
        isEmptyAndAdd(assemblyXmlVO::getBoxCode, assemblyXmlVO::setBoxCode, NOT_ASSIGNED_CODE);
        isEmptyAndAdd(assemblyXmlVO::getBoxName, assemblyXmlVO::setBoxName, NOT_ASSIGNED);
    }

    /**
     * Assembly标签结束
     */
    private void endAssemblie() {
        // 设置全局参数
        partItemCode = null;
    }

    /**
     * 解析blockxml的成品轮廓信息
     *
     * @param attributes
     * @throws Exception
     */
    private void analyzeOutline(Iterator<Attribute> attributes) throws Exception {
        // 设置全局参数
        inOutline = true;
        if (inGroove) {
            grooveOutlineXmlVO = getJavaBeanFromXmlAttr(OutlineXmlVO.class, attributes);
        } else {
            outlineXmlVO = getJavaBeanFromXmlAttr(OutlineXmlVO.class, attributes);
        }
    }

    /**
     * Outline标签结束
     */
    private void endOutline() {
        // 校验point数量
        if (inGroove) {
            if (CollUtil.isEmpty(points)) {
                throw new ServiceException(ORDER_IMPORT_DETAIL_VALID_ERROR, "原始造型(外)轮廓点阵列表不能为空");
            }
            grooveOutlineXmlVO.setPointXmlVOList(ListUtil.toList(points));
        } else {
            outlineXmlVO.setPointXmlVOList(ListUtil.toList(points));
        }
        // 重置全局参数
        points.clear();
        inOutline = false;
    }

    /**
     * 解析blockxml的开料轮廓信息
     *
     * @param attributes
     * @throws Exception
     */
    private void analyzeCuttingOutline(Iterator<Attribute> attributes) throws Exception {
        // 设置全局参数
        inCuttingOutline = true;
        // 从标签属性生成cuttingoutline对象
        cuttingOutlineXmlVO = getJavaBeanFromXmlAttr(CuttingOutlineXmlVO.class, attributes);
        // 校验
        isEmptyBackAndNumber(cuttingOutlineXmlVO::getReferenceOffsetX, Double.class, "referenceOffsetX");
        isEmptyBackAndNumber(cuttingOutlineXmlVO::getReferenceOffsetY, Double.class, "referenceOffsetY");
    }

    /**
     * CuttingOutline标签结束
     */
    private void endCuttingOutline() {
        cuttingOutlineXmlVO.setPointXmlVOList(ListUtil.toList(points));
        // 重置全局参数
        points.clear();
        inCuttingOutline = false;
    }

    /**
     * 解析blockxml的开料轮廓信息
     *
     * @param attributes
     * @throws Exception
     */
    private void analyzeRawOutline(Iterator<Attribute> attributes) throws Exception {
        // 设置全局参数
        inRawOutline = true;
        // 从标签属性生成rawoutline对象
        rawOutlineXmlVO = getJavaBeanFromXmlAttr(RawOutlineXmlVO.class, attributes);
    }

    /**
     * RawOutline标签结束
     */
    private void endRawOutline() {
        if (CollUtil.isNotEmpty(points) && ObjectUtil.isNotNull(rawOutlineXmlVO)) {
            isEmptyBackAndNumber(rawOutlineXmlVO::getReferenceOffsetX, Double.class, "referenceOffsetX" + rawOutlineXmlVO.getReferenceOffsetX());
            isEmptyBackAndNumber(rawOutlineXmlVO::getReferenceOffsetY, Double.class, "referenceOffsetY" + rawOutlineXmlVO.getReferenceOffsetY());
        }
        if (ObjectUtil.isNotNull(rawOutlineXmlVO)) {
            rawOutlineXmlVO.setPointXmlVOList(ListUtil.toList(points));
        }
        // 重置全局参数
        inRawOutline = false;
        points.clear();
    }

    /**
     * 解析blockxml的孔列表
     *
     * @param attributes
     * @throws Exception
     */
    private void analyzeHoles(Iterator<Attribute> attributes) throws Exception {
        // 从标签属性生成hole对象
        holeXmlVOS = getJavaBeanFromXmlAttr(HoleXmlVOS.class, attributes);
        if (ObjectUtil.isNull(holeXmlVOS)) {
            return;
        }
        // 校验holes
        checkHoles(holeXmlVOS);
        holeXmlVOS.setHoleXmlVOList(new ArrayList<>());
    }

    /**
     * 校验孔列表
     *
     * @param holeXmlVOS
     */
    private void checkHoles(HoleXmlVOS holeXmlVOS) {
        isEmptyAddAndPositiveNumber(holeXmlVOS::getCount, holeXmlVOS::setCount, "0", Integer.class, "count " + holeXmlVOS.getCount());
        isEmptyAddAndPositiveNumber(holeXmlVOS::getCountFaceA, holeXmlVOS::setCountFaceA, "0", Integer.class, "countFaceA " + holeXmlVOS.getCountFaceA());
        isEmptyAddAndPositiveNumber(holeXmlVOS::getCountFaceB, holeXmlVOS::setCountFaceB, "0", Integer.class, "countFaceB " + holeXmlVOS.getCountFaceB());
        isEmptyAddAndPositiveNumber(holeXmlVOS::getCountSide, holeXmlVOS::setCountSide, "0", Integer.class, "countSide " + holeXmlVOS.getCountSide());
        isEmptyAddAndPositiveNumber(holeXmlVOS::getCountSideL, holeXmlVOS::setCountSideL, "0", Integer.class, "countSideL " + holeXmlVOS.getCountSideL());
        isEmptyAddAndPositiveNumber(holeXmlVOS::getCountSideR, holeXmlVOS::setCountSideR, "0", Integer.class, "countSideR " + holeXmlVOS.getCountSideR());
        isEmptyAddAndPositiveNumber(holeXmlVOS::getCountSideU, holeXmlVOS::setCountSideU, "0", Integer.class, "countSideU " + holeXmlVOS.getCountSideU());
        isEmptyAddAndPositiveNumber(holeXmlVOS::getCountSideD, holeXmlVOS::setCountSideD, "0", Integer.class, "countSideD " + holeXmlVOS.getCountSideD());
        isEmptyAddAndPositiveNumber(holeXmlVOS::getCountBevelled, holeXmlVOS::setCountBevelled, "0", Integer.class, "countBevelled " + holeXmlVOS.getCountBevelled());
        isEmptyAddAndPositiveNumber(holeXmlVOS::getCountLocking, holeXmlVOS::setCountLocking, "0", Integer.class, "countLocking " + holeXmlVOS.getCountLocking());
        isEmptyAddAndPositiveNumber(holeXmlVOS::getCountBuildIn, holeXmlVOS::setCountBuildIn, "0", Integer.class, "countBuildIn " + holeXmlVOS.getCountBuildIn());
        isEmptyAddAndPositiveNumber(holeXmlVOS::getCountHinge, holeXmlVOS::setCountHinge, "0", Integer.class, "countHinge " + holeXmlVOS.getCountHinge());
        isEmptyAddAndPositiveNumber(holeXmlVOS::getCountThrough, holeXmlVOS::setCountThrough, "0", Integer.class, "countThrough " + holeXmlVOS.getCountThrough());
    }

    /**
     * 解析孔明细
     *
     * @param attributes
     * @throws Exception
     */
    private void analyzeHole(Iterator<Attribute> attributes) throws Exception {
        // 从标签属性生成hole对象
        HoleXmlVO holeXmlVO = getJavaBeanFromXmlAttr(HoleXmlVO.class, attributes);
        if (ObjectUtil.isNull(holeXmlVO)) {
            return;
        }
        // 校验holes
        checkHole(holeXmlVO);
        holeXmlVOS.getHoleXmlVOList().add(holeXmlVO);
    }

    /**
     * 校验孔明细
     *
     * @param holeXmlVO
     */
    private void checkHole(HoleXmlVO holeXmlVO) {
        isEmptyBackAndInvalid(holeXmlVO::getDirection, DictTypeConstants.HOLE_DIRECTION, "direction ");
        isEmptyBackAndPositiveNumber(holeXmlVO::getDiameter, "diameter", Double.class);
        if (com.alibaba.nacos.common.utils.StringUtils.isNotEmpty(holeXmlVO.getType()) && holeXmlVO.getType().equals("沉头孔")) {
            isEmptyBackAndPositiveNumber(holeXmlVO::getDiameterInner, "当小板孔类型为沉头孔时，小板孔沉头孔内径", Double.class);
            isEmptyBackAndPositiveNumber(holeXmlVO::getDepthInner, "当小板孔类型为沉头孔时，沉头孔的深度", Double.class);
        }
        isEmptyBackAndPositiveNumber(holeXmlVO::getDepth, FIELD_DEPTH, Double.class);
        notEmptyPositiveNumber(holeXmlVO::getGroupNumber, "groupNumber", Integer.class);
        isEmptyAddAndNumber(holeXmlVO::getGroupXoffset, holeXmlVO::setGroupXoffset, "0", Double.class, "groupXoffset");
        isEmptyAddAndNumber(holeXmlVO::getGroupYoffset, holeXmlVO::setGroupYoffset, "0", Double.class, "groupYoffset");
        isEmptyAddAndNumber(holeXmlVO::getGroupZoffset, holeXmlVO::setGroupZoffset, "0", Double.class, "groupZoffset");
        isEmptyBackAndNumber(holeXmlVO::getStartX, Double.class, "startX");
        isEmptyBackAndNumber(holeXmlVO::getStartY, Double.class, "startY");
        isEmptyBackAndNumber(holeXmlVO::getStartZ, Double.class, "startZ");
        isEmptyBackAndNumber(holeXmlVO::getEndX, Double.class, "endX");
        isEmptyBackAndNumber(holeXmlVO::getEndY, Double.class, "endY");
        isEmptyBackAndNumber(holeXmlVO::getEndZ, Double.class, "endZ");
        notEmptyPositiveNumber(holeXmlVO::getToolStartRotateX, "toolStartRotateX", Double.class);
        notEmptyPositiveNumber(holeXmlVO::getToolStartRotateY, "toolStartRotateY", Double.class);
        notEmptyPositiveNumber(holeXmlVO::getToolStartRotateZ, "toolStartRotateZ", Double.class);
        notEmptyPositiveNumber(holeXmlVO::getToolEndRotateX, "toolEndRotateX", Double.class);
        notEmptyPositiveNumber(holeXmlVO::getToolEndRotateY, "toolEndRotateY", Double.class);
        notEmptyPositiveNumber(holeXmlVO::getToolEndRotateZ, "toolEndRotateZ", Double.class);
    }

    /**
     * 解析造型列表
     *
     * @param attributes
     * @throws Exception
     */
    private void analyzeGrooves(Iterator<Attribute> attributes) throws Exception {
        // 从标签属性生成grooves对象
        grooveXmlVOS = getJavaBeanFromXmlAttr(GrooveXmlVOS.class, attributes);
        if (ObjectUtil.isNotNull(grooveXmlVOS)) {
            // 校验grooves
            checkGooves(grooveXmlVOS);
            grooveXmlVOS.setGrooveXmlVOList(new ArrayList<>());
        }
    }

    /**
     * 校验造型列表
     *
     * @param grooveXmlVOS
     */
    private void checkGooves(GrooveXmlVOS grooveXmlVOS) {
        isEmptyAddAndPositiveNumber(grooveXmlVOS::getCount, grooveXmlVOS::setCount, "0", Double.class, FIELD_COUNT);
        isEmptyAddAndPositiveNumber(grooveXmlVOS::getCountFaceA, grooveXmlVOS::setCountFaceA, "0", Double.class, "countFaceA");
        isEmptyAddAndPositiveNumber(grooveXmlVOS::getCountFaceB, grooveXmlVOS::setCountFaceB, "0", Double.class, "countFaceB");
        isEmptyAddAndPositiveNumber(grooveXmlVOS::getCountSide, grooveXmlVOS::setCountSide, "0", Double.class, "countSide");
        isEmptyAddAndPositiveNumber(grooveXmlVOS::getCountSideL, grooveXmlVOS::setCountSideL, "0", Double.class, "countSideL");
        isEmptyAddAndPositiveNumber(grooveXmlVOS::getCountSideR, grooveXmlVOS::setCountSideR, "0", Double.class, "countSideR");
        isEmptyAddAndPositiveNumber(grooveXmlVOS::getCountSideU, grooveXmlVOS::setCountSideU, "0", Double.class, "countSideU");
        isEmptyAddAndPositiveNumber(grooveXmlVOS::getCountSideD, grooveXmlVOS::setCountSideD, "0", Double.class, "countSideD");
        isEmptyAddAndPositiveNumber(grooveXmlVOS::getCountBevelled, grooveXmlVOS::setCountBevelled, "0", Double.class, "countBevelled");
        isEmptyAddAndPositiveNumber(grooveXmlVOS::getCountRectangle, grooveXmlVOS::setCountRectangle, "0", Double.class, "countRectangle");
        isEmptyAddAndPositiveNumber(grooveXmlVOS::getCountThrough, grooveXmlVOS::setCountThrough, "0", Double.class, "countThrough");
        isEmptyAddAndPositiveNumber(grooveXmlVOS::getCount2V, grooveXmlVOS::setCount2V, "0", Double.class, "count2V");
        isEmptyAddAndPositiveNumber(grooveXmlVOS::getCount3D, grooveXmlVOS::setCount3D, "0", Double.class, "count3D");
    }

    /**
     * 解析造型
     *
     * @param attributes
     * @throws Exception
     */
    private void analyzeGroove(Iterator<Attribute> attributes) throws Exception {
        // 设置全局参数
        inGroove = true;
        // 从标签属性生成groove对象
        grooveXmlVO = getJavaBeanFromXmlAttr(GrooveXmlVO.class, attributes);
        if (ObjectUtil.isNotNull(grooveXmlVO)) {
            // 校验groove
            checkGroove(grooveXmlVO);
        }
    }

    /**
     * Groove标签结束
     */
    private void endGroove() {
        grooveXmlVO.setOutlineXmlVO(grooveOutlineXmlVO);
        grooveXmlVO.setPointXmlVOS(groovePointXmlVOS);
        grooveXmlVOS.getGrooveXmlVOList().add(grooveXmlVO);
        // 重置全局参数
        inGroove = false;
    }

    /**
     * 校验造型明细
     *
     * @param grooveXmlVO
     */
    private void checkGroove(GrooveXmlVO grooveXmlVO) {
        isEmptyAndBack(grooveXmlVO::getId, "id");

        // 父孤岛id
        isEmptyAndBack(grooveXmlVO::getType, "type");
        isEmptyBackAndInvalid(grooveXmlVO::getFace, DictTypeConstants.HOLE_FACE, "face");

        if (grooveXmlVO.getType().trim().equals("矩形槽")) {
            isEmptyAndBack(grooveXmlVO::getWidth, "当槽为矩形时，槽宽度");
        }

        isEmptyAddAndPositiveNumber(grooveXmlVO::getWidth, grooveXmlVO::setWidth, "0", Double.class, FIELD_WIDTH);
        isEmptyAddAndPositiveNumber(grooveXmlVO::getLength, grooveXmlVO::setLength, "0", Double.class, FIELD_LENGTH);
        isEmptyBackAndPositiveNumber(grooveXmlVO::getDepth, FIELD_DEPTH, Double.class);
        isEmptyAddAndPositiveNumber(grooveXmlVO::getWidthExtend, grooveXmlVO::setWidthExtend, "0", Double.class, "widthExtend");
        isEmptyAddAndPositiveNumber(grooveXmlVO::getLengthExtend, grooveXmlVO::setLengthExtend, "0", Double.class, "lengthExtend");
        isEmptyAddAndPositiveNumber(grooveXmlVO::getDepthExtend, grooveXmlVO::setDepthExtend, "0", Double.class, "depthExtend");
        isEmptyAddAndPositiveNumber(grooveXmlVO::getDirection, grooveXmlVO::setDirection, "0", Double.class, "direction");
        isEmptyAddAndPositiveNumber(grooveXmlVO::getMode, grooveXmlVO::setMode, "0", Double.class, "mode");
        isEmptyAddAndPositiveNumber(grooveXmlVO::getRound, grooveXmlVO::setRound, "0", Double.class, "round");

        if (grooveXmlVO.getType().trim().equals("路径槽") || grooveXmlVO.getType().trim().equals("折返路径槽")) {
            isEmptyAndBack(grooveXmlVO::getToolDiameter, "当槽为路径槽或是折返路径槽时，刀直径");

        }
        notEmptyPositiveNumber(grooveXmlVO::getToolDiameter, "toolDiameter", Double.class);
        isEmptyAddAndPositiveNumber(grooveXmlVO::getRedundance, grooveXmlVO::setRedundance, "0", Double.class, "redundance");
        isEmptyAddAndPositiveNumber(grooveXmlVO::getTangentAngle, grooveXmlVO::setTangentAngle, "0", Double.class, "tangentAngle");
        isEmptyAddAndPositiveNumber(grooveXmlVO::getIsletCount, grooveXmlVO::setIsletCount, "0", Integer.class, "isletCount");
    }

    /**
     * 解析造型店镇道路列表
     *
     * @param attributes
     * @throws Exception
     */
    private void analyzePoints(Iterator<Attribute> attributes) throws Exception {
        // 设置全局参数
        inPoints = true;
        // 从标签属性生成points对象
        if (inGroove) {
            groovePointXmlVOS = getJavaBeanFromXmlAttr(PointXmlVOS.class, attributes);
        } else {
            pointXmlVOS = getJavaBeanFromXmlAttr(PointXmlVOS.class, attributes);
        }
    }

    /**
     * Points标签结束
     */
    private void endPoints() {
        // 校验point数量
        if (ObjectUtil.isNotNull(pointXmlVOS) && CollUtil.isEmpty(points)) {
            throw new ServiceException(ORDER_IMPORT_DETAIL_VALID_ERROR, inIslet ? "孤岛点阵列表不可为空" : "槽点坐标列表不可为空");
        }
        if (inGroove) {
            groovePointXmlVOS.setPointXmlVOList(ListUtil.toList(points));
        } else {
            pointXmlVOS.setPointXmlVOList(ListUtil.toList(points));
        }
        // 重置全局参数
        points.clear();
        inPoints = false;
    }

    /**
     * 解析偏移刀路
     *
     * @param attributes
     * @throws Exception
     */
    private void analyzeOffsetLines(Iterator<Attribute> attributes) throws Exception {
        // 从标签属性生成offsetLines对象
        offsetLineXmlVOS = getJavaBeanFromXmlAttr(OffsetLineXmlVOS.class, attributes);
        if (ObjectUtil.isNotNull(offsetLineXmlVOS)) {
            offsetLineXmlVOS.setOffsetLineXmlVOList(new ArrayList<>());
        }
    }

    /**
     * 解析偏移刀路
     *
     * @param attributes
     * @throws Exception
     */
    private void analyzeOffsetLine(Iterator<Attribute> attributes) throws Exception {
        // 从标签属性生成OffsetLine对象
        OffsetLineXmlVO offsetLineXmlVO = getJavaBeanFromXmlAttr(OffsetLineXmlVO.class, attributes);
        if (ObjectUtil.isNotNull(offsetLineXmlVO)) {
            // 校验OffsetLine
            checkOffsetLine(offsetLineXmlVO);
            offsetLineXmlVOS.getOffsetLineXmlVOList().add(offsetLineXmlVO);
        }
    }

    /**
     * 校验偏移刀路
     *
     * @param offsetLineXmlVO
     */
    private void checkOffsetLine(OffsetLineXmlVO offsetLineXmlVO) {
        isEmptyBackAndNumber(offsetLineXmlVO::getOffset, Double.class, "offset");
        isEmptyBackAndPositiveNumber(offsetLineXmlVO::getDepth, FIELD_DEPTH, Double.class);
        isEmptyBackAndPositiveNumber(offsetLineXmlVO::getToolDiameter, "toolDiameter", Double.class);
        isEmptyAddAndPositiveNumber(offsetLineXmlVO::getTangentAngle, offsetLineXmlVO::setTangentAngle, "0", Double.class, "tangentAngle");
    }

    /**
     * 解析孤岛
     *
     * @param attributes
     * @throws Exception
     */
    private void analyzeIslets(Iterator<Attribute> attributes) throws Exception {
        // 设置全局参数
        inIslet = true;
        // 从标签属性生成grooves对象
        isletXmlVOS = getJavaBeanFromXmlAttr(IsletXmlVOS.class, attributes);
        if (ObjectUtil.isNotNull(isletXmlVOS)) {
            isletXmlVOS.setIsletXmlVOList(new ArrayList<>());
        }
    }

    /**
     * 解析孤岛
     *
     * @param attributes
     * @throws Exception
     */
    private void analyzeIslet(Iterator<Attribute> attributes) throws Exception {
        // 设置全局参数
        inIslet = true;
        // 从标签属性生成grooves对象
        isletXmlVO = getJavaBeanFromXmlAttr(IsletXmlVO.class, attributes);
        if (ObjectUtil.isNotNull(isletXmlVO)) {
            isEmptyAndBack(isletXmlVO::getId, "孤岛ID");
        }
    }

    /**
     * Islet标签结束
     */
    private void endIslet() {
        if (ObjectUtil.isNotNull(isletXmlVO)) {
            isletXmlVO.setPointXmlVOS(pointXmlVOS);
            isletXmlVOS.getIsletXmlVOList().add(isletXmlVO);
        }
        // 重置全局参数
        points.clear();
        inIslet = false;
    }

    /**
     * Plates标签结束
     */
    private void endPlates() {
        for (Map.Entry<String, OrderImportTempDataDO> entry : existGoodsMap.entrySet()) {
            cachedDataList.add(entry.getValue());
        }
    }

    /**
     * 解析关联配件
     *
     * @param attributes
     * @throws Exception
     */
    private void analyzeCorrelations(Iterator<Attribute> attributes) throws Exception {
        // 从标签属性生成correlation对象
        correlationXmlVOS = getJavaBeanFromXmlAttr(CorrelationXmlVOS.class, attributes);
        if (ObjectUtil.isNotNull(correlationXmlVOS)) {
            correlationXmlVOS.setCorrelationXmlVOList(new ArrayList<>());
        }

    }

    /**
     * 解析关联配件
     *
     * @param attributes
     * @throws Exception
     */
    private void analyzeCorrelation(Iterator<Attribute> attributes) throws Exception {
        // 从标签属性生成correlation对象
        CorrelationXmlVO correlationXmlVO = getJavaBeanFromXmlAttr(CorrelationXmlVO.class, attributes);
        if (ObjectUtil.isNotNull(correlationXmlVO)) {
            if (StringUtils.isEmpty(correlationXmlVO.getItemCode())) {
                isEmptyAndBack(correlationXmlVO::getName, "name");
            }
            isEmptyBackAndPositiveNumber(correlationXmlVO::getCount, FIELD_COUNT, Integer.class);
            correlationXmlVOS.getCorrelationXmlVOList().add(correlationXmlVO);
        }
    }

    List<PointXmlVO> points = new ArrayList<>();

    /**
     * 解析点明细
     *
     * @param attributes
     * @throws Exception
     */
    private void analyzePoint(Iterator<Attribute> attributes) throws Exception {
        // 从标签属性生成point对象
        PointXmlVO pointXmlVO = getJavaBeanFromXmlAttr(PointXmlVO.class, attributes);
        if (ObjectUtil.isNull(pointXmlVO)) {
            return;
        }

        if (inOutline) {
            if (inGroove) {
                checkPoint(pointXmlVO, "原始造型(外)轮廓");
            } else {
                checkOutlinePoint(pointXmlVO);
            }
        }
        if (inCuttingOutline) {
            checkPointRemoveZ(pointXmlVO, "开料成型轮廓信息");
        }
        if (inRawOutline) {
            checkPointRemoveZ(pointXmlVO, "坯料轮廓信息");
        }
        if (inPoints) {
            if (inIslet) {
                checkPointRemoveZ(pointXmlVO, "孤岛");
            } else {
                checkPoint(pointXmlVO, "刀路点阵");
            }
        }
        points.add(pointXmlVO);
    }

    /**
     * 校验点明细
     *
     * @param pointXmlVO
     * @param name
     */
    private void checkPoint(PointXmlVO pointXmlVO, String name) {
        checkPointRemoveZ(pointXmlVO, name);
        isEmptyBackAndNumber(pointXmlVO::getPointZ, Double.class, name + "pointZ");
    }

    /**
     * 解析备注
     *
     * @param attributes
     * @throws Exception
     */
    private void analyzeRemarks(Iterator<Attribute> attributes) throws Exception {
        // 从标签属性生成correlation对象
        remarkXmlVOS = new RemarkXmlVOS();
        remarkXmlVOS.setRemarkXmlVOList(new ArrayList<>());
    }

    /**
     * 解析备注
     *
     * @param attributes
     * @throws Exception
     */
    private void analyzeRemark(Iterator<Attribute> attributes) throws Exception {
        // 从标签属性生成correlation对象
        RemarkXmlVO remarkXmlVO = getJavaBeanFromXmlAttr(RemarkXmlVO.class, attributes);
        remarkXmlVOS.getRemarkXmlVOList().add(remarkXmlVO);
    }

    /**
     * 校验成品轮廓
     *
     * @param pointXmlVO
     */
    private void checkOutlinePoint(PointXmlVO pointXmlVO) {
        isEmptyBackAndNumber(pointXmlVO::getPointX, Double.class, "pointX");
        isEmptyBackAndNumber(pointXmlVO::getPointY, Double.class, "pointY");
        isEmptyBackAndNumber(pointXmlVO::getCurvature, Double.class, "curvature");
        isEmptyBackAndNumber(pointXmlVO::getRadius, Double.class, "radius");

        checkEdging(pointXmlVO);

        isEmptyAddAndPositiveNumber(pointXmlVO::getPreMilling, pointXmlVO::setPreMilling, "0", Double.class, "预铣/精修量");
        isEmptyAddAndNumber(pointXmlVO::getScale, pointXmlVO::setScale, "0", Double.class, "延展/收缩量");
        isEmptyAddAndPositiveNumber(pointXmlVO::getCount, pointXmlVO::setCount, "0", Double.class, "单边封边尺寸");
        isEmptyAddAndPositiveNumber(pointXmlVO::getCountUnit, pointXmlVO::setCountUnit, "m", String.class, "封边条长度单位");
    }

    private void checkPointRemoveZ(PointXmlVO pointXmlVO, String name) {
        isEmptyBackAndNumber(pointXmlVO::getPointX, Double.class, "pointX");
        isEmptyBackAndNumber(pointXmlVO::getPointY, Double.class, "pointY");
        isEmptyBackAndNumber(pointXmlVO::getCurvature, Double.class, "curvature");
        isEmptyBackAndNumber(pointXmlVO::getRadius, Double.class, "radius");
    }

    /**
     * 校验板件中的封边条
     *
     * @param pointXmlVO
     */
    public void checkEdging(PointXmlVO pointXmlVO) {
        if (!(StringUtils.isEmpty(pointXmlVO.getItemCode()) && StringUtils.isEmpty(pointXmlVO.getEdgingName()) &&
                StringUtils.isEmpty(pointXmlVO.getEdgingColor()) && StringUtils.isEmpty(pointXmlVO.getEdgingMaterial()) &&
                (StringUtils.isEmpty(pointXmlVO.getEdgingThickness()) || pointXmlVO.getEdgingThickness().trim().equals("0")))) {
            if (StringUtils.isEmpty(pointXmlVO.getEdgingName())) {
                pointXmlVO.setEdgingName("封边条");
            }
            if (StringUtils.isEmpty(pointXmlVO.getItemCode())) {
                if (StringUtils.isEmpty(pointXmlVO.getEdgingColor()) || StringUtils.isEmpty(pointXmlVO.getEdgingMaterial())) {
                    throw new ServiceException(ORDER_IMPORT_DETAIL_VALID_ERROR, "当封边条没有物料编码时，封边条颜色、材质不可为空");
                }
            }
            isEmptyAddAndPositiveNumber(pointXmlVO::getEdgingThickness, pointXmlVO::setEdgingThickness, "0", Double.class, "edgingThickness");
            isEmptyAddAndPositiveNumber(pointXmlVO::getEdgingExtend, pointXmlVO::setEdgingExtend, "0", Double.class, "edgingExtend");
        }
    }

    /**
     * 校验订单信息
     *
     * @param orderXmlVO
     */
    public void validOrderInfo(OrderXmlVO orderXmlVO) {
        // 自定义单号校验
        validCustomOrderNo(FIELD_ORDERCODE, orderXmlVO.getOrderCode());
        // 校验非空
        validOrderInfoEmpty(FIELD_ORDERCODE, orderXmlVO.getOrderCode());
        validOrderInfoEmpty("customer", orderXmlVO.getCustomer());
        validOrderInfoEmpty("dealer", orderXmlVO.getDealer());
        validOrderInfoEmpty("customerAddress", orderXmlVO.getCustomerAddress());
        validOrderInfoEmpty("customerPhone", orderXmlVO.getCustomerPhone());
        validOrderInfoEmpty("deliveryDate", orderXmlVO.getDeliveryDate());

        // 长度校验
        validOrderInfoLength(FIELD_ORDERCODE, orderXmlVO.getOrderCode(), 32);
        validOrderInfoLength("customer", orderXmlVO.getCustomer(), 255);
        validOrderInfoLength("dealer", orderXmlVO.getDealer(), 255);
        validOrderInfoLength("customerAddress", orderXmlVO.getCustomerAddress(), 255);
        validOrderInfoLength("seller", orderXmlVO.getSeller(), 64);
        validOrderInfoLength("memo", orderXmlVO.getMemo(), 255);

        // 校验手机号
        validatePhoneNumbers(orderXmlVO);

        LocalDate orderDate;
        LocalDate deliverDate;
        // 校验订单日期、出货日期
        String xmlOrderData = orderXmlVO.getOrderDate();
        if (ObjectUtil.isNull(xmlOrderData)) {
            orderDate = LocalDate.now();
        } else {
            orderDate = validOrderInfoDate("orderDate", orderXmlVO.getOrderDate());
        }

        String xmlDeliveryDate = orderXmlVO.getDeliveryDate();
        if (ObjectUtil.isNull(xmlDeliveryDate)) {
            deliverDate = orderDate.plus(30, ChronoUnit.DAYS);
        } else {
            deliverDate = validOrderInfoDate("deliveryDate", xmlDeliveryDate);
            // 出货时间早于导入时间，自动延长30天
            if (orderDate != null && deliverDate.isBefore(orderDate)) {
                deliverDate = orderDate.plus(30, ChronoUnit.DAYS);
            }
        }
        orderDO = OrderDO.builder()
                .dataType(DataTypeEnum.FILE_IMPORT.getStatus())
                .customer(orderXmlVO.getCustomer())
                .customOrderNo(orderXmlVO.getOrderCode())
                .dealer(orderXmlVO.getDealer())
                .dealerPhoneNumber(orderXmlVO.getDealerPhone())
                .address(orderXmlVO.getCustomerAddress())
                .phoneNumber(orderXmlVO.getCustomerPhone())
                .salesman(orderXmlVO.getSeller())
                .splitter(orderXmlVO.getAnalyst())
                .remark(orderXmlVO.getMemo())
                .orderDate(orderDate.atStartOfDay())
                .deliveryDate(deliverDate.atStartOfDay())
                .orderType(OrderTypeEnum.MAIN_ORDER.getStatus())
                .organId(OrganContextHolder.getOrganId())
                .build();
        String loginUserName = WebFrameworkUtils.getLoginUserName();
        LocalDateTime now = LocalDateTime.now();
        orderDO.setCreator(loginUserName);
        orderDO.setUpdater(loginUserName);
        orderDO.setCreateTime(now);
        orderDO.setUpdateTime(now);
    }

    /**
     * 订单数据保存临时表
     */
    private void saveOrderTempData() {
        OrderImportTempDataDO orderInfoTempDataDO = OrderImportTempDataDO.builder()
                .id((Long) snowFlakeGenerator.nextId(null))
                .orderId(orderId)
                .taskId(taskId)
                .type(OrderImportTmpDataTypeEnum.ORDER.getType())
                .createTime(LocalDateTime.now())
                .sort(0)
                .build();
        // 非补板即新增生成单，记录基础信息
        if (!addToExistsOrder) {
            orderDO.setId(orderId);
            orderDO.setStatus(NumberUtil.compare(sort, 1) > 0 ? OrderStatusEnum.NEW_ORDER.getStatus() : OrderStatusEnum.EMPTY.getStatus());
            orderInfoTempDataDO.setDetail(JsonUtils.zipString(JSON.toJSONString(orderDO)));
        }
        orderImportTempDataMapper.insert(orderInfoTempDataDO);
        log.info("xml导入临时订单数据保存成功！");
    }

    /**
     * 校验/缓存检查房间、柜体、加工组编码
     *
     * @param se
     * @param roomCode
     * @param boxCode
     * @param groupCodes
     */
    private void checkRoomBoxGroup(StartElement se, String roomCode, String boxCode, String groupCodes) {
        boolean roomEmpty = StringUtil.equals(roomCode, NOT_ASSIGNED_CODE);
        boolean bodyEmpty = StringUtil.equals(boxCode, NOT_ASSIGNED_CODE);
        String roomBodyKey = roomCode + boxCode;

        RoomBoxXmlVO roomBoxXmlVO = roomBodyMap.get(roomBodyKey);
        RoomXmlVO roomXml = roomMap.get(roomCode);
        // 板件列表存在柜体没有缓存的
        if (ObjectUtil.isNull(roomBoxXmlVO)) {

            // 检查roomCode
            if (!bodyEmpty) {
                throw new ServiceException(ORDER_IMPORT_BODYCODE_NOT_EXIST_VALID_ERROR, se, boxCode);
            }

            String roomName = NOT_ASSIGNED;
            String boxName = NOT_ASSIGNED;
            // 房间缓存了取房间名
            if (ObjectUtil.isNotNull(roomXml)) {
                roomName = roomXml.getName();
            } else {
                // 检查roomCode
                if (!roomEmpty) {
                    throw new ServiceException(ORDER_IMPORT_ROOMCODE_NOT_EXIST_VALID_ERROR, se, roomCode);
                }
            }

            cachedDataList.add(OrderImportTempDataDO.builder()
                    .id((Long) snowFlakeGenerator.nextId(null))
                    .orderId(orderId)
                    .taskId(taskId)
                    .type(OrderImportTmpDataTypeEnum.BODY.getType())
                    .roomCode(roomCode)
                    .roomName(roomName)
                    .bodyCode(boxCode)
                    .bodyName(boxName)
                    .createTime(LocalDateTime.now())
                    .sort(sort++)
                    .area(BigDecimal.ZERO)
                    .build());

            roomBodyMap.put(roomBodyKey, RoomBoxXmlVO.builder()
                    .code(roomCode)
                    .name(roomName)
                    .build());

        }

//        // 校验板件的房间、柜体、加工组编号
//        Set<String> bodyCodeSet = roomBodyCodeMap.get(roomCode);
//        if (ObjectUtil.isNull(bodyCodeSet)) {
//            // 房间不存在roomCode且不是未命名房间的抛出提示
//            if (!StringUtils.equals(NOT_ASSIGNED_CODE, roomCode)) {
//
//            }
//        } else if (CollUtil.isNotEmpty(bodyCodeSet)) {
//            // 房间下柜体没有该boxCode且不是未命名柜体的抛出提示
//            if (!bodyCodeSet.contains(boxCode) && !StringUtils.equals(NOT_ASSIGNED_CODE, boxCode)) {
//
//            }
//        }

        if (StringUtils.isNotEmpty(groupCodes) && !groupCodeSet.contains(groupCodes)) {
            throw new ServiceException(ORDER_IMPORT_GROUPCODE_NOT_EXIST_VALID_ERROR, se, groupCodes);
        }
    }

    /**
     * 校验手机号
     *
     * @param orderXmlVO
     */
    private void validatePhoneNumbers(OrderXmlVO orderXmlVO) {
        validOrderInfoPhone("customerPhone", orderXmlVO.getCustomerPhone());
        validOrderInfoPhone("dealerPhone", orderXmlVO.getDealerPhone());
        validOrderInfoPhone("sellerPhone", orderXmlVO.getSellerPhone());
        validOrderInfoPhone("designerPhone", orderXmlVO.getDesignerPhone());
        validOrderInfoPhone("analystPhone", orderXmlVO.getAnalystPhone());
    }

    /**
     * 自定义单号校验
     *
     * @param key
     * @param val
     */
    private void validCustomOrderNo(String key, String val) {
        if (StringUtils.isNotEmpty(val) && !orderService.customOrderNoIsExists(val)) {
            throw new ServiceException(ORDER_IMPORT_ORDER_CUSTOMERNO_EXISTS, key, val);
        }
    }

    /**
     * 校验空
     *
     * @param key
     * @param val
     */
    private void validOrderInfoEmpty(String key, String val) {
        if (StringUtils.isEmpty(val)) {
            throw new ServiceException(ORDER_IMPORT_ORDER_FIELD_EMPTY, key);
        }
    }

    /**
     * 订单属性长度校验
     *
     * @param key
     * @param val
     * @param length
     */
    private void validOrderInfoLength(String key, String val, int length) {
        if (StringUtils.isNotEmpty(val) && length < val.length()) {
            throw new ServiceException(ORDER_IMPORT_ORDER_FIELD_LENGTH_ERROR, key, length);
        }
    }

    /**
     * 校验手机号格式
     *
     * @param key
     * @param val
     */
    private void validOrderInfoPhone(String key, String val) {
        if (StringUtils.isNotEmpty(val) && Boolean.FALSE.equals(ToolUtil.validatePhoneNumber(val))) {
            throw new ServiceException(ORDER_IMPORT_ORDER_FIELD_MOBILE_FORMAT_ERROR, key);
        }
    }

    /**
     * 校验时间格式
     *
     * @param key
     * @param val
     */
    private LocalDate validOrderInfoDate(String key, String val) {
        try {
            return LocalDateTimeUtil.parseDate(val, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        } catch (Exception e) {
            throw new ServiceException(ORDER_IMPORT_ORDER_FIELD_DATE_FORMAT_ERROR, key);
        }
    }

    /**
     * 非空判断+数据类型+数据正数
     *
     * @param getter:
     * @param name:       字段错误名称
     * @param targetType: 数字类型
     */
    private void notEmptyPositiveNumber(Supplier<String> getter, String name, Class<?> targetType) {
        String value = getter.get();
        if (!StringUtils.isEmpty(value)) { // 非空
            positiveNumber(value, name, targetType);
        }
    }

    /**
     * 数量类型+数量正数
     *
     * @param value:
     * @param name:       字段错误名称
     * @param targetType: 需要转换的数字类型
     */
    private void positiveNumber(String value, String name, Class<?> targetType) {
        if (ToolUtil.convertToType(value, targetType)) {
            if (!ToolUtil.checkNumber(value)) {
                throw new ServiceException(ORDER_IMPORT_DETAIL_VALID_ERROR, String.format(se.toString() + POSITIVE_ERR, name));
            }
        } else {
            throw new ServiceException(ORDER_IMPORT_DETAIL_VALID_ERROR, String.format(se.toString() + POSITIVE_ERR, name));
        }

    }

    /**
     * 非空判断，空赋初始值
     *
     * @param getter:
     * @param setter:
     * @param defaultValue: 赋值空值
     */

    private void isEmptyAndAdd(Supplier<String> getter, Consumer<String> setter, String defaultValue) {
        String value = getter.get();
        if (StringUtils.isEmpty(value)) {
            setter.accept(defaultValue);
        }
    }

    /**
     * 空值赋值+数量类型+数量正数+数据长度
     *
     * @param getter:
     * @param setter:
     * @param defaultValue: 赋值数据
     * @param targetType:   数据类型
     * @param name:         字段错误名称
     */
    private void isEmptyAddAndPositiveNumberSize(Supplier<String> getter, Consumer<String> setter, String defaultValue, Class<?> targetType, String name, Integer length, Integer decimalLength) {
        String value = getter.get();
        if (StringUtils.isEmpty(value)) {
            setter.accept(defaultValue);
        } else {
            positiveNumberSize(value, setter, targetType, name, length, decimalLength);
        }
    }

    /**
     * 数量类型+数量正数+数据长度
     *
     * @param value:      待校验数据
     * @param targetType: 数据类型
     * @param name:       字段错误名称
     */
    private void positiveNumberSize(String value, Consumer<String> setter, Class<?> targetType, String name, Integer length, Integer decimalLength) {
        if (ToolUtil.convertToType(value, targetType)) {
            if (ToolUtil.checkNumber(value)) {
                if (!ToolUtil.checkPrecision(value, length, decimalLength)) {
                    throw new ServiceException(ORDER_IMPORT_DETAIL_VALID_ERROR, String.format(se.toString() + LONG_ERR, name));
                }
            } else {
                throw new ServiceException(ORDER_IMPORT_DETAIL_VALID_ERROR, String.format(se.toString() + POSITIVE_ERR, name));
            }
        } else {
            throw new ServiceException(ORDER_IMPORT_DETAIL_VALID_ERROR, String.format(se.toString() + TYPE_ERR, name));
        }
    }

    /**
     * 空值赋值 +数量类型+数量正数
     *
     * @param getter:
     * @param setter:
     * @param defaultValue: 赋值数据
     * @param targetType:   数据类型
     * @param name:         字段错误名称
     */
    private void isEmptyAddAndPositiveNumber(Supplier<String> getter, Consumer<String> setter, String defaultValue, Class<?> targetType, String name) {
        String value = getter.get();
        if (StringUtils.isEmpty(value)) {
            setter.accept(defaultValue);
        } else {
            positiveNumber(value, name, targetType);
        }
    }

    /**
     * 空值赋值+数量类型
     *
     * @param getter:
     * @param setter:
     * @param defaultValue: 赋值数据
     * @param targetType:   数据类型
     * @param name:         字段错误名称
     */
    private void isEmptyAddAndNumber(Supplier<String> getter, Consumer<String> setter, String defaultValue, Class<?> targetType, String name) {
        String value = getter.get();
        if (StringUtils.isEmpty(value)) {
            setter.accept(defaultValue);
        } else {
            if (!ToolUtil.convertToType(value.trim(), targetType)) {
                throw new ServiceException(ORDER_IMPORT_DETAIL_VALID_ERROR, String.format(se.toString() + TYPE_ERR, name));
            }
        }
    }

    /**
     * 非空判断，错误返回
     *
     * @param getter:
     * @param name:   字段错误名称
     */
    private void isEmptyAndBack(Supplier<String> getter, String name) {
        String value = getter.get();
        if (StringUtils.isEmpty(value)) {
            throw new ServiceException(ORDER_IMPORT_DETAIL_VALID_ERROR, String.format(se.toString() + IS_NULL, name));
        }
    }

    /**
     * 空值返回+非空有效性判断
     *
     * @param getter:
     * @param setter:
     * @param defaultValue:      赋值数据
     * @param dictTypeConstants: 字典标签
     * @param name:              字段错误名称
     */
    private void isEmptyAddAndInvalid(Supplier<String> getter, Consumer<String> setter, String defaultValue, String dictTypeConstants, String name) {
        String value = getter.get();
        if (StringUtils.isEmpty(value)) {
            setter.accept(defaultValue);
        } else {
            String dictData = DictFrameworkUtils.parseDictDataValue(dictTypeConstants, value.trim());
            if (StringUtils.isEmpty(dictData)) {
                throw new ServiceException(ORDER_IMPORT_DETAIL_VALID_ERROR, String.format(se.toString() + INVALID, name));
            }
        }
    }

    /**
     * 非空错返 +数量类型+数量正数+数据长度
     *
     * @param getter:
     * @param name:       字段错误名称
     * @param setter:
     * @param targetType: 需要转换的数字类型
     */
    private void isEmptyAndPositiveNumber(Supplier<String> getter, String name, Consumer<String> setter, Class<?> targetType, Integer length, Integer decimalLength) {
        String value = getter.get();
        if (StringUtils.isEmpty(value)) {
            throw new ServiceException(ORDER_IMPORT_DETAIL_VALID_ERROR, String.format(se.toString() + IS_NULL, name));
        } else {
            positiveNumberSize(value, setter, targetType, name, length, decimalLength);
        }
    }

    /**
     * 非空判断 +数据类型+数据正数+数据长度
     *
     * @param getter:
     * @param setter:
     * @param name:       字段错误名称
     * @param targetType: 数字类型
     */
    private void notEmptyPositiveNumberSize(Supplier<String> getter, Consumer<String> setter, String name, Class<?> targetType, Integer length, Integer decimalLength) {
        String value = getter.get();
        if (StringUtils.isNotEmpty(value)) { // 非空
            positiveNumberSize(value, setter, targetType, name, length, decimalLength);
        }
    }

    /**
     * 非空错返+数量类型+数量正数
     *
     * @param getter:
     * @param name:       字段错误名称
     * @param targetType: 需要转换的数字类型
     */
    private void isEmptyBackAndPositiveNumber(Supplier<String> getter, String name, Class<?> targetType) {
        String value = getter.get();
        if (StringUtils.isEmpty(value)) {
            throw new ServiceException(ORDER_IMPORT_DETAIL_VALID_ERROR, String.format(se.toString() + IS_NULL, name));
        } else {
            positiveNumber(value, name, targetType);
        }
    }

    /**
     * 空值错返+数量类型
     *
     * @param getter:
     * @param targetType: 数据类型
     * @param name:       字段错误名称
     */
    private void isEmptyBackAndNumber(Supplier<String> getter, Class<?> targetType, String name) {
        String value = getter.get();
        if (StringUtils.isEmpty(value)) {
            throw new ServiceException(ORDER_IMPORT_DETAIL_VALID_ERROR, String.format(se.toString() + IS_NULL, name));
        } else {
            if (!ToolUtil.convertToType(value.trim(), targetType)) {
                throw new ServiceException(ORDER_IMPORT_DETAIL_VALID_ERROR, String.format(se.toString() + TYPE_ERR, name));
            }
        }
    }

    /**
     * 空值赋值+非空有效性判断
     *
     * @param getter:
     * @param dictTypeConstants: 字典标签
     * @param name:              字段错误名称
     */
    private void isEmptyBackAndInvalid(Supplier<String> getter, String dictTypeConstants, String name) {
        String value = getter.get();
        if (com.alibaba.nacos.common.utils.StringUtils.isEmpty(value)) {
            throw new ServiceException(ORDER_IMPORT_DETAIL_VALID_ERROR, String.format(se.toString() + IS_NULL, name));
        } else {
            String dictData = DictFrameworkUtils.parseDictDataValue(dictTypeConstants, value.trim());
            if (StringUtils.isEmpty(dictData)) {
                throw new ServiceException(ORDER_IMPORT_DETAIL_VALID_ERROR, String.format(se.toString() + INVALID, name));
            }
        }
    }

    /**
     * 长度面积数值按单位转换
     *
     * @param lengthStr
     * @param unit
     * @param type
     * @return
     */
    public Double changeUnit(String lengthStr, String unit, String type) {
        String dictData = DictFrameworkUtils.parseDictDataValue(type, unit);
        Double length = Double.parseDouble(lengthStr);
        return length * Double.parseDouble(dictData);
    }
}
