package digital.laboratory.platform.inspection.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import digital.laboratory.platform.common.core.constant.CommonConstants;
import digital.laboratory.platform.common.core.exception.CheckedException;
import digital.laboratory.platform.common.core.util.R;
import digital.laboratory.platform.common.mybatis.security.service.DLPUser;
import digital.laboratory.platform.comservice.entity.DlpDictData;
import digital.laboratory.platform.comservice.feign.RemoteDictDataService;
import digital.laboratory.platform.inspection.constant.TestRecordSampleDataConstant;
import digital.laboratory.platform.inspection.convert.TestRecordSampleDataConverter;
import digital.laboratory.platform.inspection.dto.*;
import digital.laboratory.platform.inspection.entity.*;
import digital.laboratory.platform.inspetion.api.enums.BusinessType;
import digital.laboratory.platform.inspection.enums.StdSolutionNum;
import digital.laboratory.platform.inspection.enums.TaskTestDataStatus;
import digital.laboratory.platform.inspection.event.AuditDataExecutionEvent;
import digital.laboratory.platform.inspection.event.FinishTestExecutionEvent;
import digital.laboratory.platform.inspection.event.PushDataToLabsCareEvent;
import digital.laboratory.platform.inspection.mapper.SampleInjectorMapper;
import digital.laboratory.platform.inspection.mapper.TestRecordSampleDataMapper;
import digital.laboratory.platform.inspection.query.AnalysisTestResultPageQuery;
import digital.laboratory.platform.inspection.query.QueryTestResultPageQuery;
import digital.laboratory.platform.inspection.service.*;
import digital.laboratory.platform.inspection.utils.datafile.hair.HairSewageCompoundData;
import digital.laboratory.platform.inspection.utils.datafile.nps.NPSDataFileStruct;
import digital.laboratory.platform.inspection.utils.datafile.nps.NPSTestDetailDataStruct;
import digital.laboratory.platform.inspection.vo.*;
import digital.laboratory.platform.inspetion.api.entity.EntrustInfo;
import digital.laboratory.platform.inspetion.api.entity.SampleInfo;
import digital.laboratory.platform.inspetion.api.entity.TestRecord;
import digital.laboratory.platform.sys.entity.Drug;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * @author xy
 * @version 1.0
 * @title TestRecordSampleDataServiceImpl
 * @description 检验数据实现类
 * @create 2024/1/30 11:07
 */
@Service
@Slf4j
public class TestRecordSampleDataServiceImpl extends ServiceImpl<TestRecordSampleDataMapper, TestRecordSampleData> implements TestRecordSampleDataService {
    @Resource
    private TestRecordSampleSolutionService testRecordSampleSolutionService;

    @Resource
    private TestRecordStandardSolutionService testRecordStandardSolutionService;

    @Resource
    private SampleInfoService sampleInfoService;

    @Resource
    private SampleInjectorMapper sampleInjectorMapper;

    @Resource
    private TestRecordSampleDataMapper testRecordSampleDataMapper;

    @Resource
    private TestRecordService testRecordService;

    @Resource
    private EntrustInfoService entrustInfoService;

    @Resource
    private TaskInfoService taskInfoService;

    @Resource
    private TestRecordSampledataExpandService testRecordSampledataExpandService;

    @Resource
    private ApplicationContext applicationContext;

    @Resource
    private TestRecordReagentService testRecordReagentService;

    @Resource
    private ProcessInspectDataService processInspectDataService;

    /**
     * 校验实验状态是否完成，完成则提升不能修改数据
     *
     * @param testId 实验id
     */
    @Override
    public void validateTestStatus(String testId) {
        // 校验实验状态是否完成
        if (testRecordService
                .count(Wrappers.<TestRecord>lambdaQuery()
                        .eq(TestRecord::getId, testId)
                        .ge(TestRecord::getStatus, 5))
                > 0) {
            // 大于0 说明该实验已经完成
            throw new RuntimeException("该实验已经完成，禁止修改原始数据！");
        }
    }

    /**
     * 删除检验数据
     * @param testId
     */
    @Override
    public void delete(String testId) {
        // 删除检验数据前，先删除扩展数据
        boolean remove = testRecordSampledataExpandService.remove(Wrappers.<TestRecordSampleDataExpand>lambdaQuery()
                .inSql(TestRecordSampleDataExpand::getTestDataId, String.format("SELECT id FROM b_test_record_sampledata WHERE test_id = %s", testId))
        );
        if (remove) {
            super.remove(Wrappers.<TestRecordSampleData>lambdaQuery().eq(TestRecordSampleData::getTestId, testId));
        }
    }

    //获取检验记录中的样品信息数据
    @Override
    public List<NPSCaseTestDataDto> getNPSSampleTestDataByTestId(String testId) {
        List<TestRecordSampleData> list = this.list(Wrappers.<TestRecordSampleData>lambdaQuery().eq(TestRecordSampleData::getTestId, testId));
        List<NPSCaseTestDataDto> retList = new ArrayList<>();
        for (TestRecordSampleData testRecordSampleData : list) {
//            NPSCaseTestDataDto npsCaseTestDataDto = testRecordSampleDataToPSCaseTestDataDto(testRecordSampleData);
            NPSCaseTestDataDto npsCaseTestDataDto = testRecordSampleDataToPSCaseTestDataDto(testRecordSampleData, NPSCaseTestDataDto.class);
            retList.add(npsCaseTestDataDto);
        }
        return retList;
    }

    /**
     * TestRecordSampleDataMapper
     * 根据业务id获取检验数据
     *
     * @param businessId 业务id
     * @return
     */
    @Override
    public List<?> getSampleTestDataByBusiness(String businessId) {
        // 先根据业务id查询委托、筛查、任务表中有没有这个id
        List<ESTBusinessInfoVO> estBusinessInfoVOS = testRecordSampleDataMapper.queryESTBusinessInfoList(Wrappers.<ESTBusinessInfoVO>query().eq("T.id", businessId));
        if (CollUtil.isEmpty(estBusinessInfoVOS)) {
            return null;
        }
        // 根据业务id查询样品信息
        List<String> sampleInfoIds = sampleInfoService.list(Wrappers.<SampleInfo>lambdaQuery().eq(SampleInfo::getBusinessId, businessId))
                .stream().map(SampleInfo::getId).collect(Collectors.toList());
        TestRecord testRecord = null;
        for (String sampleInfoId : sampleInfoIds) {
            TestRecord testRecord1 = testRecordService.getOne(Wrappers.<TestRecord>lambdaQuery().like(TestRecord::getSampleTestList, sampleInfoId));
            if (testRecord1 != null) {
                testRecord = testRecord1;
                break;
            }
        }
        if (testRecord == null) {
            throw new RuntimeException(String.format("没有查询到委托对应的试实验信息!"));
        }
        // 根据检材id 查询溶液编号
        Set<String> testRecordSampleSolutionsNoList = testRecordSampleSolutionService.list(Wrappers.<TestRecordSampleSolution>lambdaQuery()
                        .in(TestRecordSampleSolution::getMaterialId, sampleInfoIds))
                .stream()
                .map(TestRecordSampleSolution::getSampleNo)
                .collect(Collectors.toSet());

        List<TestRecordSampleData> testRecordSampleDataList = this.list(Wrappers.<TestRecordSampleData>lambdaQuery()
                .eq(TestRecordSampleData::getTestId, testRecord.getId())
                .last("ORDER BY SUBSTRING_INDEX(name, '-', -1) + 0")).stream().filter(item -> {
//            int size = testRecordSampleSolutionsNoList.size();
            // 这里判断了如果不是检材也返回true，是检材的话判断是不是这个业务id下的检材，根据编号判断
            return !item.getSampleType().equals(TestRecordSampleDataConstant.SAMPLE_TYPE_ANALYTE) ||
                    (item.getSampleType().equals(TestRecordSampleDataConstant.SAMPLE_TYPE_ANALYTE)
                            && testRecordSampleSolutionsNoList.contains(item.getSampleNo())
                    );
        }).collect(Collectors.toList());
//        ESTBusinessInfoVO estBusinessInfoVO = estBusinessInfoVOS.get(0);
//        // 封装的结果集
//        List<Object> retList = new ArrayList<>();
//        if (estBusinessInfoVO.getBusinessType().equals(BusinessType.BOINT_CASE.getBusinessType())) {
//            extractedSampleTestData(testRecordSampleDataList, retList, HairSewageDataDto.class);
//        } else if (estBusinessInfoVO.getBusinessType().equals(BusinessType.NPS_CASE.getBusinessType()) || estBusinessInfoVO.getBusinessType().equals(BusinessType.SCREENING_EVENT.getBusinessType())) {
//            extractedSampleTestData(testRecordSampleDataList, retList, NPSCaseTestDataDto.class);
//        } else {
//            extractedSampleTestData(testRecordSampleDataList, retList, HairSewageDataDto.class);
//        }
////        List<?> collect = TypeCasting(retList);
        /**
         * 根据测试ID和扩展数据列表获取分组后的扩展数据。
         *
         * @param testId 测试ID
         * @param newExpandDatList 新的扩展数据列表
         * @return 分组后的扩展数据
         */
        return testRecordSampleDataList;
    }

    /**
     * 毛发案件检验数据读取处理保存到数据库
     *
     * @param hairCompoundDataMap
     * @param testId
     * @return
     */
    @Override
    public Boolean saveTestDataHairCase(Map<String, List<HairSewageCompoundData>> hairCompoundDataMap, String testId) {
        try {
            buildSampleDataFromHairCase(hairCompoundDataMap, testId);//构造实验数据
            testRecordService.update(Wrappers.<TestRecord>lambdaUpdate().eq(TestRecord::getId, testId).set(TestRecord::getStatus, 2));
            return true;
        } catch (Exception err) {
            log.info("程序执行异常{}", err.getMessage());
            err.printStackTrace();
            return false;
        }
    }

    /**
     * 根据实验id获取不同类型的数据
     *
     * @param testId
     * @param type
     * @return
     */
    @Override
    public List<?> getSampleTestDataByTestId(String testId, String type) {
        List<TestRecordSampleDataVO> list = baseMapper.queryTestRecordSampleDataVOList(Wrappers.<TestRecordSampleData>lambdaQuery()
                .eq(TestRecordSampleData::getTestId, testId)
                .last("ORDER BY compound_name, SUBSTRING_INDEX(name, '-', -1) + 0"));
        return list;
    }

    /**
     * 分页查询 根据实验id获取不同类型的数据
     *
     * @param pageDTO
     * @return
     */
    @Override
    public IPage<TestRecordSampleDataVO> getSampleTestDataByTestIdPage(AnalysisTestResultPageQuery pageDTO) {
        IPage<TestRecordSampleDataVO> page = baseMapper.queryTestRecordSampleDataVOPage(new Page<>(pageDTO.getCurrent(), pageDTO.getSize()), Wrappers.<TestRecordSampleData>lambdaQuery()
                .eq(TestRecordSampleData::getTestId, pageDTO.getTestId())
                .orderByDesc(TestRecordSampleData::getCompoundName)
                .orderByDesc(TestRecordSampleData::getSampleNo));
        return page;
    }

    /**
     * 分页查询 获取审核的任务检验数据
     *
     * @param pageDTO
     * @return
     */
    @Override
    public Map<String, Object> queryWaitApproveTaskTestDataPage(TaskTestDataPageDTO pageDTO) {
        QueryWrapper<TaskTestDataDTO> queryWrapper = generateTaskTestDataQW(pageDTO);
        // 获取所有的任务数据
        List<TaskTestDataDTO> taskTestDataDTOList = baseMapper.queryWaitApproveTaskTestDataList(queryWrapper);

        // 根据检材编号进行分组, 使用LinkedHashMap的原因是保证顺序不乱
        LinkedHashMap<String, List<TaskTestDataDTO>> dataGroupBySampleNoMap = taskTestDataDTOList
                .stream()
                .collect(Collectors.groupingBy(TaskTestDataDTO::getSampleNo, LinkedHashMap::new, Collectors.toList()));

        Map<String, Object> map = new HashMap<>();

        JSONArray headerArray = generateHeaderArray(dataGroupBySampleNoMap);
        JSONArray dataArray = generateDataArray(dataGroupBySampleNoMap);
        // 手动分页
        map.put("header", headerArray);
        map.put("page", getPageData(dataArray, pageDTO.getCurrent(), pageDTO.getSize()));
        return map;
    }


    /**
     * 毛发、污水任务检验数据导入并保存到数据库
     *
     * @param hairSewageCompoundDataMap
     * @param testId
     * @param fileTypeCode
     * @return
     */
    @Override
    public Boolean saveTestDataHairSewageTask(Map<String, List<HairSewageCompoundData>> hairSewageCompoundDataMap, String testId, String fileTypeCode) {
        List<HairSewageDataDto> testRecordSampleDataList_All = new Vector<>();

        hairSewageCompoundDataMap.forEach((k, v) -> {
            v.parallelStream().forEach(hairsSewageCompoundData -> {
                String idName = hairsSewageCompoundData.getIdName();
                HairSewageDataDto hairSewageDataDto = new HairSewageDataDto();
                hairSewageDataDto.setSampleNo(idName); // 样本编号
                hairSewageDataDto.setSampleName(hairsSewageCompoundData.getSampleName()); // 样本名称
                hairSewageDataDto.setTestId(testId); // 实验id
                hairSewageDataDto.setBusinessType(fileTypeCode);
                // 设置对应的元素数据
                hairSewageDataDto.setTestSampleDataList(Lists.newArrayList(hairsSewageCompoundData));
                hairSewageDataDto.setCompoundName(k); // 化合物名称
                completeHairDataInfo(hairsSewageCompoundData, hairSewageDataDto);
                hairSewageDataDto.setTargetConcentration(hairsSewageCompoundData.getConcentration());
                if (hairsSewageCompoundData.getRatioFlag().equals("NO")) {
                    hairSewageDataDto.setIsDetected(1);
                } else {
                    hairSewageDataDto.setIsDetected(0);
                }
                if (idName.startsWith(StdSolutionNum.QC_SOLUTION.getPrefix())) {
                    hairSewageDataDto.setSampleType(TestRecordSampleDataConstant.SAMPLE_TYPE_QC);
                } else if (idName.startsWith(StdSolutionNum.MIXED_SOLUTION.getPrefix()) || idName.startsWith(StdSolutionNum.SIMPLE_SOLUTION.getPrefix())) {
                    hairSewageDataDto.setSampleType(TestRecordSampleDataConstant.SAMPLE_TYPE_STD);
                } else {
                    hairSewageDataDto.setSampleType(TestRecordSampleDataConstant.SAMPLE_TYPE_ANALYTE);
                }
                testRecordSampleDataList_All.add(hairSewageDataDto);//所有的样本的数据
            });
        });

        List<TestRecordSampleData> testRecordSampleDataAnalyteList = genPersistenceSampleData(testRecordSampleDataList_All);
        // 批量保存到数据库
        if (super.saveBatch(testRecordSampleDataAnalyteList)) {
            return testRecordService.update(Wrappers.<TestRecord>lambdaUpdate().eq(TestRecord::getId, testId).set(TestRecord::getStatus, 2));
        }
        return false;
    }

    /**
     * 实验结果查询中分页查询
     *
     * @param testResultBusinessVOPage
     * @param dto
     * @return
     */
    @Override
    public IPage<TestResultBusinessVO> queryTestResultPage(Page<TestResultBusinessVO> testResultBusinessVOPage, QueryTestResultPageQuery dto, Object... arg) {
        DLPUser dlpUsers = (DLPUser) arg[0];
        IPage<TestResultBusinessVO> testResultBusinessVOIPage = new Page<>();
        /** 1 查询当前用户做了哪些实验
         * 2 取出实验中关联的检材id
         * 3 根据检材id 取查询检材
         * 4 根据检材提取出对应的业务id
         * 5 最后通过业务id 进行分页查询
         */
        List<TestRecord> testRecordList = testRecordService.list(Wrappers.<TestRecord>lambdaQuery()
                .eq(TestRecord::getTestUserId, dlpUsers.getId())
                .eq(TestRecord::getStatus, 5));
        List<String> sampleTestListAll = new ArrayList<>();
        testRecordList.forEach(o -> sampleTestListAll.addAll(o.getSampleTestList())); // 合并所有的检材id

        Set<String> businessIdList = new HashSet<>();
        if (CollUtil.isNotEmpty(sampleTestListAll)) {
            businessIdList = sampleInfoService.list(Wrappers.<SampleInfo>lambdaQuery().in(SampleInfo::getId, sampleTestListAll)).stream().map(SampleInfo::getBusinessId).collect(Collectors.toSet());
        } else {
            return testResultBusinessVOIPage;
        }
        // 查询所有污水报告配置信息不为空的的任务信息
        Map<String, TaskInfo> taskInfoMap = taskInfoService.list(Wrappers.<TaskInfo>lambdaQuery().isNotNull(TaskInfo::getReportConfig).ne(TaskInfo::getReportConfig, "")).stream().collect(Collectors.toMap(TaskInfo::getId, Function.identity()));
        QueryWrapper<TestResultBusinessVO> queryWrapper = generateQueryWrapperByDTO(dto, businessIdList);
        testResultBusinessVOIPage = baseMapper.queryTestResultTaskPage(testResultBusinessVOPage, queryWrapper);
        testResultBusinessVOIPage.getRecords().parallelStream().forEach(item -> {
            item.setBusinessTypeName(BusinessType.getBusinessTypeName(item.getBusinessType()));
            if (taskInfoMap.containsKey(item.getId())) {
                item.setReportConfig(taskInfoMap.get(item.getId()).getReportConfig());
            }
            if (StrUtil.isNotBlank(item.getCompounds())) {
                item.setCompoundsJsonArray(JSONArray.parseArray(item.getCompounds()));
            }
        });
        return testResultBusinessVOIPage;
    }

    /**
     * 修改导入的实验数据， 如果testId不为空， 则会根据标准物质进行排序，先计算标准物质的
     *
     * @param dtoList
     * @return
     */
    @Override
    public boolean updateEntrustTestData(List<SampleInspectDataDTO> dtoList) {
        List<TestRecordSampleData> testRecordSampleDataList = TestRecordSampleDataConverter.dtoToEntityList(dtoList);
        if (super.updateBatchById(testRecordSampleDataList)) {
            TestRecord testRecord = testRecordService.getById(dtoList.get(0).getTestId());
            if (testRecord.getStatus().compareTo(2) < 0) {
                testRecordService.update(Wrappers.<TestRecord>lambdaUpdate().eq(TestRecord::getId, dtoList.get(0).getTestId()).set(TestRecord::getStatus, 2));
            }
            // 开启异步执行
//            CompletableFuture.runAsync(() -> {
            calculateInspectData(testRecordSampleDataList.stream().map(TestRecordSampleData::getId).collect(Collectors.toList()), testRecord.getBusinessType());
//            }).exceptionally(e -> {
//                log.error("计算保留时间以及相对误差出错！");
//                e.printStackTrace();
//                return null;
//            });
            return testRecordSampledataExpandService.updateBatchByDTO(dtoList.stream().flatMap(dto -> dto.getExpandList().stream()).collect(Collectors.toList()), testRecord.getId());
        }
        return false;
    }

    /**
     * 获取任务实验结果数据
     *
     * @param testId
     * @return
     */
    @Override
    public Map<String, JSONArray> queryTaskTestResult(String testId) {
        // 查询初2这个实验id下的实验数据，然后根据检材编号进行分组
        Map<String, List<TestRecordSampleData>> dataGroupBySampleNoMap = this
                .list(Wrappers.<TestRecordSampleData>lambdaQuery()
                        .eq(TestRecordSampleData::getTestId, testId)
                        // 对名称进行排序，因为字符串的排序中 22 < 3，所以需要转换进行怕排序
                        .last("ORDER BY SUBSTRING_INDEX(name, '-', -1) + 0"))
                .stream()
                .collect(Collectors.groupingBy(TestRecordSampleData::getSampleNo, LinkedHashMap::new, Collectors.toList()));
        if (CollUtil.isEmpty(dataGroupBySampleNoMap)) {
            return null;
        }
        Map<String, JSONArray> map = new HashMap<>();

        JSONArray headerArray = new JSONArray();
        JSONArray dataArray = new JSONArray();
        // 标记表头是否添加完成
        AtomicReference<Boolean> finish = new AtomicReference<>(false); // 为了能在lambda中使用
        dataGroupBySampleNoMap.forEach((k, v) -> {
            // 组装列表数据
            JSONObject dataObject = new JSONObject();
            dataObject.put("sampleNo", k);
            int size = v.size(); // 防止频繁调用方法
            for (int i = 0; i < size; i++) {
                TestRecordSampleData testRecordSampleData = v.get(i);
                String compoundKey = "compound" + (i + 1);
                if (!finish.get()) {
                    if (i == 0) {
                        headerArray.add(createHeaderObject("sampleNo", "溶液编号", false));
                    }
                    headerArray.add(createHeaderObject(compoundKey, "化合物" + "(" + testRecordSampleData.getCompoundName() + ")", false));
                }
                // 把化合物组装导对象里
                dataObject.put(compoundKey, testRecordSampleData.getSampleConcentration());
                if (i == size - 1) {
                    finish.set(true);
                }
            }
            dataArray.add(dataObject);
        });
        map.put("header", headerArray);
        map.put("data", dataArray);
        return map;
    }

    /**
     * 根据业务id获取任务实验结果数据
     *
     * @param businessId
     * @return
     */
    @Override
    public Map<String, JSONArray> queryTaskTestResultByBusiness(String businessId) {
        // 获取业务信息
        List<TestResultBusinessVO> testResultBusinessVOS = baseMapper.queryTestResultTaskList(Wrappers.<TestResultBusinessVO>query().eq("T.id", businessId));
        if (CollUtil.isEmpty(testResultBusinessVOS)) {
            throw new RuntimeException(String.format("业务id 为 %s 的数据在系统查询不到！", businessId));
        }
        TestResultBusinessVO testResultBusinessVO = testResultBusinessVOS.get(0);
        JSONArray compoundsJsonArray = JSONArray.parseArray(testResultBusinessVO.getCompounds()); // 获取头数组

        /**
         * 1 根据业务id查询到所有的检材
         * 2 通过查询到的检材id 关联查询到溶液
         * 3 通过溶液编号查询实验数据
         */
        List<DataSolutionSampleDTO> testDataListByBusiness = baseMapper
                .getTestDataListByBusiness(Wrappers.<DataSolutionSampleDTO>query().eq("si.business_id", businessId)
                        .orderByDesc("si.accept_no"))
                .stream()
                .filter(item -> StrUtil.isNotBlank(item.getSampleDataId()) && StrUtil.isNotBlank(item.getCompoundName())) // 筛选 检验数据不为空的检材数据
                .collect(Collectors.toList());
        // 根据化合物进行分组
        Map<String, List<DataSolutionSampleDTO>> dataGroupByCompoundMap = testDataListByBusiness.stream()
                .collect(Collectors.groupingBy(DataSolutionSampleDTO::getSampleNo, LinkedHashMap::new, Collectors.toList()));

        Map<String, JSONArray> map = new HashMap<>();

        JSONArray headerArray = new JSONArray();
        JSONArray dataArray = new JSONArray();
        // 标记表头是否添加完成
        AtomicReference<Boolean> finish = new AtomicReference<>(false); // 为了能在lambda中使用
        dataGroupByCompoundMap.forEach((k, v) -> {

            Map<String, DataSolutionSampleDTO> dataSolutionSampleDTOMap = v.stream()
                    .collect(Collectors
                            .toMap(DataSolutionSampleDTO::getCompoundName, Function.identity(), (v1, v2) -> v2)); // 化合物相同则后面的覆盖前面的值
            // 组装列表数据
            JSONObject dataObject = new JSONObject();
            dataObject.put("sampleNo", k);
            int size = compoundsJsonArray.size(); // 防止频繁调用方法
            for (int i = 0; i < size; i++) {
                JSONObject headerObject = compoundsJsonArray.getJSONObject(i);
                String dictValue = headerObject.getString("dictValue");
                String dictLabel = headerObject.getString("dictLabel").substring(0, 3);//把化合物后缀取消，例如化合物1

                // 第一次循环时添加头部信息
                if (!finish.get()) {
                    if (i == 0) {
                        headerArray.add(createHeaderObject("sampleNo", "溶液编号", false));
                    }
                    headerArray.add(createHeaderObject(dictValue, dictLabel + "(" + dictValue + ")", false));
                }
                // 设置标记，避免多次执行上面的代码
                if (i == size - 1) {
                    finish.set(true);
                }
                DataSolutionSampleDTO dto = dataSolutionSampleDTOMap.get(dictValue);
                // 显示的数据
//                if (dto != null) {
                // 把化合物组装导对象里
                dataObject.put(dictValue, dto != null ? dto.getSampleConcentration() : 0);
//                }
            }
            dataArray.add(dataObject);
        });
        map.put("header", headerArray);
        map.put("data", dataArray);
        return map;
    }

    /**
     * 保存实验数据的定量结果
     *
     * @param dto
     * @return
     */
    @Override
    public boolean saveQuantitativeResults(SaveQuantitativeResultsDTO dto) {
        TestRecordSampleData testRecordSampleData = this.getOne(Wrappers.<TestRecordSampleData>lambdaQuery()
                .eq(TestRecordSampleData::getCompoundName, dto.getCompoundName())
                .eq(StrUtil.isNotBlank(dto.getSampleName()), TestRecordSampleData::getName, dto.getSampleName())
                .eq(StrUtil.isNotBlank(dto.getSampleNo()), TestRecordSampleData::getSampleNo, dto.getSampleNo())
        );
        if (testRecordSampleData == null) {
            throw new RuntimeException(String.format("定量结果保存失败！%s 和 化合物 %s 对应数据不存在！",
                    StrUtil.isNotBlank(dto.getSampleName()) ? "上样编号 " + dto.getSampleName() : "检材编号 " + dto.getSampleNo(), dto.getCompoundName()));
        }
        validateTestStatus(testRecordSampleData.getTestId());
        JSONObject jsonObject = JSONObject.parseObject(testRecordSampleData.getDataResultJson());
        jsonObject.put("quantitativeResults", dto.getQuantitativeResults());
        return this.update(Wrappers.<TestRecordSampleData>lambdaUpdate()
                .eq(TestRecordSampleData::getId, testRecordSampleData.getId())
                .set(TestRecordSampleData::getDataResultJson, jsonObject.toJSONString()));
    }


    /**
     * 删除实验数据的定量结果
     *
     * @param testSampleDataId
     * @return
     */
    @Override
    public boolean deleteQuantitativeResults(String testSampleDataId) {
        TestRecordSampleData testRecordSampleData = this.getById(testSampleDataId);
        if (testRecordSampleData == null) {
            throw new RuntimeException(String.format("id 为 %s 的检验数据在系统中查询不到！", testSampleDataId));
        }
        validateTestStatus(testRecordSampleData.getTestId());
        JSONObject jsonObject = JSONObject.parseObject(testRecordSampleData.getDataResultJson());
        jsonObject.put("quantitativeResults", null);
        return this.update(Wrappers.<TestRecordSampleData>lambdaUpdate()
                .eq(TestRecordSampleData::getId, testRecordSampleData.getId())
                .set(TestRecordSampleData::getDataResultJson, jsonObject.toJSONString()));
    }

    /**
     * 标记该流程是否开始实验
     *
     * @param testId
     * @return
     */
    @Override
    public boolean whetherSave(String testId) {
        long count = this.count(Wrappers.<TestRecordSampleData>lambdaQuery().eq(TestRecordSampleData::getTestId, testId));
        return count > 0;
    }

    /**
     * 根据实验id生成定性结果
     *
     * @param businessId
     * @return 从1号和2号检材中均检出合成大麻素类物质 MDMB-4E-PINACA，
     * 未检出四氢大麻酚和 ADB-BUTINACA4F-MDMB-BUTICA、AMB-FUBICA、ADB-4E-PINACA、4F-MDMB-BUTINACA、SF-EDMB-PICA、
     * SF-ADB、SF-MPP-PICA4F-ABUTINACA、EDMB-PINACA、SCIAPINACA、4CN-CUMYL-BUTINACA、FUB-144。
     */
    @Override
    public String generateQualitativeResults(String businessId) {

        List<DataSolutionSampleDTO> dataSolutionSampleDTOS = baseMapper.queryDataSolutionSampleDTOList(Wrappers.<DataSolutionSampleDTO>query()
                .eq("si.business_id", businessId)
                .last("ORDER BY SUBSTRING_INDEX(rs.name, '-', -1) + 0"));
        Map<String, List<DataSolutionSampleDTO>> dataMap = dataSolutionSampleDTOS
                .stream()
                .collect(
                        Collectors.groupingBy(
                                item -> StrUtil.join("_", item.getCompoundName(), item.getIsDetected()),
                                LinkedHashMap::new,
                                Collectors.toList()
                        )
                );

        StringBuilder stringBuilder = new StringBuilder();
//        AtomicInteger index = new AtomicInteger(1); // 在lambda里进行递增
        if (dataMap.size() == 1) {
            dataMap.forEach((key, value) -> {
                DataSolutionSampleDTO first = value.get(0);
                stringBuilder
                        .append("\t")
//                        .append(index.getAndIncrement())
//                        .append("、")
                        .append(first.getOrderNo()).append("号");
                if (value.size() > 1) {
                    stringBuilder.append("至").append(value.get(value.size() - 1).getOrderNo()).append("号");
                }
                stringBuilder.append("检材")
                        .append(first.getIsDetected() == 1 ? TestRecordSampleDataConstant.CHECK_OUT : TestRecordSampleDataConstant.NOT_CHECK_OUT)
                        .append(first.getCompoundName())
                        .append("\n");
            });
        } else {
            dataMap.forEach((key, value) -> {
                DataSolutionSampleDTO first = value.get(0);
                stringBuilder
                        .append("\t")
//                        .append(index.getAndIncrement())
//                        .append("、")
                        .append(value.stream().map(item -> item.getOrderNo() + "号检材").collect(Collectors.joining("、")));
                stringBuilder.append(first.getIsDetected() == 1 ? TestRecordSampleDataConstant.CHECK_OUT : TestRecordSampleDataConstant.NOT_CHECK_OUT)
                        .append(first.getCompoundName())
                        .append("。\n");
            });
        }

        return stringBuilder.toString();
    }


    /**
     * 完成实验
     *
     * @param testId
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R finishTest(String testId) {
        TestRecord testRecord = testRecordService.getById(testId);
        if (testRecord == null) {
            return R.failed(String.format("id 为 %s 的数据不存在！", testId));
        }
        // 更新实验状态
        if (testRecordService.update(Wrappers.<TestRecord>lambdaUpdate()
                .eq(TestRecord::getId, testId)
                .set(TestRecord::getStatus, 5)
                .set(TestRecord::getTestEndDate, LocalDate.now()))) {
            // 获取业务id
            List<DataSolutionSampleDTO> dataSolutionSampleDTOS = baseMapper.queryDataSolutionSampleDTOList(Wrappers.<DataSolutionSampleDTO>query()
                    .in("si.id", testRecord.getSampleTestList()));
            if (CollUtil.isEmpty(dataSolutionSampleDTOS)) {
                throw new RuntimeException("检验数据和检材不匹配！");
            }
            Set<String> businessIdSet = dataSolutionSampleDTOS.stream().map(DataSolutionSampleDTO::getBusinessId).collect(Collectors.toSet());
            // 获取业务信息
            List<TestResultBusinessVO> testResultBusinessVOS = baseMapper.queryTestResultTaskList(Wrappers.<TestResultBusinessVO>query().in("T.id", businessIdSet));
            // 进行分组
            Map<Integer, List<TestResultBusinessVO>> testResultBusinessVOTypeMap = testResultBusinessVOS.stream().collect(Collectors.groupingBy(TestResultBusinessVO::getType));
            testResultBusinessVOTypeMap.forEach((k, v) -> {
                if (k == 10000 && CollUtil.isNotEmpty(v)) {
                    List<String> entrustIds = v.stream().map(TestResultBusinessVO::getId).collect(Collectors.toList());
                    if (!entrustInfoService.update(Wrappers.<EntrustInfo>lambdaUpdate()
                            .in(EntrustInfo::getId, entrustIds).set(EntrustInfo::getStatus, 3))) {
                        throw new RuntimeException("委托状态更新失败！");
                    }
                }
            });
        } else {
            throw new RuntimeException("实验状态更新失败！");
        }

        // 发布事件
        applicationContext.publishEvent(new FinishTestExecutionEvent(this));
        if(testRecord.getBusinessType().equals(BusinessType.BOINT_CASE.getBusinessType())) {
            applicationContext.publishEvent(new PushDataToLabsCareEvent(this, testId, 3));
        } else if (testRecord.getBusinessType().equals(BusinessType.NPS_CASE.getBusinessType())) {
            applicationContext.publishEvent(new PushDataToLabsCareEvent(this, testId, 4));
        }
        return R.ok("完成实验成功！");
    }

    /**
     * 审核审批数据
     *
     * @param auditDataDTO
     * @param httpServletRequest
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R auditTaskTestData(AuditDataDTO auditDataDTO, HttpServletRequest httpServletRequest) {
        // 标记操作是否成功
        boolean success = true;
        try {
            // 获取参数中所有编号的检验数据
            Map<String, List<TestRecordSampleData>> testRecordSampleDataMap = this.list(Wrappers.<TestRecordSampleData>lambdaQuery()
                            .in(TestRecordSampleData::getSampleNo, auditDataDTO.getSampleNoList()))
                    .stream()
                    .collect(Collectors.groupingBy(TestRecordSampleData::getSampleNo));
            // 对执行数据进行校验
            testRecordSampleDataMap.forEach((k, v) -> {
                TestRecordSampleData testRecordSampleData = v.get(0);
                if (auditDataDTO.getOpCode().equals(TaskTestDataStatus.REVIEWED.getCode())) {
                    if (testRecordSampleData.getStatus() > TaskTestDataStatus.REVIEWED.getCode()) {
                        throw new RuntimeException(String.format("编号为 %s 的数据已经审核过，不能重复审核！", k));
                    }
                } else if (auditDataDTO.getOpCode().equals(TaskTestDataStatus.APPROVED.getCode())) {
                    if (testRecordSampleData.getStatus() > TaskTestDataStatus.APPROVED.getCode()) {
                        throw new RuntimeException(String.format("编号为 %s 的数据已经审批过，不能重复审批！", k));
                    }
                }
            });

            switch (TaskTestDataStatus.getEnumByCode(auditDataDTO.getOpCode())) {
//            case REJECT:
//                success = this.update(Wrappers.<TestRecordSampleData>lambdaUpdate()
//                        .in(TestRecordSampleData::getSampleNo, auditDataDTO.getSampleNoList())
//                        .set(TestRecordSampleData::getStatus, TaskTestDataStatus.REJECT.getCode()));
//                break;
                case REVIEWED:
                    success = this.update(Wrappers.<TestRecordSampleData>lambdaUpdate()
                            .in(TestRecordSampleData::getSampleNo, auditDataDTO.getSampleNoList())
                            .set(TestRecordSampleData::getStatus, TaskTestDataStatus.REVIEWED.getCode()));

                    break;
                case APPROVED:
                    success = this.update(Wrappers.<TestRecordSampleData>lambdaUpdate()
                            .in(TestRecordSampleData::getSampleNo, auditDataDTO.getSampleNoList())
                            .set(TestRecordSampleData::getStatus, TaskTestDataStatus.APPROVED.getCode()));
                    List<String> sampleNoList = auditDataDTO.getSampleNoList();
//                taskInfoService.createUploadItem(sampleNoList);
                    applicationContext.publishEvent(new AuditDataExecutionEvent(this, sampleNoList));
                    break;

            }
        } catch (Exception e) {
            e.printStackTrace();
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
        if (success) {
            return R.ok(true, "操作成功");
        } else {
            return R.failed(false, "操作失败！");
        }
    }

    //获取标准品样品信息数据
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean saveTestDataFromNps(List<NPSDataFileStruct> npsDataFileStructList, String testId) {
//        try {
        buildSampleDataFromNps(npsDataFileStructList, testId);//构造实验数据
        testRecordService.update(Wrappers.<TestRecord>lambdaUpdate().eq(TestRecord::getId, testId).set(TestRecord::getStatus, 2));
        return true;
//        } catch (Exception err) {
//            log.info("程序执行异常{}", err.getMessage());
//            err.printStackTrace();
//            return false;
//        }
    }

    /**
     * 毛发案件检验数据持久化到数据库
     *
     * @param hairCompoundDataMap
     * @param testId
     * @return
     */
    private List<TestRecordSampleData> buildSampleDataFromHairCase(Map<String, List<HairSewageCompoundData>> hairCompoundDataMap, String testId) {
        List<HairSewageDataDto> testRecordSampleDataList_All = new Vector<>();
        List<HairSewageDataDto> testRecordSampleDataList_Std = new Vector<>();//如果筛查物是多个，就可能出现多个标准品的数据
        List<HairSewageDataDto> testRecordSampleDataList_Analyte = new Vector<>();//检材样品的数据

        hairCompoundDataMap.forEach((k, v) -> {
            v.parallelStream().forEach(hairCompoundData -> {
                String sampleName = hairCompoundData.getSampleName();
                String idName = hairCompoundData.getIdName();
                HairSewageDataDto hairSewageDataDto = new HairSewageDataDto();
                hairSewageDataDto.setSampleNo(idName); // 样本编号
                hairSewageDataDto.setSampleName(sampleName); // 样本名称
                hairSewageDataDto.setTestId(testId); // 实验id
                // 设置对应的元素数据
                hairSewageDataDto.setTestSampleDataList(Lists.newArrayList(hairCompoundData));
                hairSewageDataDto.setCompoundName(k); // 化合物名称

                hairSewageDataDto.setBusinessType(BusinessType.BOINT_CASE.getBusinessType());
                completeHairDataInfo(hairCompoundData, hairSewageDataDto);
                if (idName.contains(TestRecordSampleDataConstant.SAMPLE_TYPE_STD)) {
                    hairSewageDataDto.setRtTimeError(TestRecordSampleDataConstant.INVALID_VALUE); // 标准溶液的相对误差为 /
                    hairSewageDataDto.setRtTimeWithinError(TestRecordSampleDataConstant.IS);
                    hairSewageDataDto.setIonAbundanceRatioWithinError(TestRecordSampleDataConstant.INVALID_VALUE); // 离子丰度比相对误差
                    hairSewageDataDto.setWhetherCheckOut(TestRecordSampleDataConstant.IS);
                    hairSewageDataDto.setStdRtTime(hairSewageDataDto.getTargetRtTime());
                    hairSewageDataDto.setSampleType(TestRecordSampleDataConstant.SAMPLE_TYPE_STD);
                    testRecordSampleDataList_Std.add(hairSewageDataDto);//放标准品的列表
                } else {
                    hairSewageDataDto.setSampleType(
                            idName.startsWith(TestRecordSampleDataConstant.SAMPLE_TYPE_QC) ?
                                    TestRecordSampleDataConstant.SAMPLE_TYPE_QC : TestRecordSampleDataConstant.SAMPLE_TYPE_ANALYTE);
//                    hairSewageDataDto.setTargetRtTime(rtTime); // 目标物保留时间
                    testRecordSampleDataList_Analyte.add(hairSewageDataDto);//放被分析物
                }
                testRecordSampleDataList_All.add(hairSewageDataDto);//所有的样本的数据
            });
        });
        // 计算相对误差喝离子丰度比相对误差，并判断是否符合
        settingHairCaseStdConcentration(testRecordSampleDataList_Std, testId);
        settingHairCaseRelativeErrorAnalyte(testRecordSampleDataList_Std, testRecordSampleDataList_Analyte, testId);

        // 组装数据持久化到数据库
        List<TestRecordSampleData> testRecordSampleDataStdList = genPersistenceSampleData(testRecordSampleDataList_Std);
        List<TestRecordSampleData> testRecordSampleDataAnalyteList = genPersistenceSampleData(testRecordSampleDataList_Analyte);
        // 合并数组
        List<TestRecordSampleData> mergeList = new ArrayList<>();
        mergeList.addAll(testRecordSampleDataStdList);
        mergeList.addAll(testRecordSampleDataAnalyteList);
        // 批量保存到数据库
        this.saveBatch(mergeList);
        return mergeList;
    }

    /**
     * 补全毛发检验数据信息
     *
     * @param hairCompoundData
     * @param hairSewageDataDto
     */
    private void completeHairDataInfo(HairSewageCompoundData hairCompoundData, HairSewageDataDto hairSewageDataDto) {
        hairSewageDataDto.setQualitativeIonPairUp(hairCompoundData.getQuanTrace()); // 定性离子对
        hairSewageDataDto.setQualitativeIonPairDown(hairCompoundData.getTrace1());
        hairSewageDataDto.setPeakAreaUp(hairCompoundData.getArea() == null ? TestRecordSampleDataConstant.INVALID_VALUE : hairCompoundData.getArea()); // 峰面积
        hairSewageDataDto.setPeakAreaDown(hairCompoundData.getArea1() == null ? TestRecordSampleDataConstant.INVALID_VALUE : hairCompoundData.getArea1());

        // 封装定性离子对和峰面积
        Map<String, Object> upMap = new HashMap<>();
        upMap.put("qualitativeIonPair", hairSewageDataDto.getQualitativeIonPairUp());
        upMap.put("peakArea", hairSewageDataDto.getPeakAreaUp());
        Map<String, Object> downMap = new HashMap<>();
        downMap.put("qualitativeIonPair", hairSewageDataDto.getQualitativeIonPairDown());
        downMap.put("peakArea", hairSewageDataDto.getPeakAreaDown());
        hairSewageDataDto.setMaps(Lists.newArrayList(upMap, downMap));
        // 离子丰度比
        if (hairCompoundData.getRatioActual() == null) {
            if (hairCompoundData.getArea() != null && hairCompoundData.getArea1() != null) {
                hairSewageDataDto.setIonAbundanceRatio(hairCompoundData.getArea1() / hairCompoundData.getArea() * 100);
            } else {
                hairSewageDataDto.setIonAbundanceRatio(TestRecordSampleDataConstant.INVALID_VALUE);
            }
        } else {
            // 从文件中获取到的丰度比并没有乘以100
            hairSewageDataDto.setIonAbundanceRatio(hairCompoundData.getRatioActual() * 100);
        }
        Double rtTime = hairCompoundData.getRtTime() == null ? TestRecordSampleDataConstant.INVALID_VALUE : hairCompoundData.getRtTime(); // 取文件的保留时间
        hairSewageDataDto.setTargetRtTime(rtTime); // 目标物保留时间, 标准物质的保留时间同时存在 TargetRtTime 和 StdRtTime
    }

    /**
     * 计算相对误差喝离子丰度比相对误差，并判断是否符合
     *
     * @param testRecordSampleDataListStd
     * @param testRecordSampleDataListAnalyte
     * @param testId
     * @return
     */
    private List<HairSewageDataDto> settingHairCaseRelativeErrorAnalyte(List<HairSewageDataDto> testRecordSampleDataListStd,
                                                                        List<HairSewageDataDto> testRecordSampleDataListAnalyte, String testId) {

        List<ResultConcentrationVO> resultConcentrationVOList = sampleInjectorMapper.queryResultConcentrationList(
                Wrappers.<ResultConcentrationVO>query().eq("T.test_id", testId));
        Map<String, HairSewageDataDto> stdMap = testRecordSampleDataListStd.stream()
                .collect(Collectors.toMap(HairSewageDataDto::getCompoundName, Function.identity()));

        testRecordSampleDataListAnalyte.parallelStream().forEach(hairSewageDataDto -> {
            List<ResultConcentrationVO> testRecordSampleSolutionlist = resultConcentrationVOList.stream()
                    .filter(item -> item.getNumber().equals(hairSewageDataDto.getSampleNo())).collect(Collectors.toList());
            /// 设置目标物浓度
            if (CollUtil.isNotEmpty(testRecordSampleSolutionlist)) {
                hairSewageDataDto.setStdConcentration(testRecordSampleSolutionlist.get(0).getResultConcentration());
            } else {
                log.info("没有找到该样本溶液，检验的化合物 {} 样本编号 {}", hairSewageDataDto.getCompoundName(), hairSewageDataDto.getSampleNo());
                hairSewageDataDto.setStdConcentration("/");
            }

            // 设置标准物质浓度
            HairSewageDataDto hairSewageDataDtoStd = stdMap.get(hairSewageDataDto.getCompoundName()); // 获取化合物下的标准物质信息
            if (hairSewageDataDtoStd != null) {
                // 计算相对误差  (目标物保留时间 - 标准物保留时间) / 标准物质保留时间 * 100
                processInspectDataService.calculateHairCaseRtTimeError(hairSewageDataDto, hairSewageDataDtoStd);
                // 计算离子丰度比 相对偏差 (目标物离子丰度比 - 标准物离子丰度比) / 标准物质离子丰度比 * 100
                hairSewageDataDto.setIonAbundanceRatioWithinError(processInspectDataService.getAbundanceRatioErrorValue(hairSewageDataDto.getIonAbundanceRatio(), hairSewageDataDtoStd.getIonAbundanceRatio()));
                hairSewageDataDto.setWhetherCheckOut(processInspectDataService.calculateHairCaseIonAbundanceRatioWithinError(hairSewageDataDto.getIonAbundanceRatioWithinError(), hairSewageDataDtoStd.getIonAbundanceRatio()));
                hairSewageDataDto.setIsDetected(hairSewageDataDto.getWhetherCheckOut().equals(TestRecordSampleDataConstant.IS) ? 1 : 0);
            }
        });
        return testRecordSampleDataListAnalyte;
    }


    /**
     * 设置标准物的溶液浓度
     *
     * @param testRecordSampleDataListStd
     * @param testId
     * @return
     */
    private List<HairSewageDataDto> settingHairCaseStdConcentration(List<HairSewageDataDto> testRecordSampleDataListStd, String testId) {
        List<TestRecordStandardSolution> testRecordStandardSolutionList = testRecordStandardSolutionService.list(
                Wrappers.<TestRecordStandardSolution>lambdaQuery()
                        .eq(TestRecordStandardSolution::getTestId, testId));
        // 根据编号转map 方便获取
        Map<String, TestRecordStandardSolution> testRecordStandardSolutionMap = testRecordStandardSolutionList
                .stream()
                .collect(Collectors.toMap(TestRecordStandardSolution::getNumber, Function.identity()));
        for (HairSewageDataDto hairSewageDataDto : testRecordSampleDataListStd) {
            //设置标准品的浓度
            TestRecordStandardSolution testRecordStandardSolution = testRecordStandardSolutionMap.get(hairSewageDataDto.getSampleNo());
            if (ObjectUtil.isNotEmpty(testRecordStandardSolution)) {
                hairSewageDataDto.setStdConcentration(testRecordStandardSolution.getResultConcentration());//设置标准品的浓度
            } else {
                log.info("没有找到该标准溶液，实验ID {} {}", hairSewageDataDto.getCompoundName(), hairSewageDataDto.getSampleNo());
                hairSewageDataDto.setStdConcentration("/");
            }
        }
        return testRecordSampleDataListStd;
    }

    /**
     * 构建数据库持久化对象
     *
     * @param npsDataFileStructList
     * @param testId
     * @return
     */
    private List<TestRecordSampleData> buildSampleDataFromNps(List<NPSDataFileStruct> npsDataFileStructList, String testId) {
        List<NPSCaseTestDataDto> testRecordSampleDataList_All = new ArrayList<>();
        List<NPSCaseTestDataDto> testRecordSampleDataList_Std = new ArrayList<>();//如果筛查物是多个，就可能出现多个标准品的数据
        List<NPSCaseTestDataDto> testRecordSampleDataList_Analyte = new ArrayList<>();//检材样品的数据

        //一批文件中含有多个样本，将一个样本的检验数据封装到一个NPSCaseTestDataDto对象中
        //查出这批实验中的标准品数据，并将NPSDataFileStruct 转化为NPSCaseTestDataDto对象
        npsDataFileStructList.forEach(item -> {
            String sampleNo = item.getSi_SampleId();
            String sampleType = item.getSi_SampleType();
            //离子丰度比数据
            NPSCaseTestDataDto npsCaseTestDataDto = getNpsCaseTestDataDto(testId, item, sampleNo);
            if (sampleType.equals(TestRecordSampleDataConstant.SAMPLE_TYPE_STD)) {
                testRecordSampleDataList_Std.add(npsCaseTestDataDto);//放标准品的列表
            } else {
                testRecordSampleDataList_Analyte.add(npsCaseTestDataDto);//放被分析物
            }
            testRecordSampleDataList_All.add(npsCaseTestDataDto);//所有的样本的数据
        });
        //设置计算保留时间和丰度比
        settingAbundanceRatioStd(testRecordSampleDataList_Std, testId);
        settingAbundanceRatioAnalyte(testRecordSampleDataList_Analyte, testRecordSampleDataList_Std, testId);
        //构造持久化到数据库的对象
        List<TestRecordSampleData> testRecordSampleDataList_std = genPersistenceSampleData(testRecordSampleDataList_Std);
        List<TestRecordSampleData> testRecordSampleDataList_analyte = genPersistenceSampleData(testRecordSampleDataList_Analyte);
        this.saveBatch(testRecordSampleDataList_std);
        this.saveBatch(testRecordSampleDataList_analyte);
        List<TestRecordSampleData> retList = new ArrayList<>();
        retList.addAll(testRecordSampleDataList_std);
        retList.addAll(testRecordSampleDataList_analyte);
        return retList;
    }

    /**
     * 生成npsDTO
     *
     * @param testId
     * @param item
     * @param sampleNo
     * @return
     */
    private NPSCaseTestDataDto getNpsCaseTestDataDto(String testId, NPSDataFileStruct item, String sampleNo) {
        List<NPSTestDetailDataStruct> testDetailDataList = item.getChildDataList();
        NPSCaseTestDataDto npsCaseTestDataDto = new NPSCaseTestDataDto();
        npsCaseTestDataDto.setSampleName(item.getSi_SampleName());
        npsCaseTestDataDto.setSampleNo(sampleNo);
        npsCaseTestDataDto.setTestId(testId);
        npsCaseTestDataDto.setTestSampleDataList(testDetailDataList);
        npsCaseTestDataDto.setCompoundName(testDetailDataList.get(0).getName());
        npsCaseTestDataDto.setBusinessType(BusinessType.NPS_CASE.getBusinessType()); // 设置业务类型
        return npsCaseTestDataDto;
    }

    /**
     * 构造持久化到数据库的对象
     * 使用了通配符 <? extends BaseCaseDataDto> 来接收不同类型的数据对象，并通过 instanceof 关键字进行类型判断以获取对应的属性值。
     * 这样可以避免重复的代码逻辑，提高代码的可维护性和复用性
     *
     * @param npsCaseTestDataDtoList
     * @return
     */
    public List<TestRecordSampleData> genPersistenceSampleData(List<? extends BaseCaseDataDto> npsCaseTestDataDtoList) {
        List<TestRecordSampleData> persistenceSampleDataList = Collections.synchronizedList(new ArrayList<>());
        List<TestRecordSampleDataExpand> dataExpandList = Collections.synchronizedList(new ArrayList<>());
        npsCaseTestDataDtoList.parallelStream().forEach(item -> {

            TestRecordSampleData testRecordSampleData = new TestRecordSampleData();
            testRecordSampleData.setId(IdWorker.getIdStr());
            testRecordSampleData.setTestId(item.getTestId());
//            testRecordSampleData.setSampleNo(item.getSampleId());
            if (item instanceof HairSewageDataDto) {
                HairSewageDataDto hairSewageDataDto = (HairSewageDataDto) item;
                testRecordSampleData.setName(hairSewageDataDto.getSampleName());
                testRecordSampleData.setSampleNo(hairSewageDataDto.getSampleNo());
                dataExpandList.addAll(testRecordSampledataExpandService.saveHairSewageInspectExpData(hairSewageDataDto, testRecordSampleData.getId()));
            } else if (item instanceof NPSCaseTestDataDto) {
                NPSCaseTestDataDto npsCaseTestDataDto = (NPSCaseTestDataDto) item;
                testRecordSampleData.setName(npsCaseTestDataDto.getSampleName());
                testRecordSampleData.setSampleNo(npsCaseTestDataDto.getSampleNo());
                dataExpandList.addAll(testRecordSampledataExpandService.saveNPSInspectExpData(npsCaseTestDataDto, testRecordSampleData.getId()));
            }
            testRecordSampleData.setSampleConcentration(item.getTargetConcentration());
            testRecordSampleData.setStdConcentration(item.getStdConcentration());
            testRecordSampleData.setTargetRtTime(item.getTargetRtTime());//检材保留时间
            testRecordSampleData.setStdRtTime(item.getStdRtTime());//标准品保留时间
            testRecordSampleData.setCompoundName(item.getCompoundName());
            testRecordSampleData.setRtTimeWithinError(item.getRtTimeWithinError());
            testRecordSampleData.setIsDetected(item.getIsDetected());
            testRecordSampleData.setSampleType(item.getSampleType());
            String dataJsonStr = null;
            String dataResultJsonStr = null;
            try {
                dataJsonStr = JSON.toJSONString(item.getTestSampleDataList());
            } catch (Exception e) {
                e.printStackTrace();
            }
            try {
                dataResultJsonStr = JSON.toJSONString(item);
            } catch (Exception e) {
                e.printStackTrace();
            }
            testRecordSampleData.setDataJson(dataJsonStr);
            testRecordSampleData.setDataResultJson(dataResultJsonStr);
            persistenceSampleDataList.add(testRecordSampleData);
        });
        testRecordSampledataExpandService.saveBatch(dataExpandList);
        return persistenceSampleDataList;
    }

    //计算标准品的丰度比
    private List<NPSCaseTestDataDto> settingAbundanceRatioStd(List<NPSCaseTestDataDto> testRecordSampleDataListStd, String testId) {
        //
        if (CollUtil.isEmpty(testRecordSampleDataListStd)) {
            throw new RuntimeException("未检测到数据文件中的标准品溶液！");
        }
//        Optional<String> testId = testRecordSampleDataListStd.stream().map(item -> item.getTestId()).findFirst();
        List<TestRecordStandardSolution> testRecordStandardSolutionList = testRecordStandardSolutionService.list(
                Wrappers.<TestRecordStandardSolution>lambdaQuery()
                        .eq(TestRecordStandardSolution::getTestId, testId));

        for (NPSCaseTestDataDto npsCaseTestDataDto : testRecordSampleDataListStd) {
            //设置标准品的浓度
            List<TestRecordStandardSolution> testRecordStandardSolution = testRecordStandardSolutionList.stream()
                    .filter(item -> item.getNumber().equals(npsCaseTestDataDto.getSampleNo())).collect(Collectors.toList());
            if (testRecordStandardSolution.size() > 0) {
                npsCaseTestDataDto.setStdConcentration(testRecordStandardSolution.get(0).getResultConcentration());//设置标准品的浓度
            } else {
                log.info("没有找到该标准溶液，实验ID {} {}", npsCaseTestDataDto.getCompoundName(), npsCaseTestDataDto.getSampleNo());
                npsCaseTestDataDto.setStdConcentration("/");
            }

            //根据化合物名称查出谁是基峰
            List<NPSTestDetailDataStruct> testSampleDataList = npsCaseTestDataDto.getTestSampleDataList();
            NPSTestDetailDataStruct npsTestDetailDataStruct_Base = getBasePeakMass(testSampleDataList);
            if (npsTestDetailDataStruct_Base == null) {
                throw new RuntimeException("在系统中没有找到对应的化合物基峰！");
            }
            double sumRetTime = 0;
            for (NPSTestDetailDataStruct npsTestDetailDataStruct : testSampleDataList) {
                //求保留时间
                sumRetTime = sumRetTime + npsTestDetailDataStruct.getRetTime();
                if (!npsTestDetailDataStruct.getId().equals(npsTestDetailDataStruct_Base.getId())) {
                    //计算丰度比
                    double mzValue = npsTestDetailDataStruct.getArea() / npsTestDetailDataStruct_Base.getArea() * 100;
                    npsTestDetailDataStruct.setAbundanceRatio(mzValue);
                } else {
                    npsTestDetailDataStruct.setAbundanceRatio(TestRecordSampleDataConstant.INVALID_VALUE);
                    npsTestDetailDataStruct.setIsBasePeak(1);
                }

            }
            double sampleRetTime = sumRetTime / testSampleDataList.size();
            npsCaseTestDataDto.setStdRtTime(sampleRetTime);//设置样品的保留时间
            npsCaseTestDataDto.setSampleType(TestRecordSampleDataConstant.SAMPLE_TYPE_STD);

        }
        return testRecordSampleDataListStd;
    }

    /**
     * 计算待测样品的保留时间和丰度比
     *
     * @param testRecordSampleDataList
     * @param testId
     */
    private List<NPSCaseTestDataDto> settingAbundanceRatioAnalyte(List<NPSCaseTestDataDto> testRecordSampleDataList, List<NPSCaseTestDataDto> testRecordSampleDataList_std, String testId) {
        //因为要读样本的浓度，所以我们把该实验下所有样本信息拿过来
//        Optional<String> testId = testRecordSampleDataList.stream().map(item -> item.getTestId()).findFirst();
        List<TestRecordSampleSolution> sampleSolutionlist = testRecordSampleSolutionService.list(Wrappers.<TestRecordSampleSolution>lambdaQuery()
                .eq(TestRecordSampleSolution::getTestId, testId));

        for (NPSCaseTestDataDto npsCaseTestDataDto : testRecordSampleDataList) {

            List<TestRecordSampleSolution> testRecordSampleSolutionlist = sampleSolutionlist.stream()
                    .filter(item -> item.getSampleNo().equals(npsCaseTestDataDto.getSampleNo())).collect(Collectors.toList());
            if (testRecordSampleSolutionlist.size() > 0) {
                npsCaseTestDataDto.setTargetConcentration(testRecordSampleSolutionlist.get(0).getResultConcentration());//设置标准品的浓度
            } else {
                log.info("没有找到该样本溶液，检验的化合物 {} 样本编号 {}", npsCaseTestDataDto.getCompoundName(), npsCaseTestDataDto.getSampleNo());
                npsCaseTestDataDto.setTargetConcentration("/");
            }
            //设置标准物质的浓度
            List<NPSCaseTestDataDto> stdSolution = testRecordSampleDataList_std.stream()
                    .filter(item -> item.getCompoundName().equals(npsCaseTestDataDto.getCompoundName()))
                    .collect(Collectors.toList());
            if (stdSolution.size() > 0) {
                npsCaseTestDataDto.setStdConcentration(stdSolution.get(0).getStdConcentration());
            } else {
                log.info("没有找到该标准溶液，化合物 {} 样本ID {}", npsCaseTestDataDto.getCompoundName(), npsCaseTestDataDto.getSampleNo());
                npsCaseTestDataDto.setStdConcentration("/");
            }
            // 取出这个子列表
            List<NPSTestDetailDataStruct> testSampleDataList = npsCaseTestDataDto.getTestSampleDataList();
            List<NPSTestDetailDataStruct> notIdentifiedList = testSampleDataList.stream().filter(o -> o.isNotIdentified()).collect(Collectors.toList());
            if (CollUtil.isEmpty(notIdentifiedList)) { // 没有未检出的数据
                //根据化合物名称查出谁是基峰
                NPSTestDetailDataStruct npsTestDetailDataStruct_Base = getBasePeakMass(testSampleDataList);
                double sumRetTime = 0;
                //定义1个List来保存除基峰之外的其他碎片峰
                List<NPSTestDetailDataStruct> otherList = new ArrayList<>();
                for (NPSTestDetailDataStruct npsTestDetailDataStruct : testSampleDataList) {
                    //求保留时间
                    sumRetTime = sumRetTime + npsTestDetailDataStruct.getRetTime();
                    if (!npsTestDetailDataStruct.getId().equals(npsTestDetailDataStruct_Base.getId())) {
                        //计算丰度比
                        double mzValue = npsTestDetailDataStruct.getArea() / npsTestDetailDataStruct_Base.getArea() * 100;
                        npsTestDetailDataStruct.setAbundanceRatio(mzValue);
                        //设置误差范围，基峰不用设置误差范围
                        BigDecimal errorRange = processInspectDataService.getErrorRange(BigDecimal.valueOf(mzValue));
                        npsTestDetailDataStruct.setErrorRange(errorRange.doubleValue());
                        //设置标准品的丰度比
                        double stdCorrespondingAbundanceRatio = getStdCorrespondingAbundanceRatio(npsTestDetailDataStruct.getMass(),
                                npsTestDetailDataStruct.getName(), testRecordSampleDataList_std);
                        npsTestDetailDataStruct.setAbundanceRatio_std(stdCorrespondingAbundanceRatio);
                        //设置偏差，公式是 样品-标准品/标准品
                        double abundanceRatioErrorValue = processInspectDataService.getAbundanceRatioErrorValue(npsTestDetailDataStruct.getAbundanceRatio(),
                                npsTestDetailDataStruct.getAbundanceRatio_std());
                        npsTestDetailDataStruct.setAbundanceRatioError(abundanceRatioErrorValue);
                        //设置是否在误差范围内
                        String withinErrorText = processInspectDataService.getWithinErrorText(BigDecimal.valueOf(npsTestDetailDataStruct.getAbundanceRatioError()), BigDecimal.valueOf(npsTestDetailDataStruct.getErrorRange()));
                        npsTestDetailDataStruct.setWithinError(withinErrorText);
                        otherList.add(npsTestDetailDataStruct);
                    } else {
                        npsTestDetailDataStruct.setAbundanceRatio(TestRecordSampleDataConstant.INVALID_VALUE);//设置检材丰度比
                        npsTestDetailDataStruct.setAbundanceRatio_std(TestRecordSampleDataConstant.INVALID_VALUE);//设置标准物质丰度比
                        npsTestDetailDataStruct.setErrorRange(TestRecordSampleDataConstant.INVALID_VALUE);//设置丰度比偏差
                        npsTestDetailDataStruct.setAbundanceRatioError(TestRecordSampleDataConstant.INVALID_VALUE);//设置偏差范围
                        npsTestDetailDataStruct.setWithinError("/");//设置是否在误差范围内
                        npsTestDetailDataStruct.setIsBasePeak(1);//设置他是基峰
                    }

                }
                double sampleRetTime = sumRetTime / testSampleDataList.size();
                npsCaseTestDataDto.setTargetRtTime(sampleRetTime);//设置样品的保留时间
                //设置标准品的保留时间
                double stdRtTime = findStdRtTime(npsCaseTestDataDto.getTestSampleDataList().get(0).getName(), testRecordSampleDataList_std);
                npsCaseTestDataDto.setStdRtTime(stdRtTime);
                //设置保留时间偏差是否在范围 ±1 范围内  公式是  (Rt样品-标品)/标品 *100%
                double tmp1 = (sampleRetTime - stdRtTime) / stdRtTime;
                tmp1 = Math.abs(tmp1) * 100;
                npsCaseTestDataDto.setRtTimeError(tmp1);
                String rtWithinErrorText = processInspectDataService.getRtWithinErrorText(tmp1, 1);
                npsCaseTestDataDto.setRtTimeWithinError(rtWithinErrorText);
                //设置碎片峰的结果,条件是 除了基峰外的所有特征碎片都满足 离子丰度误差，才算是检出条件之一
                int isOk = checkOtherFragmentResult(otherList);
                npsCaseTestDataDto.setIsDetected(isOk);//1是检出，0是未检出
                npsCaseTestDataDto.setWhetherCheckOut(isOk == 1 ?
                        TestRecordSampleDataConstant.CHECK_OUT + npsCaseTestDataDto.getCompoundName() :
                        TestRecordSampleDataConstant.NOT_CHECK_OUT + npsCaseTestDataDto.getCompoundName());
                npsCaseTestDataDto.setSampleType(TestRecordSampleDataConstant.SAMPLE_TYPE_ANALYTE);
            } else {
                npsCaseTestDataDto.setTargetRtTime(TestRecordSampleDataConstant.INVALID_VALUE);//设置样品的保留时间
                npsCaseTestDataDto.setStdRtTime(TestRecordSampleDataConstant.INVALID_VALUE);
                npsCaseTestDataDto.setRtTimeError(TestRecordSampleDataConstant.INVALID_VALUE);
                npsCaseTestDataDto.setRtTimeWithinError("/");
                npsCaseTestDataDto.setIsDetected(0);//1是检出，0是未检出
                npsCaseTestDataDto.setWhetherCheckOut(TestRecordSampleDataConstant.NOT_CHECK_OUT + npsCaseTestDataDto.getCompoundName());
                npsCaseTestDataDto.setSampleType(TestRecordSampleDataConstant.SAMPLE_TYPE_ANALYTE);
            }
        }
        return testRecordSampleDataList;
    }

    @Resource
    private RemoteDictDataService remoteDictDataService;

    /**
     * 根据化合物名称找出基峰的对应的数据
     *
     * @param testSampleDataList
     * @return 如果没找到 返回null
     */
    private NPSTestDetailDataStruct getBasePeakMass(List<NPSTestDetailDataStruct> testSampleDataList) {
        NPSTestDetailDataStruct npsTestDetailDataStruct_Base = null;
        R<Map<String, DlpDictData>> r = remoteDictDataService.getDictDataListByDictType(TestRecordSampleDataConstant.COMPOUND_BASIC_PEAK);
        if (r.getCode() == CommonConstants.FAIL) {
            log.error(LocalDateTime.now() + "从字典中获取化合物基峰数据失败！");
            throw new RuntimeException(LocalDateTime.now() + "从字典中获取化合物基峰数据失败！");
        }
        Map<String, DlpDictData> compoundBasicPeak = r.getData(); // 这里我们需要注意的是化合物基峰的值我们存储在备注里
        for (NPSTestDetailDataStruct npsTestDetailDataStruct : testSampleDataList) {
            String compoundName = npsTestDetailDataStruct.getName();//化合物名称
            double massValue = compoundBasicPeak.containsKey(compoundName) ? Double.parseDouble(compoundBasicPeak.get(compoundName).getRemark()) : 0;
            if (Double.parseDouble(npsTestDetailDataStruct.getMass()) == massValue) {
                npsTestDetailDataStruct_Base = npsTestDetailDataStruct;
                log.info("找到化合物 {} 的基峰质荷比(mz)是 {}", compoundName, npsTestDetailDataStruct_Base);
                break;
            }
        }
        return npsTestDetailDataStruct_Base;
    }

    //查找标准品的保留时间
    private double findStdRtTime(String compoundName, List<NPSCaseTestDataDto> testRecordSampleDataList_std) {
        List<NPSCaseTestDataDto> retStd = testRecordSampleDataList_std.stream()
                .filter(item -> item.getCompoundName()
                        .equals(compoundName)).collect(Collectors.toList());

        if (retStd.size() > 0) {
            return retStd.get(0).getStdRtTime();
        } else {
            log.info("没有找到化合物是{}的标准溶液，请核查参数是否准确", compoundName);
            return -1;
        }
    }

    private double getStdCorrespondingAbundanceRatio(String massValue, String compoundName, List<NPSCaseTestDataDto> testRecordSampleDataList_std) {
        double retValue = 0;
        //根据化合物名称找出对应的标准品
        List<NPSCaseTestDataDto> std = testRecordSampleDataList_std.stream()
                .filter(item -> item.getCompoundName().equals(compoundName)).collect(Collectors.toList());
        NPSCaseTestDataDto std_NPSCaseTestDataDto = null;
        for (NPSCaseTestDataDto item : testRecordSampleDataList_std) {
            if (item.getCompoundName().equals(compoundName)) {
                std_NPSCaseTestDataDto = item;
                break;
            }
        }
        for (NPSTestDetailDataStruct npsTestDetailDataStruct : std_NPSCaseTestDataDto.getTestSampleDataList()) {
            if (Double.parseDouble(npsTestDetailDataStruct.getMass()) == Double.parseDouble(massValue)) {
                retValue = npsTestDetailDataStruct.getAbundanceRatio();
            }
        }
        return retValue;
    }

    //TestRecordSampleData 与NPSCaseTestDataDto的转换
    private NPSCaseTestDataDto testRecordSampleDataToPSCaseTestDataDto(TestRecordSampleData testRecordSampleData) {
        JSONObject jsonObject = (JSONObject) JSONObject.parse(testRecordSampleData.getDataResultJson());
        NPSCaseTestDataDto npsCaseTestDataDto = JSONObject.toJavaObject(jsonObject, NPSCaseTestDataDto.class);
        return npsCaseTestDataDto;
    }

    private int checkOtherFragmentResult(List<NPSTestDetailDataStruct> npsTestDetailDataStructList) {
        int ret = 1;

        for (NPSTestDetailDataStruct npsTestDetailDataStruct : npsTestDetailDataStructList) {
            if (npsTestDetailDataStruct.getWithinError().equals(TestRecordSampleDataConstant.NO)) {
                ret = 0;
            }
        }
        return ret;
    }

    /**
     * 封装根据类的类型去组装数据
     *
     * @param list
     * @param retList
     * @param clazz
     * @param <T>
     */
    private <T> void extractedSampleTestData(List<TestRecordSampleData> list, List<Object> retList, Class<T> clazz) {
        for (TestRecordSampleData testRecordSampleData : list) {
            retList.add(testRecordSampleDataToPSCaseTestDataDto(testRecordSampleData, clazz));
        }
    }

    /**
     * 获取TestRecordSampleData中的dataResultJson 转换成指定的对象
     *
     * @param testRecordSampleData
     * @param clazz
     * @param <T>
     * @return
     */
    private <T> T testRecordSampleDataToPSCaseTestDataDto(TestRecordSampleData testRecordSampleData, Class<T> clazz) {
        JSONObject jsonObject = (JSONObject) JSONObject.parse(testRecordSampleData.getDataResultJson());
        jsonObject.put("testSampleDataId", testRecordSampleData.getId());
        // 对小数进行保留两位小数的操作
        DecimalFormat df = new DecimalFormat("#.##");
        jsonObject.put("rtTimeError", df.format(jsonObject.getDouble("rtTimeError")));
        jsonObject.put("compoundCnName", testRecordSampleData.getCompoundCnName());
        return JSONObject.toJavaObject(jsonObject, clazz);
    }

    /**
     * 生成审核任务列表的头部名称json数组
     *
     * @param dataGroupBySampleNoMap
     * @return
     */
    private JSONArray generateHeaderArray(LinkedHashMap<String, List<TaskTestDataDTO>> dataGroupBySampleNoMap) {
        JSONArray headerArray = new JSONArray();
        AtomicBoolean finish = new AtomicBoolean(false);

        dataGroupBySampleNoMap.forEach((sampleNo, dataList) -> {
            if (!finish.get()) {
                headerArray.add(createHeaderObject("sampleNo", "溶液编号", true));
                headerArray.add(createHeaderObject("taskName", "案件/任务名称", false));
                headerArray.add(createHeaderObject("businessTypeName", "业务类型", false));
                headerArray.add(createHeaderObject("statusName", "状态", false));

                IntStream.range(0, dataList.size())
                        .forEach(i -> headerArray
                                .add(createHeaderObject(
                                        "compound" + (i + 1),
                                        "化合物" + "(" + dataList.get(i).getCompoundName() + ")",
                                        false)));
                finish.set(true);
            }
        });

        return headerArray;
    }

    /**
     * 创建列表头名称json
     *
     * @param prop
     * @param label
     * @param fixed
     * @return
     */
    private JSONObject createHeaderObject(String prop, String label, boolean fixed) {
        JSONObject object = new JSONObject();
        object.put(TestRecordSampleDataConstant.PROP, prop);
        object.put(TestRecordSampleDataConstant.LABEL, label);
        if (fixed) {
            object.put("fixed", true);
        }
        return object;
    }

    /**
     * 组装具体的检验数据
     *
     * @param dataGroupBySampleNoMap
     * @return
     */
    private JSONArray generateDataArray(LinkedHashMap<String, List<TaskTestDataDTO>> dataGroupBySampleNoMap) {
        JSONArray dataArray = new JSONArray();

        dataGroupBySampleNoMap.forEach((sampleNo, dataList) -> {
            JSONObject dataObject = new JSONObject();
            dataObject.put("sampleNo", sampleNo);

            dataList.forEach(taskTestDataDTO -> {
                dataObject.put("taskName", taskTestDataDTO.getTaskName());
                dataObject.put("businessTypeName", BusinessType.getBusinessTypeName(taskTestDataDTO.getBusinessType()));
                dataObject.put("status", taskTestDataDTO.getStatus());
                dataObject.put("statusName", TaskTestDataStatus.getStatusNameByCode(taskTestDataDTO.getStatus()));
                IntStream.range(0, dataList.size())
                        .forEach(i -> dataObject.put("compound" + (i + 1), dataList.get(i).getSampleConcentration()));
            });

            dataArray.add(dataObject);
        });

        return dataArray;
    }


    /**
     * 手动分页方法
     *
     * @param dataList
     * @param current
     * @param size
     * @return
     */
    private Page<Object> getPageData(JSONArray dataList, int current, int size) {
        int fromIndex = (current - 1) * size;
        if (fromIndex >= dataList.size()) {
            return new Page<>(); // 如果起始索引超出数据范围，返回空page
        }
        int toIndex = Math.min(fromIndex + size, dataList.size());
        Page<Object> page = new Page<>();
        page.setCurrent(current);
        page.setSize(size);
        page.setRecords(dataList.subList(fromIndex, toIndex));
        page.setTotal(dataList.size());
        return page;
    }

    /**
     * 生成查询条件
     *
     * @param pageDTO
     * @return
     */
    private QueryWrapper<TaskTestDataDTO> generateTaskTestDataQW(TaskTestDataPageDTO pageDTO) {
        QueryWrapper<TaskTestDataDTO> queryWrapper = new QueryWrapper<>();
        queryWrapper.orderByDesc("ti.create_time").orderByDesc("ss.sample_no");
        queryWrapper.and(StrUtil.isNotBlank(pageDTO.getKeyword()),
                wrapper ->
                        wrapper
                                .like("ti.task_name", pageDTO.getKeyword())
                                .or()
                                .like("si.sample_name", pageDTO.getKeyword())
                                .or()
                                .like("ss.sample_no", pageDTO.getKeyword()));
        queryWrapper.eq(StrUtil.isNotBlank(pageDTO.getBusinessType()), "ti.business_type", pageDTO.getBusinessType());
        if (pageDTO.getStatus().equals(TaskTestDataStatus.REVIEWED.getCode())) {
            // 任务审核列表，查询待审核的数据返回
            queryWrapper.eq("rs.status", TaskTestDataStatus.WAIT_REVIEW.getCode());
        } else if (pageDTO.getStatus().equals(TaskTestDataStatus.APPROVED.getCode())) {
            // 任务审批列表，查询待审批的数据返回
            queryWrapper.eq("rs.status", TaskTestDataStatus.REVIEWED.getCode());
        }
        return queryWrapper;
    }

    /**
     * 生成查询条件
     *
     * @param dto
     * @param businessIdList
     * @return
     */
    private QueryWrapper<TestResultBusinessVO> generateQueryWrapperByDTO(QueryTestResultPageQuery dto, Set<String> businessIdList) {
        QueryWrapper<TestResultBusinessVO> queryWrapper = new QueryWrapper<>();
        // 获取查询的关键字字段
        String keyword = dto.getKeywords();
        queryWrapper.in("T.id", businessIdList);
        queryWrapper.eq("T.type", dto.getType());
        queryWrapper.ge("T.status", 3);
        // 如果创建人不为空，则查询该创建人的任务
        queryWrapper.eq(StrUtil.isNotBlank(dto.getCreateBy()), "T.create_by", dto.getCreateBy())
                .and(StrUtil.isNotBlank(keyword), wrapper ->
                        wrapper.like("T.case_name", keyword));
        // 时间范围查询，包含开始时间和结束时间
        queryWrapper.ge(dto.getStartDate() != null, "T.create_time", dto.getStartDate())
                .le(dto.getEndDate() != null, "T.create_time", dto.getEndDate());
        // 根据创建时间进行排序
        if (dto.getType().equals(20000)) {
            queryWrapper.orderByDesc("T.task_start_date");
        } else {
            queryWrapper.orderByDesc("T.create_time");
        }
        return queryWrapper;
    }

    /**
     * 拼接定性结果，检出或未检出的字符串
     *
     * @param existingResult
     * @param newResult
     * @return
     */
    private String appendCheckOutResult(String existingResult, String newResult) {
        return StrUtil.isBlank(existingResult) ? newResult : existingResult + "、" + newResult;
    }

    /**
     * 自动创建测试记录样本数据
     *
     * @param testId       测试记录的ID
     * @param reagentId 化合物名称
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void autoCreateData(String testId, String reagentId) {
        TestRecord testRecord = testRecordService.getById(testId);
        if (testRecord == null) {
            throw new CheckedException("Test record not found for ID: " + testId);
        }

        TestRecordReagentVO reagentVO = testRecordReagentService.getVOById(reagentId);
        if (reagentVO == null) {
            throw new CheckedException("Reagent not found for ID: " + reagentId);
        }

        Drug drug = reagentVO.getDrug();
        String compoundName = drug.getEnglishName();
        String cnName = drug.getName();

        List<String> sampleIdList = testRecord.getSampleTestList();
        List<SampleInfo> sampleInfos = sampleInfoService.list(Wrappers.<SampleInfo>lambdaQuery()
                .in(SampleInfo::getId, sampleIdList));

        List<TestRecordSampleData> sampleDataList = new ArrayList<>();
        TestRecordSampleData stdSampleData = createSampleData(compoundName, TestRecordSampleDataConstant.SAMPLE_TYPE_STD, TestRecordSampleDataConstant.SAMPLE_TYPE_STD + "-" + compoundName, testId, cnName);
        sampleDataList.add(stdSampleData);

        sampleInfos.forEach(item -> {
            TestRecordSampleData sampleData = createSampleData(compoundName, TestRecordSampleDataConstant.SAMPLE_TYPE_ANALYTE, item.getAcceptNo(), testId, cnName);
            sampleDataList.add(sampleData);
        });
        // 判断标准物质中是否存在对应的 drug 信息，并且相应的定性离子对和质谱离子峰是否存在
        if (
                drug == null
                        ||
                        (
                                StrUtil.isBlank(drug.getCharacteristicIons())
                                        &&
                                        (
                                            StrUtil.isBlank(drug.getMainProductIon())
                                            ||
                                            StrUtil.isBlank(drug.getMinorProductIon())
                                        )
                        )
        ) {
            throw new CheckedException("标准物质中对应的毒品信息不完善！");
        }

        // 当对应的毒品信息不为空，添加扩展数据
        List<TestRecordSampleDataExpand> testRecordSampleDataExpandList = sampleDataList.stream().flatMap(sampleData -> {
            List<TestRecordSampleDataExpand> testRecordSampleDataExpand = testRecordSampledataExpandService.saveExpDataByBusinessType(sampleData.getId(), drug, testRecord.getBusinessType());
            return testRecordSampleDataExpand.stream();
        }).collect(Collectors.toList());
        testRecordSampledataExpandService.saveBatch(testRecordSampleDataExpandList);

        super.saveBatch(sampleDataList);
    }

    /**
     * 创建一个检验数据样本数据对象
     *
     * @param compoundName 化合物名称
     * @param sampleType   样本类型
     * @param sampleNo     样本编号
     * @param testId       实验id
     * @param cnName
     * @return 返回创建的测试记录样本数据对象
     */
    private TestRecordSampleData createSampleData(String compoundName, String sampleType, String sampleNo, String testId, String cnName) {
        TestRecordSampleData data = new TestRecordSampleData();
        data.setCompoundName(compoundName);
        data.setSampleNo(sampleNo);
        data.setSampleType(sampleType);
        data.setName(sampleNo);
        data.setStatus(TaskTestDataStatus.WAIT_REVIEW.getCode());
        data.setTestId(testId);
        data.setId(IdWorker.getIdStr());
        data.setCompoundCnName(cnName);
        return data;
    }


    /**
     * 自动删除指定测试记录和化合物名称对应的测试记录样本数据
     *
     * @param testId       测试记录的ID
     * @param reagentId 化合物名称
     * @return 如果删除成功返回 true，否则返回 false
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean autoDelData(String testId, String reagentId) {
        TestRecordReagentVO reagentVO = testRecordReagentService.getVOById(reagentId);
        Drug drug = reagentVO.getDrug();
        String compoundName = drug.getEnglishName();
        List<TestRecordSampleData> removeDataList = this.list(Wrappers.<TestRecordSampleData>lambdaQuery()
                .eq(TestRecordSampleData::getTestId, testId)
                .eq(TestRecordSampleData::getCompoundName, compoundName));
        List<String> removeIds = removeDataList.stream().map(TestRecordSampleData::getId).collect(Collectors.toList());
        if (CollUtil.isNotEmpty(removeIds)) {
            // 根据测试记录ID和化合物名称删除对应的测试记录样本数据
            testRecordSampledataExpandService.remove(Wrappers.<TestRecordSampleDataExpand>lambdaQuery().in(TestRecordSampleDataExpand::getTestDataId, removeIds));
        }
        return this.removeBatchByIds(removeIds);
    }

    /**
     * 更新计算数据，计算保留时间和保留时间误差
     *
     * @param dataIds      检验数据id列表
     * @param businessType 实验类型(业务类型)
     * @return
     */
    public Boolean calculateInspectData(List<String> dataIds, String businessType) {
        // 获取所有需要操作的列表
        List<TestRecordSampleData> testRecordSampleDataList = super.listByIds(dataIds);

        // 筛选出标准物质检验数据，并根据compoundName转map
        Map<String, TestRecordSampleData> stdTestDataMap = testRecordSampleDataList
                .stream()
                .filter(testRecordSampleData -> testRecordSampleData.getSampleType().equals(TestRecordSampleDataConstant.SAMPLE_TYPE_STD))
                .collect(Collectors.toMap(TestRecordSampleData::getCompoundName, Function.identity()));

        testRecordSampleDataList.forEach(testRecordSampleData -> {
            TestRecordSampleData std = stdTestDataMap.get(testRecordSampleData.getCompoundName());

            if ( std != null && testRecordSampleData.getTargetRtTime() != null && std.getTargetRtTime() != null ) {
                testRecordSampleData.setRtTimeError(processInspectDataService.getHairCaseRtTimeError(testRecordSampleData.getTargetRtTime(), std.getTargetRtTime()));
                if (businessType.equals(BusinessType.BOINT_CASE.getBusinessType())) {
                    testRecordSampleData.setRtTimeWithinError(
                            processInspectDataService.setHairCaseRtTimeWithinError(
                                    testRecordSampleData.getRtTimeError().doubleValue()
                            )
                    );
                } else {
                    testRecordSampleData.setRtTimeWithinError(
                            processInspectDataService.getRtWithinErrorText(
                                    Math.abs(testRecordSampleData.getRtTimeError().doubleValue()),
                                    1
                            )
                    );
                }
            }
        });

        return this.updateBatchById(testRecordSampleDataList);
    }

    @Override
    public Comparator getSortBySampleNo() {
        return Comparator.comparing(TestRecordSampleData::getSampleNo, (a, b) -> {
                    String[] partsA = a.split("-");
                    String[] partsB = b.split("-");
                    // 逐部分比较数值
                    for (int i = 0; i < 3; i++) {
                        int numA = Integer.parseInt(partsA[i]);
                        int numB = Integer.parseInt(partsB[i]);
                        if (numA != numB) return Integer.compare(numA, numB);
                    }
                    return 0; // 所有部分相同
                }
        );
    }
}
