package com.yeejoin.amos.api.openapi.service;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelReader;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.read.metadata.ReadSheet;
import com.alibaba.excel.read.metadata.holder.ReadRowHolder;
import com.alibaba.fastjson.JSON;
import com.yeejoin.amos.api.common.restful.utils.ResponseHelper;
import com.yeejoin.amos.api.openapi.dto.XiAnElevatorExcelDto;
import com.yeejoin.amos.api.openapi.dto.XiAnEquipInfoExcelDto;
import com.yeejoin.amos.api.openapi.feign.TzsJgServiceFeignClient;
import com.yeejoin.amos.boot.biz.common.utils.RequestContextWrapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import org.typroject.tyboot.core.foundation.utils.ValidationUtil;
import org.typroject.tyboot.core.restful.exception.instance.BadRequest;

import java.io.InputStream;
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Stream;

@Slf4j
@Service
public class XiAnDataDockServiceImpl {

    private final TzsJgServiceFeignClient jgServiceFeignClient;

    public XiAnDataDockServiceImpl(TzsJgServiceFeignClient jgServiceFeignClient) {
        this.jgServiceFeignClient = jgServiceFeignClient;
    }

    // 自定义线程池
    private final ExecutorService executorService = Executors.newFixedThreadPool(10);


    private final String excelErrorStr = "Excel 第[%s]行";

    private final Map<String, Object> resultError = new HashMap<>();
    List<String> useInnerCodeList = new ArrayList<>();// 单位内部编号集合
    List<String> equCodeList = new ArrayList<>();// 设备代码集合
    List<String> factoryNumList = new ArrayList<>();// 出厂编码集合
    List<String> recordList = new ArrayList<>();// 注册代码集合
    List<String> useOrgCodeList = new ArrayList<>();// 使用登记证号集合


    /**
     * 西安设备数据导入
     *
     * @param file excel 文件
     * @return 导入结果
     * @throws Exception 异常
     */
    public Object importEquipmentData(MultipartFile file) {
        List<XiAnEquipInfoExcelDto> equipInfoExcelDtos;
        try {
            // 1, 数据读取 + 格式、类型异常检查
            equipInfoExcelDtos = this.dataHandler(file);
        } catch (Exception e) {
            return ResponseHelper.buildFailureResponse(e.getMessage(), "参数校验失败，详细请看返回信息", HttpStatus.BAD_REQUEST);
        }

        // 2, 分批保存设备数据
        this.batchSaveEquipmentData(equipInfoExcelDtos);
        return ResponseHelper.buildResponse("设备保存成功！");
    }

    /**
     * 批量异步保存设备数据
     *
     * @param equipInfoExcelDtos
     */
    public void batchSaveEquipmentData(List<XiAnEquipInfoExcelDto> equipInfoExcelDtos) {
        log.info("解析成功，准备上传数据，条数：{}", equipInfoExcelDtos.size());
        int batchSize = 1000;
        int totalSize = equipInfoExcelDtos.size();
        // 主线程中获取登录信息传递到异步线程中
        RequestContextWrapper contextWrapper = RequestContextWrapper.capture();
        List<CompletableFuture<Void>> futures = new ArrayList<>();
        for (int i = 0; i < totalSize; i += batchSize) {
            List<XiAnEquipInfoExcelDto> batch = equipInfoExcelDtos.subList(i, Math.min(totalSize, i + batchSize));
            CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
                try {
                    contextWrapper.apply();
                    jgServiceFeignClient.saveEquipmentData(batch);
                } catch (Exception e) {
                    log.error("西安数据上传，保存设备数据失败: ", e);
                }
            }, executorService);
            futures.add(future);
        }
        // 等待所有异步任务完成
        CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
    }


    /**
     * excel 文件读取，字段校验
     *
     * @param file excel 文件
     * @return 设备数据
     * @throws Exception 异常信息
     */
    public List<XiAnEquipInfoExcelDto> dataHandler(MultipartFile file) {
        List<XiAnEquipInfoExcelDto> dataList = new ArrayList<>();
        resultError.clear();
        useInnerCodeList.clear();
        equCodeList.clear();
        factoryNumList.clear();

        try {
            InputStream inputStream = file.getInputStream();
            ExcelReader excelReader = EasyExcel.read(inputStream).build();
            List<ReadSheet> sheetList = excelReader.excelExecutor().sheetList(); // 获取所有 sheet

            for (ReadSheet readSheet : sheetList) {
                String sheetName = readSheet.getSheetName();
                int sheetNo = readSheet.getSheetNo();

                // 忽略填充设备种类类别品种的三级联动sheet页
                if (sheetName.contains("忽略")) {
                    continue;
                }
                Map<String, Object> sheetError = new HashMap<>();
                EasyExcel.read(file.getInputStream(), XiAnEquipInfoExcelDto.class, new AnalysisEventListener<XiAnEquipInfoExcelDto>() {
                    // 每读一行都会执行
                    @Override
                    public void invoke(XiAnEquipInfoExcelDto data, AnalysisContext context) {
                        // 数据检查
                        checkExcelData(sheetName, data, context, sheetError);
                        useInnerCodeList.add(data.getUseInnerCode());
                        equCodeList.add(data.getEquCode());
                        dataList.add(data);
                    }

                    // 每个sheet页读完都会执行
                    @Override
                    public void doAfterAllAnalysed(AnalysisContext context) {
                        // 记录每一个sheet页的错误集
                        if (!ValidationUtil.isEmpty(sheetError)) {
                            resultError.put(String.format("%s sheet页", sheetName), sheetError);
                        }
                    }
                }).headRowNumber(4).sheet(sheetNo, sheetName).doRead();
            }

            excelReader.finish(); // 关闭 reader，释放资源

            if (CollectionUtils.isEmpty(dataList)) {
                resultError.put("allSheet", "你上传了一个空数据的Excel文档!");
                throw new BadRequest("你上传了一个空数据的Excel文档!");
            }

            // 检查 resultError中的每一项，若存在内容，则有错误信息，直接抛异常
            if (!ValidationUtil.isEmpty(resultError)) {
                throw new BadRequest(JSON.toJSONString(resultError));
            }

            return dataList;
        } catch (Exception e) {
            throw new BadRequest(e.getMessage());
        }
    }

    /**
     * excel数据字段校验
     *
     * @param sheetName sheet页名称
     * @param data      excel数据
     * @param context
     */
    public void checkExcelData(String sheetName, XiAnEquipInfoExcelDto data, AnalysisContext context, Map<String, Object> sheetError) {
        ReadRowHolder readRowHolder = context.readRowHolder();
        int rowIndex = readRowHolder.getRowIndex() + 1;
        StringBuffer rowError = new StringBuffer();
        log.info("开始解析数据，第{}条数据", rowIndex);
        // 是否起重机械
        boolean isQZJX = !ValidationUtil.isEmpty(data.getEquList()) && "4000".equals(data.getEquList());
        // 是否场内机动车辆
        boolean isCNJDXL = !ValidationUtil.isEmpty(data.getEquList()) && "5000".equals(data.getEquList());
        // 是否锅炉
        boolean isGL = !ValidationUtil.isEmpty(data.getEquList()) && "1000".equals(data.getEquList());
        // 是否压力容器
        boolean isYLRQ = !ValidationUtil.isEmpty(data.getEquList()) && "2000".equals(data.getEquList());
        // 是否压力管道
        boolean isYLGD = !ValidationUtil.isEmpty(data.getEquList()) && "8000".equals(data.getEquList());
        // 是否大型游乐设施
        boolean isDXYNSS = !ValidationUtil.isEmpty(data.getEquList()) && "6000".equals(data.getEquList());
        // 是否客运索道
        boolean isKYSD = !ValidationUtil.isEmpty(data.getEquList()) && "9000".equals(data.getEquList());

        try {
            log.info("解析第{}行数据：{}", rowIndex, JSON.toJSONString(data));

            // 通用字段检查 每一个sheet页都有的基本信息，设计信息，制造信息和使用信息
            this.commonFieldCheck(data, rowError);
            data.setIsCompleteXa(this.checkIsComplete(data) ? 0 : 1);
            // 起重机械----技术参数 检查
            if (isQZJX) {
                this.QZJXTechnicalParamsCheck(data, rowError);
            }
            // 场内机动车辆----技术参数 检查
            if (isCNJDXL) {
                this.CNJDCLTechnicalParamsCheck(data, rowError);
            }
            // 锅炉----技术参数 检查
            if (isGL) {
                this.GLTechnicalParamsCheck(data, rowError);
            }
            // 压力容器----技术参数 检查
            if (isYLRQ) {
                this.YLRQTechnicalParamsCheck(data, rowError);
            }
            // 压力管道----技术参数 检查
            if (isYLGD) {
                this.YLGDTechnicalParamsCheck(data, rowError);
            }
            // 大型游乐设施----技术参数 检查
            if (isDXYNSS) {
                this.DXYLSSTechnicalParamsCheck(data, rowError);
            }
            // 客运索道----技术参数 检查
            if (isKYSD) {
                this.KYSDTechnicalParamsCheck(data, rowError);
            }

            if (!StringUtils.isBlank(rowError)) {
                sheetError.put(String.format(excelErrorStr, rowIndex), rowError);
            }

        } catch (Exception e) {
            log.error(String.format("sheet页：[%s] -> 对应行索引数: [%s] -> 失败的 Excel 数据: [%s]", sheetName, rowIndex, JSON.toJSONString(data)), e);
            throw e;
        }
    }

    /**
     * 检查字段是否为空，如果为空则追加错误信息到result
     *
     * @param value        待检查字段
     * @param errorMessage 错误信息
     * @param rowError     结果集
     */
    private void checkNotBlank(String value, String errorMessage, StringBuffer rowError) {
        if (StringUtils.isBlank(value)) {
            rowError.append(errorMessage);
        }
    }

    /**
     * 检查资料是否齐全，16个导入必填项
     *
     * @param data 导入项
     */
    private boolean checkIsComplete(XiAnEquipInfoExcelDto data) {
        return Stream.of(data.getEquList(), data.getEquCategory(), data.getUseInnerCode(),
                data.getProductName(), data.getEquType(), data.getUseUnitCode(),
                data.getUseUnit(), data.getDesignUnitCreditCode(), data.getDesignUnitName(),
                data.getDesignUseDate(), data.getDesignDate(), data.getProduceUnitCreditCode(),
                data.getProduceUnitName(), data.getProduceLicenseNum(), data.getFactoryNum(),
                data.getProduceDate()).noneMatch(StringUtils::isBlank);
    }

    /**
     * 通用字段检查 每一个sheet页都有的基本信息，设计信息，制造信息和使用信息
     *
     * @param data     源数据
     * @param rowError 错误集合
     */
    private void commonFieldCheck(XiAnEquipInfoExcelDto data, StringBuffer rowError) {

        boolean isPressureVessel = !ObjectUtils.isEmpty(data.getEquList()) && "2000".equals(data.getEquList());// 是否压力容器
        boolean isPressurePiping = !ObjectUtils.isEmpty(data.getEquList()) && "8000".equals(data.getEquList());// 是否压力管道

        // 基本信息
        checkNotBlank(data.getEquList(), "设备种类不能为空；", rowError);
        //checkNotBlank(data.getEquCategory(), "设备类别不能为空；", rowError);
        //checkNotBlank(data.getUseInnerCode(), "单位内编号不能为空；", rowError);
        //if (useInnerCodeList.contains(data.getUseInnerCode())) {
        //    rowError.append("单位内编号不能重复；");
        //}
        //checkNotBlank(data.getProductName(), "产品名称不能为空；", rowError);
        //checkNotBlank(data.getEquType(), "设备型号不能为空；", rowError);
        checkNotBlank(data.getEquCodeType(), "有无设备代码不能为空；", rowError);
        if ("1".equals(data.getEquCodeType())) {
            checkNotBlank(data.getEquCode(), "设备代码不能为空；", rowError);
            String equCode = data.getEquCode();
            //if (equCode.matches("[a-zA-Z0-9]+")) {
            //if (equCode.length() <= 17) {
            //    rowError.append("设备代码不能小于17位；");
            //}
            //if (equCode.length() >= 20) {
            //    rowError.append("设备代码不能大于20位；");
            //}
            //} else {
            //    rowError.append("设备代码不能包含特殊字符；");
            //}
            if (equCodeList.contains(data.getEquCode())) {
                data.setOriginalEquCode(data.getEquCode());
                data.setEquCodeType("2");
                data.setEquCode("");
            }
            if (this.checkEquCodeUniqueness(data.getEquCode())) {
                data.setOriginalEquCode(data.getEquCode());
                data.setEquCodeType("2");
                data.setEquCode("");
            }
        } else {
            data.setEquCode("");
        }
        if (isPressurePiping) {// 校验压力管道特有的参数
            //checkNotBlank(data.getProjectContraption(), "工程（装置）名称不能为空；", rowError);
        } else {
            data.setProjectContraption("");
        }
        if (isPressureVessel) {// 校验压力容器特有的参数
            boolean isGasCylinder = !ObjectUtils.isEmpty(data.getEquCategory()) && "2300".equals(data.getEquCategory());// 是否气瓶
            boolean isFixedGasCylinder = !ObjectUtils.isEmpty(data.getEquCategory()) && "2100".equals(data.getEquCategory());//是否固定式压力容器
            boolean isOxygenChamber = !ObjectUtils.isEmpty(data.getEquCategory()) && "2400".equals(data.getEquCategory());//是否氧舱
            boolean isMobilePressureVessel = !ObjectUtils.isEmpty(data.getEquCategory()) && "2200".equals(data.getEquCategory());//是否移动式压力容器
            boolean isSpecialGasCylinder = isGasCylinder && !ObjectUtils.isEmpty(data.getEquDefine()) && "23T0".equals(data.getEquDefine());// 是否特种气瓶
            boolean isCarGasCylinder = isSpecialGasCylinder && (!ObjectUtils.isEmpty(data.getWhetherVehicleCylinder()) && "1".equals(data.getWhetherVehicleCylinder()));// 是否车用气瓶
            if (isGasCylinder) { // 是气瓶
                if (!isSpecialGasCylinder) { // 不是特种气瓶
                    data.setWhetherVehicleCylinder("");
                    checkNotBlank(data.getInformationSituation(), "信息化管理情况不能为空；", rowError);
                    if (!ObjectUtils.isEmpty(data.getInformationSituation())) {
                        checkNotBlank(data.getInformationManageCode(), "二维码或者电子标签编号不能为空；", rowError);
                    }
                }
                if (isSpecialGasCylinder) { // 是特种气瓶
                    checkNotBlank(data.getWhetherVehicleCylinder(), "是否车用气瓶不能为空；", rowError);
                    if (!isCarGasCylinder) {// 不是车用气瓶
                        checkNotBlank(data.getInformationSituation(), "信息化管理情况不能为空；", rowError);
                        if (!ObjectUtils.isEmpty(data.getInformationSituation())) {
                            checkNotBlank(data.getInformationManageCode(), "二维码或者电子标签编号不能为空；", rowError);
                        }
                    }
                    if (isCarGasCylinder) { // 是车用气瓶
                        data.setInformationSituation("");
                        data.setInformationManageCode("");
                        checkFactoryNumUniqueness(data, rowError);// 校验出厂编号/产品编码唯一性
                    }
                }
                data.setWhetherSphericalTank("");
                data.setWhetherSkidMountedPressureVessel("");
            }
            if (isFixedGasCylinder) { // 是固定式压力容器
//                checkNotBlank(data.getWhetherSphericalTank(), "是否球罐不能为空；", rowError);
//                checkNotBlank(data.getWhetherSkidMountedPressureVessel(), "是否撬装式压力容器不能为空；", rowError);
                data.setInformationSituation("");
                data.setInformationManageCode("");
            }
            if (isMobilePressureVessel) {// 是移动式压力容器
                data.setWhetherSkidMountedPressureVessel("");
                data.setWhetherVehicleCylinder("");
                data.setWhetherSphericalTank("");
                data.setInformationSituation("");
                data.setInformationManageCode("");
            }
            if (isOxygenChamber) {//是氧舱
                data.setWhetherSkidMountedPressureVessel("");
                data.setWhetherVehicleCylinder("");
                data.setWhetherSphericalTank("");
                data.setInformationSituation("");
                data.setInformationManageCode("");
            }
        }
        // 使用信息
        //checkNotBlank(data.getUseUnitCode(), "使用单位统一社会信用代码不能为空；", rowError);
        //Optional.ofNullable(data.getUseUnitCode()).ifPresent(v -> checkCreditCode(v, "使用单位统一社会信用代码格式不正确；", rowError));
        //checkNotBlank(data.getUseUnit(), "使用单位名称不能为空；", rowError);
        // 设计信息
        //checkNotBlank(data.getDesignUnitCreditCode(), "设计单位统一社会信用代码不能为空；", rowError);
//        Optional.ofNullable(data.getDesignUnitCreditCode()).ifPresent(v -> checkCreditCode(v, "设计单位统一社会信用代码格式不正确；", rowError));
        //checkNotBlank(data.getDesignUnitName(), "设计单位名称不能为空；", rowError);
        //checkNotBlank(data.getDesignUseDate(), "设计使用年限不能为空；", rowError);
//        Optional.ofNullable(data.getDesignUseDate()).ifPresent(v -> checkDateFormatNumber(v, "设计使用年限不能为数字以外的其他类型；", rowError));
        //checkNotBlank(data.getDesignUseDate(), "设计日期不能为空；", rowError);
        //Optional.ofNullable(data.getDesignDate()).ifPresent(v -> checkDateFormatCorrect(v, "设计日期格式不正确；", rowError));
//        Optional.ofNullable(data.getAppraisalDate()).ifPresent(v -> checkDateFormatCorrect(v, "设计文件鉴定日期格式不正确；", rowError));
        // 制造信息
        //checkNotBlank(data.getProduceUnitCreditCode(), "制造单位统一社会信用代码不能为空；", rowError);
        //Optional.ofNullable(data.getProduceUnitCreditCode()).ifPresent(v -> checkCreditCode(v, "制造单位统一社会信用代码格式不正确；", rowError));
        //checkNotBlank(data.getProduceUnitName(), "制造单位名称不能为空；", rowError);
        //checkNotBlank(data.getProduceLicenseNum(), "制造许可编号不能为空；", rowError);
        //checkNotBlank(data.getFactoryNum(), "出厂编号/产品编码不能为空；", rowError);
        //checkNotBlank(data.getProduceDate(), "制造日期不能为空；", rowError);
        //Optional.ofNullable(data.getProduceDate()).ifPresent(v -> checkDateFormatCorrect(v, "制造日期格式不正确；", rowError));
    }

    /**
     * 检查上传Excel中的日期格式是否正确
     *
     * @param date         待检查数据
     * @param errorMessage 错误内容
     * @param rowError     错误集
     */
    private void checkDateFormatCorrect(String date, String errorMessage, StringBuffer rowError) {
        if (!date.matches("\\d{4}-\\d{2}-\\d{2}")) {
            rowError.append(errorMessage);
        }
    }

    /**
     * 检查上传Excel中的统一信用代码格式
     *
     * @param code         待检查数据
     * @param errorMessage 错误内容
     * @param rowError     错误集
     */
    private void checkCreditCode(String code, String errorMessage, StringBuffer rowError) {
        String regex = "^[A-Z0-9]{15}(?:[A-Z0-9]{3})?$";
        if (!code.matches(regex)) {
            rowError.append(errorMessage);
        }
    }

    /**
     * 检查上传Excel中的数字格式是否正确
     *
     * @param date         待检查数据
     * @param errorMessage 错误内容
     * @param rowError     错误集
     */
    private void checkDateFormatNumber(String date, String errorMessage, StringBuffer rowError) {
        if (!NumberUtils.isCreatable(date)) {
            rowError.append(errorMessage);
        }
    }

    /**
     * 车用气瓶业务里面的 出厂编号/产品编码 校验唯一性（产品编号在车用气瓶范围内全局唯一）
     *
     * @param data
     */
    private void checkFactoryNumUniqueness(XiAnEquipInfoExcelDto data, StringBuffer rowError) {
        Integer count = jgServiceFeignClient.checkFactoryNumUniquenessForVehicleCylinder(data.getFactoryNum());
        if (count > 0) {
            rowError.append("出厂编号/产品编码系统中已存在！");
        }
    }

    /**
     * 根据设备代码检查唯一性
     *
     * @param equCode 设备代码
     */
    private boolean checkEquCodeUniqueness(String equCode) {
        return jgServiceFeignClient.selectByEquCodeAndClaimStatus(equCode);
    }

    /**
     * 根据record检查唯一性
     *
     * @param record record
     */
    private boolean checkRecordUniqueness(String record) {
        return jgServiceFeignClient.selectByRecordAndClaimStatus(record);
    }

    /**
     * 起重机械----技术参数 检查
     *
     * @param data
     * @param rowError
     */
    private void QZJXTechnicalParamsCheck(XiAnEquipInfoExcelDto data, StringBuffer rowError) {
    }

    /**
     * 场内机动车辆----技术参数 检查
     *
     * @param data
     * @param rowError
     */
    private void CNJDCLTechnicalParamsCheck(XiAnEquipInfoExcelDto data, StringBuffer rowError) {
    }

    /**
     * 锅炉----技术参数 检查
     *
     * @param data
     * @param rowError
     */
    private void GLTechnicalParamsCheck(XiAnEquipInfoExcelDto data, StringBuffer rowError) {
    }

    /**
     * 压力容器----技术参数 检查
     *
     * @param data
     * @param rowError
     */
    private void YLRQTechnicalParamsCheck(XiAnEquipInfoExcelDto data, StringBuffer rowError) {
        //checkNotBlank(data.getSingleBottleVolume(), "单瓶容积不能为空；", rowError);
        //checkNotBlank(data.getChargingMedium(), "充装介质不能为空；", rowError);
        //checkNotBlank(data.getNominalWorkingPressure(), "公称工作压力不能为空；", rowError);
    }

    /**
     * 压力管道----技术参数 检查
     *
     * @param data
     * @param rowError
     */
    private void YLGDTechnicalParamsCheck(XiAnEquipInfoExcelDto data, StringBuffer rowError) {
        //checkNotBlank(data.getWallThickness_YLGD(), "公称壁厚不能为空；", rowError);
        //checkNotBlank(data.getNominalDiameter(), "公称直径不能为空；", rowError);
        checkNotBlank(data.getPipeLength(), "管道长度不能为空；", rowError);
//        checkNotBlank(data.getPressure(), "压力不能为空；", rowError);
        //checkNotBlank(data.getMedium_YLGD(), "介质不能为空；", rowError);
        //checkNotBlank(data.getTemperature_YLGD(), "温度不能为空；", rowError);
        //checkNotBlank(data.getPipelineNumber(), "管道编号不能为空；", rowError);
        //checkNotBlank(data.getDeviceLevel_YLGD(), "管道级别不能为空；", rowError);
        Optional.ofNullable(data.getStartePosition()).ifPresent(v -> {
            if (v.split("-").length <= 1) {
                rowError.append("起/始位置 （经纬度）格式不正确");
            }
        });
        if (!ObjectUtils.isEmpty(data.getStartePosition()) && data.getStartePosition().split("-").length == 2) {
            String[] split = data.getStartePosition().split("-");
            data.setStartePosition(String.format("{\"latitude\":%s,\"longitude\":%s}", split[0], split[1]));
        }
    }

    /**
     * 大型游乐设施----技术参数 检查
     *
     * @param data
     * @param rowError
     */
    private void DXYLSSTechnicalParamsCheck(XiAnEquipInfoExcelDto data, StringBuffer rowError) {
    }

    /**
     * 客运索道----技术参数 检查
     *
     * @param data
     * @param rowError
     */
    private void KYSDTechnicalParamsCheck(XiAnEquipInfoExcelDto data, StringBuffer rowError) {
    }

    /**
     * 批量保存设备信息
     *
     * @param file excel文件
     * @param isRegistration 是否登记数据
     *
     */
    public Object importElevatorData(MultipartFile file, Boolean isRegistration) {
        List<XiAnElevatorExcelDto> elevatorExcelDtos;
        try {
            elevatorExcelDtos = this.dataHandlerElevator(file, isRegistration);
        } catch (Exception e) {
            return ResponseHelper.buildFailureResponse(e.getMessage(), "参数校验失败，详细请看返回信息", HttpStatus.BAD_REQUEST);
        }

        return ResponseHelper.buildResponse(this.batchSaveElevatorData(elevatorExcelDtos, isRegistration));
    }

    /**
     * excel 文件读取，字段校验
     *
     * @param file           excel 文件
     * @param isRegistration 是否登记数据
     * @return 设备数据
     * @throws Exception 异常信息
     */
    public List<XiAnElevatorExcelDto> dataHandlerElevator(MultipartFile file, Boolean isRegistration) {
        List<XiAnElevatorExcelDto> dataList = new ArrayList<>();
        resultError.clear();
        useInnerCodeList.clear();
        equCodeList.clear();
        factoryNumList.clear();
        recordList.clear();
        useOrgCodeList.clear();
        try {
            InputStream inputStream = file.getInputStream();
            ExcelReader excelReader = EasyExcel.read(inputStream).build();
            List<ReadSheet> sheetList = excelReader.excelExecutor().sheetList();
            for (ReadSheet readSheet : sheetList) {
                String sheetName = readSheet.getSheetName();
                int sheetNo = readSheet.getSheetNo();
                // 忽略填充设备种类类别品种的三级联动sheet页
                if (sheetName.contains("忽略")) {
                    continue;
                }
                Map<String, Object> sheetError = new HashMap<>();
                EasyExcel.read(file.getInputStream(), XiAnElevatorExcelDto.class, new AnalysisEventListener<XiAnElevatorExcelDto>() {
                    @Override
                    public void invoke(XiAnElevatorExcelDto data, AnalysisContext context) {
                        checkElevatorExcelData(sheetName, data, context, sheetError, isRegistration);
//                        useInnerCodeList.add(data.getUseInnerCode());
//                        equCodeList.add(data.getEquCode());
                        recordList.add(data.getRecord());
                        useOrgCodeList.add(data.getUseOrgCode());
                        dataList.add(data);
                    }

                    @Override
                    public void doAfterAllAnalysed(AnalysisContext context) {
                        if (!ValidationUtil.isEmpty(sheetError)) {
                            resultError.put(String.format("%s sheet页", sheetName), sheetError);
                        }
                    }
                }).headRowNumber(4).sheet(sheetNo, sheetName).doRead();
            }
            excelReader.finish();
            if (CollectionUtils.isEmpty(dataList)) {
                resultError.put("allSheet", "你上传了一个空数据的Excel文档!");
            }
            if (!ValidationUtil.isEmpty(resultError)) {
                throw new BadRequest(JSON.toJSONString(resultError));
            }
            return dataList;
        } catch (Exception e) {
            e.printStackTrace();
            throw new BadRequest(e.getCause().getMessage());
        }
    }

    /**
     * excel数据字段校验
     *
     * @param sheetName      sheet页名称
     * @param data           excel数据
     * @param context
     * @param isRegistration 是否登记数据
     */
    public void checkElevatorExcelData(String sheetName, XiAnElevatorExcelDto data, AnalysisContext context, Map<String, Object> sheetError, Boolean isRegistration) {
        ReadRowHolder readRowHolder = context.readRowHolder();
        int rowIndex = readRowHolder.getRowIndex() + 1;
        StringBuffer rowError = new StringBuffer();
        try {
            log.info("解析第{}行数据：{}", rowIndex, JSON.toJSONString(data));
            if (data.getEquList() != null && !data.getEquList().isEmpty()) {
                checkXaElevatorCommonInfo(data, rowError, isRegistration);
            } else if (data.getBusinessId() != null && !data.getBusinessId().isEmpty()){
                checkResumeInfo(data, rowError);
            }else{
                checkSupervisionInfo(data, rowError);
            }
            data.setIsCompleteXa(1);
            if (!StringUtils.isBlank(rowError)) {
                sheetError.put(String.format(excelErrorStr, rowIndex), rowError);
            }
        } catch (Exception e) {
            log.error(String.format("sheet页：[%s] -> 对应行索引数: [%s] -> 失败的 Excel 数据: [%s]", sheetName, rowIndex, JSON.toJSONString(data)), e);
            throw e;
        }
    }

    private void checkSupervisionInfo(XiAnElevatorExcelDto data, StringBuffer rowError) {
        checkNotBlank(data.getRecord(), "注册代码不能为空；", rowError);
    }

    private void checkResumeInfo(XiAnElevatorExcelDto data, StringBuffer rowError) {
        checkNotBlank(data.getResumeSeq(), "监管履历主键不能为空；", rowError);
        checkNotBlank(data.getBusinessType(), "业务类型不能为空；", rowError);
//        checkNotBlank(data.getCreateDate(), "创建时间不能为空；", rowError);
//        Optional.ofNullable(data.getCreateDate()).ifPresent(v -> checkDateFormatCorrect(v, "创建时间格式不正确；", rowError));
        checkNotBlank(data.getCreateUserName(), "创建人名称不能为空；", rowError);
        checkNotBlank(data.getBusinessId(), "业务单号不能为空；", rowError);
//        checkNotBlank(data.getApprovalUnit(), "审批单位不能为空；", rowError);
        checkNotBlank(data.getApprovalDate(), "审批通过时间不能为空；", rowError);
//        Optional.ofNullable(data.getApprovalDate()).ifPresent(v -> checkDateFormatCorrect(v, "审批通过时间格式不正确；", rowError));
    }

    /**
     * 西安电梯excel数据字段校验
     *
     * @param data      excel数据
     * @param rowError  行错误信息
     * @param isRegistration 是否登记数据
     */
    private void checkXaElevatorCommonInfo(XiAnElevatorExcelDto data, StringBuffer rowError, Boolean isRegistration) {
        // 基本信息
        checkNotBlank(data.getEquList(), "设备种类不能为空；", rowError);
        checkNotBlank(data.getEquCategory(), "设备类别不能为空；", rowError);
        checkNotBlank(data.getEquDefine(), "设备品种不能为空；", rowError);

        checkNotBlank(data.getRecord(), "注册代码不能为空；", rowError);
        if (recordList.contains(data.getRecord())) {
            rowError.append("模板中注册代码不能重复；").append(data.getRecord());
        }

        // 注掉以下代码逻辑是因为西安电梯数据会存在更新操作，西安系统的注册代码在平台作为设备主键进行插入或更新
//        if (this.checkRecordUniqueness(data.getRecord())){
//            rowError.append("系统中存在注册代码；").append(data.getRecord());
//        }
        checkNotBlank(data.getEquCodeType(), "有无设备代码不能为空；", rowError);
        // 需要历史登记数据，使用登记证编号不能为空
        if (isRegistration) {
            checkNotBlank(data.getCode96333(), "96333码不能为空；", rowError);
            checkNotBlank(data.getUseOrgCode(), "使用登记证编号不能为空；", rowError);
            if (useOrgCodeList.contains(data.getUseOrgCode())) {
                rowError.append("使用登记证编号不能重复；").append(data.getUseOrgCode());
            }
            checkNotBlank(data.getoRegUnit(), "登记机关不能为空；", rowError);
            checkNotBlank(data.getoRegDate(), "登记日期不能为空；", rowError);
        }

        // 监管信息
        checkNotBlank(data.getOrgBranchName(), "属地监管部门不能为空；", rowError);

        // 使用信息
        checkNotBlank(data.getUseUnitCode(), "使用单位统一社会信用代码不能为空；", rowError);
//        Optional.ofNullable(data.getUseUnitCode()).ifPresent(v -> checkCreditCode(v, "使用单位统一社会信用代码格式不正确；", rowError));
        checkNotBlank(data.getUseUnit(), "使用单位名称不能为空；", rowError);
        checkNotBlank(data.getProvinceName(), "使用地点-省不能为空；", rowError);
        checkNotBlank(data.getCityName(), "使用地点-市不能为空；", rowError);
        checkNotBlank(data.getCountyName(), "使用地点-区/县不能为空；", rowError);
//        checkNotBlank(data.getFactoryUseSiteStreet(), "使用地点-街道不能为空；", rowError);
//        checkNotBlank(data.getAddress(), "详细地址不能为空；", rowError);
        checkNotBlank(data.getUsePlace(), "使用场所不能为空；", rowError);
//        checkNotBlank(data.getEstateUnitName(), "产权单位名称不能为空；", rowError);
//        checkNotBlank(data.getEstateUnitCreditCode(), "产权单位社会信用代码不能为空；", rowError);
//        Optional.ofNullable(data.getEstateUnitCreditCode()).ifPresent(v -> checkCreditCode(v, "产权单位社会信用代码格式不正确；", rowError));
//        checkNotBlank(data.getSafetyManager(), "安全管理员不能为空；", rowError);
//        checkNotBlank(data.getPhone(), "安全管理员联系电话不能为空；", rowError);

        // 制造信息
//        checkNotBlank(data.getProduceUnitCreditCode(), "制造单位统一社会信用代码不能为空；", rowError);
//        Optional.ofNullable(data.getProduceUnitCreditCode()).ifPresent(v -> checkCreditCode(v, "制造单位统一社会信用代码格式不正确；", rowError));
//        checkNotBlank(data.getProduceUnitName(), "制造单位名称不能为空；", rowError);
//        checkNotBlank(data.getProduceLicenseNum(), "制造许可编号不能为空；", rowError);
//        checkNotBlank(data.getFactoryNum(), "出厂编号/产品编码不能为空；", rowError);
//        Optional.ofNullable(data.getProduceDate()).ifPresent(v -> checkDateFormatCorrect(v, "制造日期格式不正确；", rowError));

        // 安装信息
//        checkNotBlank(data.getInstallUnitCreditCode(), "安装单位统一信用代码不能为空；", rowError);
//        Optional.ofNullable(data.getInstallUnitCreditCode()).ifPresent(v -> checkCreditCode(v, "安装单位统一信用代码格式不正确；", rowError));
//        checkNotBlank(data.getInstallUnitName(), "安装单位名称不能为空；", rowError);
//        Optional.ofNullable(data.getInstallStartDate()).ifPresent(v -> checkDateFormatCorrect(v, "安装日期格式不正确；", rowError));
//        checkNotBlank(data.getInstallLeaderName(), "安装负责人不能为空；", rowError);
//        checkNotBlank(data.getInstallLeaderPhone(), "安装负责人联系电话不能为空；", rowError);

        // 检验监测信息
//        checkNotBlank(data.getInspectOrgCode(), "检验机构统一信用代码不能为空；", rowError);
//        Optional.ofNullable(data.getInspectOrgCode()).ifPresent(v -> checkCreditCode(v, "检验机构统一信用代码格式不正确；", rowError));
//        checkNotBlank(data.getInspectOrgName(), "检验机构名称不能为空；", rowError);
//        checkNotBlank(data.getInspectConclusion(), "检验结论不能为空；", rowError);
//        checkNotBlank(data.getInspectType(), "检验类型不能为空；", rowError);
//        checkNotBlank(data.getInspectStaff(), "检验人员名称不能为空；", rowError);
//        checkNotBlank(data.getInspectDate(), "检验日期不能为空；", rowError);
//        Optional.ofNullable(data.getInspectDate()).ifPresent(v -> checkDateFormatCorrect(v, "检验日期格式不正确；", rowError));
//        checkNotBlank(data.getNextInspectDate(), "下次检验日期不能为空；", rowError);
//        Optional.ofNullable(data.getNextInspectDate()).ifPresent(v -> checkDateFormatCorrect(v, "下次检验日期格式不正确；", rowError));

        // 维保信息
//        checkNotBlank(data.getMeUnitCreditCode(), "维保单位统一信用代码不能为空；", rowError);
//        Optional.ofNullable(data.getMeUnitCreditCode()).ifPresent(v -> checkCreditCode(v, "维保单位统一信用代码格式不正确；", rowError));
//        checkNotBlank(data.getMeUnitName(), "维保单位不能为空；", rowError);
//        checkNotBlank(data.getInformStart(), "维保合同开始时间不能为空；", rowError);
//        Optional.ofNullable(data.getInformStart()).ifPresent(v -> checkDateFormatCorrect(v, "维保合同开始时间格式不正确；", rowError));
//        checkNotBlank(data.getInformEnd(), "维保合同结束时间不能为空；", rowError);
//        Optional.ofNullable(data.getInformEnd()).ifPresent(v -> checkDateFormatCorrect(v, "维保合同结束时间格式不正确；", rowError));
//        checkNotBlank(data.getMeMaster(), "维保人员一名称不能为空；", rowError);
//        checkNotBlank(data.getMeMasterId(), "维保人员一身份证号不能为空；", rowError);
//        checkNotBlank(data.getMeMasterPhone(), "维保人员一联系方式不能为空；", rowError);
    }

    /**
     * 批量异步保存设备数据
     *
     * @param elevatorExcelDtos
     * @param isRegistration
     */
    public Map<String, Object> batchSaveElevatorData(List<XiAnElevatorExcelDto> elevatorExcelDtos, Boolean isRegistration) {
        log.info("解析成功，准备上传数据，条数：{}", elevatorExcelDtos.size());
        int batchSize = 1000;
        int totalSize = elevatorExcelDtos.size();
        Map<String, Object> rMap = new HashMap<>();
        // 主线程中获取登录信息传递到异步线程中
        RequestContextWrapper contextWrapper = RequestContextWrapper.capture();
        Set<String> errorResult = ConcurrentHashMap.newKeySet(); // 使用线程安全的Set
        // 分批处理，但限制同时运行的批次数
        int maxConcurrentBatches = 5; // 最大并发批次数
        Semaphore semaphore = new Semaphore(maxConcurrentBatches);
        List<CompletableFuture<Void>> futures = new ArrayList<>();
        for (int i = 0; i < totalSize; i += batchSize) {
            List<XiAnElevatorExcelDto> batch = elevatorExcelDtos.subList(i, Math.min(totalSize, i + batchSize));
            CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
                try {
                    semaphore.acquire(); // 获取许可
                    contextWrapper.apply();

                    batch.forEach(equ -> {
                        if (isRegistration) {
                            // 已登记的设备作为历史设备
                            equ.setDataSource("jg_his_xa");
                        } else {
                            // 未做过登记的新设备
                            equ.setDataSource("jg_xa");
                        }
                    });

                    Object result = jgServiceFeignClient.saveElevatorData(batch);
                    LinkedHashMap<String, Object> resultMap = (LinkedHashMap<String, Object>) result;

                    if (resultMap.containsKey("result") && resultMap.get("result") instanceof Collection) {
                        Collection<?> data = (Collection<?>) resultMap.get("result");
                        data.stream()
                                .filter(item -> item instanceof String)
                                .forEach(item -> errorResult.add((String) item));
                    }
                    if (resultMap.containsKey("status") && Integer.parseInt(resultMap.get("status").toString()) != 200) {
                        throw new RuntimeException(resultMap.get("message").toString());
                    }
                } catch (Exception e) {
                    log.error("西安数据上传，保存数据失败: ", e);
                    throw new RuntimeException(e.getMessage());
                } finally {
                    semaphore.release(); // 释放许可
                }
            }, executorService);
            futures.add(future);
        }
        // 等待所有异步任务完成
        CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
        if (!ValidationUtil.isEmpty(errorResult)) {
            rMap.put("message", errorResult);
            log.info("更新失败：{}", errorResult);
            return rMap;
        }
        log.info("设备保存成功");
        rMap.put("message", "设备保存成功");
        return rMap;
    }
}
