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

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
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.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.config.Configure;
import com.deepoove.poi.plugin.table.LoopRowTableRenderPolicy;
import com.deepoove.poi.util.TableTools;
import com.deepoove.poi.xwpf.NiceXWPFDocument;
import digital.laboratory.platform.common.core.util.R;
import digital.laboratory.platform.common.mybatis.security.service.DLPUser;
import digital.laboratory.platform.common.oss.service.OssFile;
import digital.laboratory.platform.inspetion.api.enums.BusinessType;
import digital.laboratory.platform.inspection.enums.TestRecordArgumentType;
import digital.laboratory.platform.inspection.enums.TestRecordFileUrl;
import digital.laboratory.platform.inspection.constant.TestRecordSampleDataConstant;
import digital.laboratory.platform.inspection.dto.*;
import digital.laboratory.platform.inspection.entity.*;
import digital.laboratory.platform.inspection.mapper.TestRecordMapper;
import digital.laboratory.platform.inspection.mapper.TestRecordSampleDataExpandMapper;
import digital.laboratory.platform.inspection.mapper.TestRecordSampleDataMapper;
import digital.laboratory.platform.inspection.mapper.TestTemplateMapper;
import digital.laboratory.platform.inspection.service.*;
import digital.laboratory.platform.inspection.utils.PageUtils;
import digital.laboratory.platform.inspection.utils.datafile.nps.NPSTestDetailDataStruct;
import digital.laboratory.platform.inspection.vo.ProcedureVo;
import digital.laboratory.platform.inspection.vo.TestTemplateVo;
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.inspetion.api.vo.TestRecordVo;
import org.apache.commons.io.output.ByteArrayOutputStream;
import org.apache.poi.xwpf.usermodel.*;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.ByteArrayInputStream;
import java.text.DecimalFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
@SuppressWarnings("all")
public class TestRecordServiceImpl extends ServiceImpl<TestRecordMapper, TestRecord> implements TestRecordService {

    @Resource
    private SampleInfoService sampleInfoService;

    @Resource
    private TestRecordMethodService testRecordMethodService;

    @Resource
    private TestTemplateMapper testTemplateMapper;

    @Resource
    private TestRecordInstrumentConditionService testRecordInstrumentConditionService;

    @Resource
    private TestRecordReagentService testRecordReagentService;

    @Resource
    private TestRecordInstrumentService testRecordInstrumentService;

    @Resource
    private TestRecordStandardSolutionService testRecordStandardSolutionService;

    @Resource
    private TestRecordSampleSolutionService testRecordSampleSolutionService;

    @Resource
    private TestRecordSampleDataService testRecordSampleDataService;

    @Resource
    private OssFile ossFile;

    @Resource
    private EntrustInfoService entrustInfoService;

    @Resource
    private SampleInjectorService sampleInjectorService;

    @Resource
    private TestRecordSampleDataMapper testRecordSampleDataMapper;
    @Autowired
    private TestRecordSampleDataExpandMapper testRecordSampleDataExpandMapper;

    /**
     * 根据业务id获取实验检验记录
     *
     * @param businessId
     * @return
     */
    @Override
    public TestRecord getTestRecordByBusinessId(String businessId) {
        List<DataSolutionSampleDTO> dataSolutionSampleDTOS = testRecordSampleDataMapper
                .queryDataSolutionSampleDTOList(Wrappers.<DataSolutionSampleDTO>query()
                        .eq("si.business_id", businessId));
        if (dataSolutionSampleDTOS.size() == 0) {
            List<String> sampleIdList = sampleInfoService.lambdaQuery()
                    .eq(SampleInfo::getBusinessId, businessId)
                    .list()
                    .stream()
                    .map(SampleInfo::getId)
                    .collect(Collectors.toList());
            for (String sampleId : sampleIdList) {
                TestRecord record = this.lambdaQuery()
                        .like(TestRecord::getSampleTestList, sampleId)
                        .one();
                if (record != null) {
                    return record;
                }
            }

        }
        TestRecord testRecord = super.getById(dataSolutionSampleDTOS.get(0).getTestId());
        return testRecord;
    }

    /**
     * 创建一个实验
     *
     * @param testRecord
     * @param dlpUser    用户信息
     * @return
     * @throws Exception
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public TestRecord createTestInstance(TestRecordDto testRecord, DLPUser dlpUser) throws Exception {
        testRecord.setId(IdWorker.get32UUID().toUpperCase());
        List<SampleInfo> sampleInfos = new ArrayList<>();
        testRecord.setStatus(0);
        // 设置开始时间
        testRecord.setTestStartDate(LocalDate.now());
        //通过样本ID创建实验
        if (testRecord.getSampleTestList() != null && testRecord.getSampleTestList().size() > 0) {
            for (String sampleId : testRecord.getSampleTestList()) {
                SampleInfo sampleInfo = sampleInfoService.getById(sampleId);
                if (sampleInfo == null) {
                    throw new RuntimeException(String.format("没有找到ID为%s的样本!", sampleId));
                }
                sampleInfo.setStatus(2);
                sampleInfos.add(sampleInfo);
            }
            this.judgmentSampleType(sampleInfos);//判断是否有不同类型的检材
            testRecord.setId(IdWorker.get32UUID().toUpperCase());
            testRecord.setTestUserId(dlpUser.getId());
            testRecord.setTestSerialNumber(this.createTestNumber());
            testRecord.setBusinessType(sampleInfos.get(0).getBusinessType());

            sampleInfoService.updateBatchById(sampleInfos);//更新检材状态
            return this.save(testRecord) ? testRecord : null;

            //通过业务ID创建实验
        } else if (testRecord.getBusinessDtoList() != null && testRecord.getBusinessDtoList().size() > 0) {
            if (testRecord.getBusinessDtoList().size() > 1) {
                throw new RuntimeException("一次只能选择一个业务!");
            }
            List<BusinessDto> businessDtoList = testRecord.getBusinessDtoList();
            this.judgmentBusinessType(businessDtoList);//判断是否有不同类型的业务
            businessDtoList.forEach(item -> {
                List<SampleInfo> infos = sampleInfoService.getSampleInfoListForBusiness(item.getBusinessId(), dlpUser.getId());
                String businessType = item.getBusinessType();
                //如果是委托，需要更新一下委托的状态
                if (businessType.startsWith("1")) {
                    entrustInfoService.update(Wrappers.<EntrustInfo>lambdaUpdate()
                            .eq(EntrustInfo::getId, item.getBusinessId())
                            .set(EntrustInfo::getStatus, 1));
                }
                sampleInfos.addAll(infos);

            });
            List<String> sampleIdList = new ArrayList<>();
            if (sampleInfos.size() > 0) {
                for (SampleInfo sampleInfo : sampleInfos) {
                    sampleIdList.add(sampleInfo.getId());
                    sampleInfo.setStatus(2);
                }
                sampleInfoService.updateBatchById(sampleInfos);
            }
            testRecord.setBusinessId(testRecord.getBusinessDtoList().get(0).getBusinessId());
            testRecord.setTestUserId(dlpUser.getId());
            testRecord.setTestSerialNumber(this.createTestNumber());
            testRecord.setSampleTestList(sampleIdList);
            testRecord.setBusinessType(testRecord.getBusinessDtoList().get(0).getBusinessType());

            return this.save(testRecord) ? testRecord : null;
            //创建空实验
        } else {
            testRecord.setId(IdWorker.get32UUID().toUpperCase());
            testRecord.setTestUserId(dlpUser.getId());
            testRecord.setTestSerialNumber(this.createTestNumber());
            return this.save(testRecord) ? testRecord : null;
        }
    }

    /**
     * 判断是否有不同类型的业务
     *
     * @param businessDtoList ： 业务集合
     */
    public void judgmentBusinessType(List<BusinessDto> businessDtoList) {
        Map<String, List<BusinessDto>> map = businessDtoList.stream().collect(Collectors.groupingBy(item -> item.getBusinessType()));
        if (map.keySet().size() > 1) {
            throw new RuntimeException(String.format("实验创建失败,请选择相同类型的业务进行实验!"));
        }
        if (businessDtoList.get(0).getBusinessType().startsWith("1")) {
            businessDtoList.forEach(item -> {
                List<SampleInfo> sampleInfoList = sampleInfoService.list(Wrappers.<SampleInfo>lambdaQuery().eq(SampleInfo::getBusinessId, item.getBusinessId()));
                EntrustInfo entrustInfo = entrustInfoService.getById(item.getBusinessId());
                sampleInfoList.forEach(obj -> {
                    TestRecord record = this.getOne(Wrappers.<TestRecord>lambdaQuery().like(TestRecord::getSampleTestList, obj.getId()));
                    if (record != null) {
                        throw new RuntimeException(String.format("%s中的其余检材已进行了编号为" + record.getTestSerialNumber() + "的实验，无法继续选择该委托!" + record.getId(), entrustInfo.getCaseName()));
                    }
                });
            });
        }
    }

    /**
     * 判断是否有不同类型的检材
     *
     * @param sampleInfoList ： 检材集合
     */
    public void judgmentSampleType(List<SampleInfo> sampleInfoList) {
        Map<String, List<SampleInfo>> map = sampleInfoList.stream().collect(Collectors.groupingBy(item -> item.getBusinessType()));
        if (map.keySet().size() > 1) {
            throw new RuntimeException(String.format("实验创建失败,请选择相同类型的检材进行实验!"));
        }
        String businessType = "";
        if (sampleInfoList.get(0).getBusinessType().startsWith("1")) {
            Map<String, List<SampleInfo>> map1 = sampleInfoList.stream().collect(Collectors.groupingBy(item -> item.getBusinessId()));
            Set<String> keySet = map1.keySet();
            keySet.forEach(item -> {
                EntrustInfo entrustInfo = entrustInfoService.getById(item);
                List<SampleInfo> list = sampleInfoService.list(Wrappers.<SampleInfo>lambdaQuery().eq(SampleInfo::getBusinessId, item));
                List<SampleInfo> list1 = map1.get(item);
                if (list.size() != list1.size()) {
                    throw new RuntimeException(String.format("没有选择完%s下的所有检材!", entrustInfo.getCaseName()));
                }
            });
        }
    }

    /**
     * 使用模板
     *
     * @param testRecord ： 实验对象
     * @param templateId ： 模板Id
     */
    public void copyFromTemplate(TestRecord testRecord, String templateId) throws Exception {
        //将模板内容赋值给实验
        TestTemplateVo testTemplateVo = testTemplateMapper.getTestTemplateMapById(templateId);
        if (testTemplateVo.getUseCount() == null || testTemplateVo.getUseCount() == 0) {
            testTemplateVo.setUseCount(1);
        } else {
            testTemplateVo.setUseCount(testTemplateVo.getUseCount() + 1);
        }
        //复制方法
        if (testTemplateVo.getTestMethods() != null && testTemplateVo.getTestMethods().size() > 0) {
            testRecord.setTestMethodList(testTemplateVo.getTestMethods());
        }
        //复制仪器设备
        if (testTemplateVo.getDeviceIdList() != null && testTemplateVo.getDeviceIdList().size() > 0) {
            testRecord.setDeviceIdList(testTemplateVo.getDeviceIdList());
        }
        //复制仪器设备条件
        if (testTemplateVo.getDeviceUseCondition() != null && testTemplateVo.getDeviceUseCondition().size() > 0) {
            List<String> idList = testRecordInstrumentConditionService.copyCondition(testTemplateVo.getDeviceUseCondition(), testRecord.getId());
            testRecord.setDeviceUseCondition(idList);
        }
        //复制试剂耗材
        if (testTemplateVo.getReagentConsumables() != null && testTemplateVo.getReagentConsumables().size() > 0) {

            List<TestRecordReagent> reagents = testRecordReagentService.lambdaQuery()
                    .in(TestRecordReagent::getId, testTemplateVo.getReagentConsumables())
                    .eq(TestRecordReagent::getCategory, "标准物质")
                    .list();

            if (CollUtil.isNotEmpty(reagents)) {
                reagents.forEach(reagent -> {
                    testRecordSampleDataService.autoCreateData(testRecord.getId(), reagent.getId());
                });
            }
            testRecord.setReagentConsumablesList(testTemplateVo.getReagentConsumables());
        }
//        //复制仪器条件Word
//        ByteArrayOutputStream bos = new ByteArrayOutputStream();
//        ossFile.fileGet(TestRecordFileUrl.TEST_TEMPLATE_CATALOGUE.getFileUrl() + "/" + templateId + "/" + "仪器条件.docx", bos);
//        byte[] templateArray = bos.toByteArray();
//        ByteArrayInputStream bis = new ByteArrayInputStream(templateArray);
//        ossFile.fileSave(TestRecordFileUrl.TEST_RECORD_CATALOGUE.getFileUrl() + "/" + testRecord.getId() + "/" + "仪器条件.docx", bis);
        testRecord.setTemplateName(testTemplateVo.getName());
        testTemplateMapper.updateById(testTemplateVo);
        this.updateById(testRecord);
    }

    /**
     * 添加实验处理步骤
     *
     * @param testRecord
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public TestRecord updateTestInstance(TestRecord testRecord) {
        TestRecord record = this.getById(testRecord.getId());
        //样本溶液处理步骤
        if (StrUtil.isNotBlank(testRecord.getSampleSolutionProcessing())) {
            record.setSampleSolutionProcessing(testRecord.getSampleSolutionProcessing());
        } else {
            record.setSampleSolutionProcessing("");
        }
        //标准溶液处理步骤
        if (StrUtil.isNotBlank(testRecord.getStandardSolutionProcessing())) {
            record.setStandardSolutionProcessing(testRecord.getStandardSolutionProcessing());
        } else {
            record.setStandardSolutionProcessing("");
        }
        if (StringUtils.isNotBlank(testRecord.getTestProcessDes())) {
            record.setTestProcessDes(testRecord.getTestProcessDes());
        }
        return this.updateById(record) ? record : null;
    }

    /**
     * 通过实验ID查看实验
     *
     * @param id
     * @param dlpUser 用户信息
     * @return
     */
    @Override
    public TestRecordVo getTestRecord(String id, DLPUser dlpUser) {
        TestRecordVo testRecordVo = baseMapper.getTestRecordMapById(id);
        if (testRecordVo.getTestMethodList() != null && testRecordVo.getTestMethodList().size() > 0) {
            this.getTestMethodName(testRecordVo);
        }
        if (StringUtils.isNotBlank(testRecordVo.getBusinessType())) {
            testRecordVo.setBusinessTypeName(BusinessType.getBusinessTypeName(testRecordVo.getBusinessType()));
        }
        //需要判断这个实验是否由操作者创建的
        return testRecordVo.getTestUserId().equals(dlpUser.getId()) ? testRecordVo : null;
    }

    /**
     * 获取实验的方法名集合
     *
     * @param testRecordVo
     */
    public void getTestMethodName(TestRecordVo testRecordVo) {
        List<String> testMethodName = new ArrayList<String>();
        List<TestRecordMethod> list = testRecordMethodService.list(Wrappers.<TestRecordMethod>lambdaQuery().in(TestRecordMethod::getId, testRecordVo.getTestMethodList()));
        if (list.size() > 0) {
            for (TestRecordMethod testRecordMethod : list) {
                testMethodName.add(testRecordMethod.getMethodName());
            }
        }
        testRecordVo.setTestMethodName(testMethodName);
    }

    /**
     * 分页查询实验
     *
     * @param page
     * @param dlpUser      ：实验创建者
     * @param startTime    ：时间范围查询
     * @param endTime      ：时间范围查询
     * @param businessType
     */
    @Override
    public IPage<TestRecordVo> getTestRecordPageList(Page page, DLPUser dlpUser, LocalDateTime startTime, LocalDateTime endTime, String businessType) {
        IPage<TestRecordVo> testRecordMapPage = baseMapper.getTestRecordMapPage(page, Wrappers.<TestRecord>lambdaQuery()
                .eq(TestRecord::getTestUserId, dlpUser.getId())
                .ge(startTime != null, TestRecord::getCreateTime, startTime)
                .le(endTime != null, TestRecord::getCreateTime, endTime)
                .eq(StrUtil.isNotBlank(businessType), TestRecord::getBusinessType, businessType)
                .orderByDesc(TestRecord::getCreateTime));
        for (TestRecordVo testRecordVo : testRecordMapPage.getRecords()) {
            if (testRecordVo.getTestMethodList() != null && testRecordVo.getTestMethodList().size() > 0) {
                this.getTestMethodName(testRecordVo);
            }
            if (StringUtils.isNotBlank(testRecordVo.getBusinessType())) {
                testRecordVo.setBusinessTypeName(BusinessType.getBusinessTypeName(testRecordVo.getBusinessType()));
            }
        }
        return testRecordMapPage;
    }

    /**
     * 列表查询实验
     *
     * @param dlpUser 用户信息
     */
    @Override
    public List<TestRecordVo> getTestRecordList(DLPUser dlpUser) {
        List<TestRecordVo> testRecordMapList = baseMapper.getTestRecordMapList(Wrappers.<TestRecord>lambdaQuery()
                .eq(TestRecord::getTestUserId, dlpUser.getId())
                .orderByDesc(TestRecord::getCreateTime));
        for (TestRecordVo testRecordVo : testRecordMapList) {
            if (StringUtils.isNotBlank(testRecordVo.getBusinessType())) {
                testRecordVo.setBusinessTypeName(BusinessType.getBusinessTypeName(testRecordVo.getBusinessType()));
            }
            if (testRecordVo.getTestMethodList() != null && testRecordVo.getTestMethodList().size() > 0) {
                this.getTestMethodName(testRecordVo);
            }
        }
        return testRecordMapList;
    }

    /**
     * 编号生成规则
     *
     * @return
     */
    public String createTestNumber() {
        String number = "SXT";
        LocalDateTime localDateTime = LocalDateTime.now();
        number = number + LocalDateTimeUtil.format(localDateTime, "yyyyMMdd");
        List<TestRecord> list = this.list(Wrappers.<TestRecord>lambdaQuery().likeRight(TestRecord::getTestSerialNumber, number));
        if (list.size() == 0) {
            number = number + "001";
            return number;
        } else {
            //保留三位小数
            number = number + String.format("%03d", list.size() + 1);
            return number;
        }
    }

    /**
     * 为实验添加或移除内容
     *
     * @param testId               实验Id
     * @param testRecordArgumentId 参数Id
     * @param argument             参数类型
     * @param opCode               判断是添加还是删除   1 ：添加  -1 ：删除
     * @return
     */
    @Override//opCode:
    public boolean updateTestRecordArgument(String testId, String testRecordArgumentId, String argument, Integer opCode) {
        TestRecord testRecord = baseMapper.getTestRecordMapById(testId);
        List<String> argumentIdList = new ArrayList<>();
        if (testRecord == null) {
            throw new RuntimeException(String.format("没有找到ID为%s的实验", testId));
        }

        //添加或移除仪器设备
        if (argument.equals(TestRecordArgumentType.TEST_RECORD_ARGUMENT_INSTRUMENT.getType())) {
            if (testRecord.getDeviceIdList() != null && testRecord.getDeviceIdList().size() > 0) {
                argumentIdList = testRecord.getDeviceIdList();
            }
            this.updateArgument(argumentIdList, opCode, testRecordArgumentId);
            testRecord.setDeviceIdList(argumentIdList);

            //添加或移除仪器条件
        } else if (argument.equals(TestRecordArgumentType.TEST_RECORD_ARGUMENT_INSTRUMENT_CONDITION.getType())) {
            if (testRecord.getDeviceUseCondition() != null && testRecord.getDeviceUseCondition().size() > 0) {
                argumentIdList = testRecord.getDeviceUseCondition();
            }
            this.updateArgument(argumentIdList, opCode, testRecordArgumentId);
            testRecord.setDeviceUseCondition(argumentIdList);


            //添加或移除方法
        } else if (argument.equals(TestRecordArgumentType.TEST_RECORD_ARGUMENT_METHOD.getType())) {
            if (testRecord.getTestMethodList() != null && testRecord.getTestMethodList().size() > 0) {
                argumentIdList = testRecord.getTestMethodList();
            }
            this.updateArgument(argumentIdList, opCode, testRecordArgumentId);
            testRecord.setTestMethodList(argumentIdList);

            //添加或移除试剂耗材
        } else if (argument.equals(TestRecordArgumentType.TEST_RECORD_ARGUMENT_REAGENT.getType())) {
            if (testRecord.getReagentConsumablesList() != null && testRecord.getReagentConsumablesList().size() > 0) {
                argumentIdList = testRecord.getReagentConsumablesList();
            }
            this.updateArgument(argumentIdList, opCode, testRecordArgumentId);
            testRecord.setReagentConsumablesList(argumentIdList);

            //添加或移除样本溶液
        } else if (argument.equals(TestRecordArgumentType.TEST_RECORD_ARGUMENT_SAMPLE_SOLUTION.getType())) {
            if (testRecord.getSampleSolution() != null && testRecord.getSampleSolution().size() > 0) {
                argumentIdList = testRecord.getSampleSolution();
            }
            this.updateArgument(argumentIdList, opCode, testRecordArgumentId);
            testRecord.setSampleSolution(argumentIdList);

            //添加或移除标准溶液
        } else if (argument.equals(TestRecordArgumentType.TEST_RECORD_ARGUMENT_STANDARD_SOLUTION.getType())) {
            if (testRecord.getStandardSolution() != null && testRecord.getStandardSolution().size() > 0) {
                argumentIdList = testRecord.getStandardSolution();
            }
            this.updateArgument(argumentIdList, opCode, testRecordArgumentId);
            testRecord.setStandardSolution(argumentIdList);

            //添加或移除样本
        } else if (argument.equals(TestRecordArgumentType.TEST_RECORD_ARGUMENT_SAMPLE_DATA.getType())) {
            String businessType;
            SampleInfo sampleInfo = sampleInfoService.getById(testRecordArgumentId);
            businessType = sampleInfo.getBusinessType();
            //判断与实验的业务类型是否相同
            if (StringUtils.isNotBlank(testRecord.getBusinessType()) && !testRecord.getBusinessType().equals(businessType)) {
                throw new RuntimeException(String.format("添加失败，请添加%s类型的样本!", BusinessType.getBusinessTypeName(testRecord.getBusinessType())));
            }
            if (testRecord.getSampleTestList() != null && testRecord.getSampleTestList().size() > 0) {
                //如果该实验已存在样本了，添加样本时就要判断是否为同一类型
                businessType = sampleInfoService.getById(testRecord.getSampleTestList().get(0)).getBusinessType();
                if (!businessType.equals(sampleInfo.getBusinessType())) {
                    throw new RuntimeException(String.format("添加失败，请添加相同类型的样本!"));
                }
                //添加检材时，需判是否已经存在其他实验选择了该检材相关的委托下其余的检材
                if (businessType.startsWith("1") && opCode == 1) {
                    this.addOrRemoveSample(sampleInfo.getBusinessId(), testId);
                }
                argumentIdList = testRecord.getSampleTestList();
            } else {
                businessType = sampleInfo.getBusinessType();
                //添加检材时，需判是否已经存在其他实验选择了该检材相关的委托下其余的检材
                if (businessType.startsWith("1") && opCode == 1) {
                    this.addOrRemoveSample(sampleInfo.getBusinessId(), testId);
                }
                //如果是第一次添加检材，那么就给这个实验赋予这个检材的业务类型
                testRecord.setBusinessType(sampleInfo.getBusinessType());
            }
            this.updateArgument(argumentIdList, opCode, testRecordArgumentId);
            testRecord.setSampleTestList(argumentIdList);
        }
        return this.updateById(testRecord);
    }

    /**
     * 添加检材时，需判是否已经存在其他实验选择了该检材相关的委托下其余的检材
     *
     * @param businessId
     * @param testId
     */
    public void addOrRemoveSample(String businessId, String testId) {
        List<SampleInfo> sampleInfoList = sampleInfoService.list(Wrappers.<SampleInfo>lambdaQuery().eq(SampleInfo::getBusinessId, businessId));
        EntrustInfo entrustInfo = entrustInfoService.getById(businessId);
        sampleInfoList.forEach(item -> {
            TestRecord record = this.getOne(Wrappers.<TestRecord>lambdaQuery().like(TestRecord::getSampleTestList, item.getId()));
            if (record != null && (!record.getId().equals(testId))) {
                throw new RuntimeException(String.format("委托" + entrustInfo.getCaseName() + "中的其余检材已进行了编号为%s的实验，无法选择该检材!", record.getTestSerialNumber()));
            }
        });
    }

    /**
     * 判断有没有向实验重复添加或者添加了无效的参数
     *
     * @param argumentIdList       已添加的参数集合
     * @param opCode               1：添加 -1 移除
     * @param testRecordArgumentId 参数ID
     */
    public void updateArgument(List<String> argumentIdList, Integer opCode, String testRecordArgumentId) {
        if (opCode == 1) {
            if (argumentIdList.size() > 0 && argumentIdList.contains(testRecordArgumentId)) {
                throw new RuntimeException(String.format("当前内容已添加至实验，请勿重复添加!"));
            }
            argumentIdList.add(testRecordArgumentId);
        } else if (opCode == -1 && argumentIdList.contains(testRecordArgumentId)) {
            boolean ret = argumentIdList.remove(testRecordArgumentId);
            if (!ret) {
                throw new RuntimeException(String.format("在实验中没有找到ID为%s的内容!", testRecordArgumentId));
            }
        }
    }

    /**
     * 使用模板
     *
     * @param testId
     * @param templateId
     * @return
     * @throws Exception
     */
    @Override
    public TestRecord useTemplate(String testId, String templateId) throws Exception {
        TestRecord testRecord = this.getById(testId);
        List<TestRecordSampleData> dataList = testRecordSampleDataService.lambdaQuery()
                .eq(TestRecordSampleData::getTestId, testId)
                .list();

        if (CollUtil.isNotEmpty(dataList)) {
            testRecordSampleDataExpandMapper.delete(Wrappers.<TestRecordSampleDataExpand>lambdaQuery()
                    .in(TestRecordSampleDataExpand::getTestDataId, dataList.stream().map(TestRecordSampleData::getId).collect(Collectors.toList())));

            testRecordSampleDataService.removeBatchByIds(dataList);
        }
        this.copyFromTemplate(testRecord, templateId);
        return testRecord;
    }


    /**
     * 实验中不同流程的完成程度（只要在某个流程添加了数据，就传给前端true）
     *
     * @param testId
     * @return
     */
    @Override
    public ProcedureVo getProcedure(String testId) {
        TestRecordVo testRecordVo = baseMapper.getTestRecordMapById(testId);
        ProcedureVo procedureVo = new ProcedureVo();
        if (StringUtils.isNotBlank(testRecordInstrumentConditionService.generatedOrNot(testId))) {
            procedureVo.setSetInstrumentConditions(true);
        } else {
            procedureVo.setSetInstrumentConditions(false);
        }

        if (testRecordVo.getTestMethodList() != null && testRecordVo.getTestMethodList().size() > 0) {
            procedureVo.setTestRecordMethod(true);
        } else {
            procedureVo.setTestRecordMethod(false);
        }
        if (testRecordVo.getDeviceIdList() != null && testRecordVo.getDeviceIdList().size() > 0) {
            procedureVo.setTestRecordInstrument(true);
        } else {
            procedureVo.setTestRecordInstrument(false);
        }
        if (testRecordVo.getReagentConsumablesList() != null && testRecordVo.getReagentConsumablesList().size() > 0) {
            List<TestRecordReagent> testRecordReagentList = testRecordReagentService.list(Wrappers.<TestRecordReagent>lambdaQuery()
                    .in(TestRecordReagent::getCategory, "试剂", "耗材")
                    .in(TestRecordReagent::getId, testRecordVo.getReagentConsumablesList()));

            List<TestRecordReagent> testRecordStandardSubstanceList = testRecordReagentService.list(Wrappers.<TestRecordReagent>lambdaQuery()
                    .notIn(TestRecordReagent::getCategory, "试剂", "耗材")
                    .in(TestRecordReagent::getId, testRecordVo.getReagentConsumablesList()));
            if (testRecordReagentList.size() > 0) {
                procedureVo.setTestRecordReagentConsumables(true);
            } else {
                procedureVo.setTestRecordReagentConsumables(false);
            }
            if (testRecordStandardSubstanceList.size() > 0) {
                procedureVo.setTestRecordStandardSubstance(true);
            } else {
                procedureVo.setTestRecordStandardSubstance(false);
            }
        }
        procedureVo.setTestRecordReagentConsumables(true);
        if (testRecordVo.getStandardSolution() != null && testRecordVo.getStandardSolution().size() > 0 && testRecordVo.getSampleSolution() != null && testRecordVo.getSampleSolution().size() > 0) {
            procedureVo.setPretreatment(true);
        } else {
            procedureVo.setPretreatment(false);
        }
        if (testRecordVo.getSampleTestList() != null && testRecordVo.getSampleTestList().size() > 0) {
            procedureVo.setSampleInfo(true);
        } else {
            procedureVo.setSampleInfo(false);
        }
        procedureVo.setEmbarkation(true);
        procedureVo.setResultAnalysis(testRecordSampleDataService.whetherSave(testId));
        return procedureVo;
    }

    /**
     * 获取打印数据
     *
     * @param businessId
     * @return
     */
    @Override
    public Map getPrintData(String businessId) throws Exception {
        //这个委托下检材信息
        List<SampleInfo> sampleInfos = sampleInfoService.list(Wrappers.<SampleInfo>lambdaQuery().eq(SampleInfo::getBusinessId, businessId));
        ArrayList<String> sampleNoList = new ArrayList<>();
        ArrayList<String> sampleIdList = new ArrayList<>();
        sampleInfos.forEach(item -> {
            sampleNoList.add(item.getAcceptNo());
            sampleIdList.add(item.getId());
        });
        EntrustInfo entrustInfo = entrustInfoService.getById(businessId);
        String businessType = entrustInfo.getBusinessType();
        TestRecord vo = new TestRecord();

        //查询实验
        for (String sampleId : sampleIdList) {
            TestRecord testRecord = this.getOne(Wrappers.<TestRecord>lambdaQuery().like(TestRecord::getSampleTestList, sampleId));
            if (testRecord != null) {
                vo = testRecord;
                break;
            }
        }
        testRecordInstrumentConditionService.mergeFile(vo.getId());
        HashMap<String, Object> data = new HashMap<>();
        //构建检验记录的参数
        data.put("acceptNo", entrustInfo.getAcceptNo());
        if (vo.getTestMethodList() != null && vo.getTestMethodList().size() > 0) {
            List<TestRecordMethod> testRecordMethods = testRecordMethodService.list(Wrappers.<TestRecordMethod>lambdaQuery().in(TestRecordMethod::getId, vo.getTestMethodList()));
            if (BusinessType.NPS_CASE.getBusinessType().equals(businessType)) {
                String method = "";
                for (TestRecordMethod testRecordMethod : testRecordMethods) {
                    if (method.equals("")) {
                        method = "☑  " + testRecordMethod.getMethodName();
                    } else {
                        method = method + "\n" + "☑  " + testRecordMethod.getMethodName();
                    }
                }
                method = method + "\n" + "□  " + "其他";
                data.put("method", method);
            }
            data.put("testRecordMethods", testRecordMethods);
        }
        if (vo.getDeviceIdList() != null && vo.getDeviceIdList().size() > 0) {
            List<TestRecordInstrument> testRecordInstruments = testRecordInstrumentService.list(Wrappers.<TestRecordInstrument>lambdaQuery().in(TestRecordInstrument::getId, vo.getDeviceIdList()));
            data.put("testRecordInstruments", testRecordInstruments);
        }
        if (vo.getStandardSolution() != null && vo.getStandardSolution().size() > 0) {
            List<TestRecordStandardSolution> testRecordStandardSolutions = testRecordStandardSolutionService.list(Wrappers.<TestRecordStandardSolution>lambdaQuery().in(TestRecordStandardSolution::getId, vo.getStandardSolution()).eq(TestRecordStandardSolution::getSolutionType, "STD"));
            testRecordStandardSolutions.forEach(item -> {
                item.setExpirationDatePrint(item.getExpirationDate().getYear() + "-" + item.getExpirationDate().getMonthValue() + "-" + item.getExpirationDate().getDayOfMonth());
                item.setMadeDatePrint(item.getMadeDate().getYear() + "-" + item.getMadeDate().getMonthValue() + "-" + item.getMadeDate().getDayOfMonth());
            });
            data.put("testRecordStandardSolutions", testRecordStandardSolutions);
        }
        if (vo.getSampleSolution() != null && vo.getSampleSolution().size() > 0) {
            List<TestRecordSampleSolution> testRecordSampleSolutions = testRecordSampleSolutionService.list(Wrappers.<TestRecordSampleSolution>lambdaQuery().eq(TestRecordSampleSolution::getTestId, vo.getId()).in(TestRecordSampleSolution::getMaterialId, sampleIdList).orderByAsc(TestRecordSampleSolution::getSampleNo));
            Collections.sort(testRecordSampleSolutions, new Comparator<TestRecordSampleSolution>() {
                @Override
                public int compare(TestRecordSampleSolution o1, TestRecordSampleSolution o2) {
                    Integer o1No = Integer.parseInt(o1.getSampleNo().substring(o1.getSampleNo().lastIndexOf("-") + 1));
                    Integer o2No = Integer.parseInt(o2.getSampleNo().substring(o2.getSampleNo().lastIndexOf("-") + 1));
                    return Integer.compare(o1No, o2No);
                }
            });
            for (int i = 0; i < testRecordSampleSolutions.size(); i++) {
                testRecordSampleSolutions.get(i).setPrintSampleName(i + 1 + "号检材");
            }
            data.put("testRecordSampleSolutions", testRecordSampleSolutions);
        }
        //缴获物检验记录数据
        if (BusinessType.NPS_CASE.getBusinessType().equals(businessType)) {
            List<NPSCaseTestDataDto> npsCaseTestDataDtos = new ArrayList<>();
            List<NPSCaseTestDataDto> stdDataList = new ArrayList<>();
            List<NPSCaseTestDataDto> npsCaseTestDataDtoList = (List<NPSCaseTestDataDto>) testRecordSampleDataService.getSampleTestDataByBusiness(entrustInfo.getId());
            //判断是标准溶液数据还是样本的数据
            for (NPSCaseTestDataDto npsCaseTestDataDto : npsCaseTestDataDtoList) {
                if (TestRecordSampleDataConstant.SAMPLE_TYPE_STD.equals(npsCaseTestDataDto.getSampleType())) {
                    stdDataList.add(npsCaseTestDataDto);
                    data.put("stdDataList", stdDataList);
                } else {
                    npsCaseTestDataDtos.add(npsCaseTestDataDto);
                    data.put("npsCaseTestDataDtos", npsCaseTestDataDtos);
                }
            }
            //生物样本检验记录数据
        } else {
            //鉴定过程
            List<AppraisalProcessDto> appraisalProcessDtoList = new ArrayList<>();
            if (StringUtils.isNotBlank(vo.getStandardSolutionProcessing())) {
                appraisalProcessDtoList.add(new AppraisalProcessDto(vo.getStandardSolutionProcessing()));

            }
            if (StringUtils.isNotBlank(vo.getSampleSolutionProcessing())) {
                appraisalProcessDtoList.add(new AppraisalProcessDto(vo.getSampleSolutionProcessing()));
            }

            if (StringUtils.isNotBlank(vo.getTestProcessDes())) {
                appraisalProcessDtoList.add(new AppraisalProcessDto(vo.getTestProcessDes()));
            }
            data.put("appraisalProcessDtoList", appraisalProcessDtoList);

            List<HairSewageDataDto> hairSewageDataDtoList = new ArrayList<>();
            List<HairSewageDataDto> hairSewageDataDtos = new ArrayList<>();
            List<HairSewageDataDto> list = (List<HairSewageDataDto>) testRecordSampleDataService.getSampleTestDataByBusiness(entrustInfo.getId());
            Map<String, List<HairSewageDataDto>> map = list.stream().collect(Collectors.groupingBy(item -> item.getSampleType()));
            List<TestRecordStandardSolution> bls = testRecordStandardSolutionService.list(Wrappers.<TestRecordStandardSolution>lambdaQuery().eq(TestRecordStandardSolution::getTestId, vo.getId()).eq(TestRecordStandardSolution::getSolutionType, "BLS"));
            List<TestRecordStandardSolution> blk = testRecordStandardSolutionService.list(Wrappers.<TestRecordStandardSolution>lambdaQuery().eq(TestRecordStandardSolution::getTestId, vo.getId()).eq(TestRecordStandardSolution::getSolutionType, "BLK"));
            Set<String> keySet = map.keySet();

            keySet.forEach(key -> {
                if (key.equals("STD")) {
                    hairSewageDataDtoList.addAll(map.get(key));
                    data.put("STDIndex", map.get(key).size());
                }
            });
            keySet.forEach(key -> {
                if (key.equals("QC")) {
                    hairSewageDataDtoList.addAll(map.get(key));
                    data.put("QCIndex", map.get(key).size());
                }
            });
            //根据选取的溶液创建数据
            if (blk != null && blk.size() > 0) {
                //创建空白溶剂
                for (TestRecordStandardSolution testRecordStandardSolution : blk) {
                    HairSewageDataDto hairSewageDataDto = new HairSewageDataDto();
                    hairSewageDataDto.setSampleNo(testRecordStandardSolution.getNumber());
                    hairSewageDataDto.setSampleType("空白溶剂");
                    hairSewageDataDto.setTmpPeakAreaUp("/");
                    hairSewageDataDto.setTmpIonAbundanceRatio("/");
                    hairSewageDataDto.setTmpTargetRtTime("/");
                    hairSewageDataDto.setTmpRtTimeError("/");
                    hairSewageDataDto.setCompoundName("/");
                    hairSewageDataDto.setTmpPeakAreaDown("/");
                    hairSewageDataDto.setTmpIonAbundanceRatioWithinError("/");
                    hairSewageDataDto.setRtTimeWithinError("/");
                    hairSewageDataDto.setQualitativeIonPairUp("/");
                    hairSewageDataDto.setQualitativeIonPairDown("/");
                    hairSewageDataDto.setWhetherCheckOut("/");
                    hairSewageDataDtoList.add(hairSewageDataDto);
                    data.put("BLKIndex", blk.size());
                }
            }
            if (bls != null && blk.size() > 0) {
                //创建空白样品
                for (TestRecordStandardSolution testRecordStandardSolution : bls) {
                    HairSewageDataDto hairSewageDataDto = new HairSewageDataDto();
                    hairSewageDataDto.setSampleNo(testRecordStandardSolution.getNumber());
                    hairSewageDataDto.setSampleType("空白样品");
                    hairSewageDataDto.setTmpPeakAreaUp("/");
                    hairSewageDataDto.setTmpIonAbundanceRatio("/");
                    hairSewageDataDto.setTmpTargetRtTime("/");
                    hairSewageDataDto.setTmpRtTimeError("/");
                    hairSewageDataDto.setTmpPeakAreaDown("/");
                    hairSewageDataDto.setCompoundName("/");
                    hairSewageDataDto.setTmpIonAbundanceRatioWithinError("/");
                    hairSewageDataDto.setRtTimeWithinError("/");
                    hairSewageDataDto.setQualitativeIonPairUp("/");
                    hairSewageDataDto.setQualitativeIonPairDown("/");
                    hairSewageDataDto.setWhetherCheckOut("/");
                    hairSewageDataDtoList.add(hairSewageDataDto);
                    data.put("BLSIndex", bls.size());
                }
            }
            keySet.forEach(key -> {
                if (key.equals("Analyte")) {
                    ArrayList<HairSewageDataDto> hairSewageDataDtos2 = new ArrayList<>();
                    List<HairSewageDataDto> hairSewageDataDtos1 = map.get(key);
                    //按照化合物进行分组
                    Map<String, List<HairSewageDataDto>> map2 = hairSewageDataDtos1.stream().collect(Collectors.groupingBy(item -> item.getCompoundName()));
                    Set<String> keySet2 = map2.keySet();
                    for (String compoundName : keySet2) {
                        List<HairSewageDataDto> hairSewageDataDtos4 = map2.get(compoundName);
                        //分组来组装数据，因为生物样本的数据会有两条溶液编号一样的结果数据，我们需要对数据进行加工，在溶液编号后面加上-1和-2
                        Map<String, List<HairSewageDataDto>> map1 = hairSewageDataDtos4.stream().collect(Collectors.groupingBy(item -> item.getSampleNo()));
                        Set<String> keySet1 = map1.keySet();
                        List<String> list1 = new ArrayList<>(keySet1);
                        Integer x = 0;
                        //对溶液数据进行排序
                        Collections.sort(list1, new Comparator<String>() {
                            @Override
                            public int compare(String o1, String o2) {
                                int num1 = Integer.parseInt(o1.substring(o1.lastIndexOf("-") + 1));
                                int num2 = Integer.parseInt(o2.substring(o2.lastIndexOf("-") + 1));
                                return Integer.compare(num1, num2);
                            }
                        });
                        for (String key1 : list1) {
                            List<HairSewageDataDto> hairSewageDataDtos3 = map1.get(key1);
                            Collections.sort(hairSewageDataDtos3, new Comparator<HairSewageDataDto>() {
                                @Override
                                public int compare(HairSewageDataDto o1, HairSewageDataDto o2) {
                                    return o1.getSampleName().compareTo(o2.getSampleName());
                                }
                            });
                            if (hairSewageDataDtos3.size() != 1) {
                                for (int i = 0; i < hairSewageDataDtos3.size(); i++) {
                                    HairSewageDataDto hairSewageDataDto = hairSewageDataDtos3.get(i);
                                    hairSewageDataDto.setSampleNo(hairSewageDataDto.getSampleNo() + "-" + (i + 1));
                                    hairSewageDataDtos2.add(hairSewageDataDto);
                                }
                            } else {
                                hairSewageDataDtos2.add(hairSewageDataDtos3.get(0));
                            }
//                        if (dto1.getWhetherCheckOut().equals("是") && dot2.getWhetherCheckOut().equals("是")) {
//                        }
                        }
                    }
                    hairSewageDataDtoList.addAll(hairSewageDataDtos2);
                }
            });
            //处理数据类型格式转化问题
            hairSewageDataDtoList.forEach(item -> {
                switch (item.getSampleType()) {
                    case "STD":
                        item.setSampleType("标准溶液");
                        break;
                    case "QC":
                        item.setSampleType("质控样品");
                        break;
                    case "Analyte":
                        item.setSampleType(item.getSampleNo());
                        break;
                }
                if (!item.getSampleType().startsWith("空")) {
                    if (TestRecordSampleDataConstant.isInvalidValue(item.getTargetRtTime()) || item.getTargetRtTime() == 0.00) {
                        item.setTmpTargetRtTime("/");
                    } else {
                        item.setTmpTargetRtTime(String.format("%.02f", item.getTargetRtTime()));
                    }
                    if (TestRecordSampleDataConstant.isInvalidValue(item.getRtTimeError()) || item.getRtTimeError() == -100 || item.getRtTimeError() == 0.00) {
                        item.setTmpRtTimeError("/");
                    } else {
                        item.setTmpRtTimeError(String.format("%.02f", item.getRtTimeError()));
                    }
                    if (TestRecordSampleDataConstant.isInvalidValue(item.getIonAbundanceRatioWithinError()) || item.getIonAbundanceRatioWithinError() == -100) {
                        item.setTmpIonAbundanceRatioWithinError("/");
                    } else {
                        item.setTmpIonAbundanceRatioWithinError(String.format("%.02f", item.getIonAbundanceRatioWithinError()));
                    }
                    if (item.getPeakAreaUp() == 0.00) {
                        item.setTmpPeakAreaUp("/");
                    } else {
                        item.setTmpPeakAreaUp(String.format("%.02f", item.getPeakAreaUp()));
                    }

                    if (item.getPeakAreaDown() == 0.00) {
                        item.setTmpPeakAreaDown("/");
                    } else {
                        item.setTmpPeakAreaDown(String.format("%.02f", item.getPeakAreaDown()));
                    }
                    if (item.getIonAbundanceRatio() == 0.00) {
                        item.setTmpIonAbundanceRatio("/");
                    } else {
                        item.setTmpIonAbundanceRatio(String.format("%.02f", item.getIonAbundanceRatio()));
                    }
                }

                HairSewageDataDto hairSewageDataDto = new HairSewageDataDto();
                BeanUtils.copyProperties(item, hairSewageDataDto);
                if (item.getQualitativeIonPairDown() != null) {
                    hairSewageDataDto.setQualitativeIonPairUp(item.getQualitativeIonPairDown());
                    hairSewageDataDto.setQualitativeIonPairUp(item.getQualitativeIonPairDown());
                }
                if (item.getPeakAreaDown() != 0.0) {
                    hairSewageDataDto.setTmpPeakAreaUp(String.format("%.02f", item.getPeakAreaDown()));
                }
                hairSewageDataDtos.add(item);
                hairSewageDataDtos.add(hairSewageDataDto);
            });
            data.put("hairSewageDataDtos", hairSewageDataDtos);
            String results = testRecordSampleDataService.generateQualitativeResults(businessId);
            data.put("results", results);
        }
        data.put("createTime", LocalDateTimeUtil.format(entrustInfo.getAcceptDate(), "yyyy年MM月dd日"));
        data.put("vo", vo);
        return data;
    }

    /**
     * 创建生物样本检验记录
     *
     * @param businessId
     * @return
     * @throws Exception
     */
    @Override
    public boolean createInspectionRecordByBiological(String businessId) throws Exception {
        Map data = this.getPrintData(businessId);
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        TestRecord vo = (TestRecord) data.get("vo");
        ossFile.fileGet(TestRecordFileUrl.TEST_RECORD_CATALOGUE.getFileUrl() + "/" + vo.getId() + "/" + "生物样本检验记录.docx", bos);
        byte[] templateArray = bos.toByteArray();
        ByteArrayInputStream bis = new ByteArrayInputStream(templateArray);
        bos.close();

        LoopRowTableRenderPolicy policy = new LoopRowTableRenderPolicy();
        Configure config = Configure.builder().
                bind("testRecordMethods", policy)
                .bind("testRecordInstruments", policy)
                .bind("testRecordStandardSolutions", policy)
                .bind("testRecordSampleSolutions", policy)
                .bind("hairSewageDataDtos", policy)
                .bind("appraisalProcessDtoList", policy)
                .build();
        XWPFTemplate template = XWPFTemplate.compile(bis, config).render(
                new HashMap<String, Object>() {{
                    put("testRecordMethods", data.get("testRecordMethods"));
                    put("testRecordInstruments", data.get("testRecordInstruments"));
                    put("testRecordStandardSolutions", data.get("testRecordStandardSolutions"));
                    put("testRecordSampleSolutions", data.get("testRecordSampleSolutions"));
                    put("createTime", data.get("createTime"));
                    put("acceptNo", data.get("acceptNo"));
                    put("hairSewageDataDtos", data.get("hairSewageDataDtos"));
                    put("results", data.get("results"));
                    put("appraisalProcessDtoList", data.get("appraisalProcessDtoList"));
                }}
        );
        NiceXWPFDocument document = template.getXWPFDocument();
        XWPFTable table = document.getTables().get(3);
        List<HairSewageDataDto> hairSewageDataDtos = (List<HairSewageDataDto>) data.get("hairSewageDataDtos");
        int size = hairSewageDataDtos.size() / 2;
        int mergeIndex = 2;
        //合并单元格
        for (int i = 0; i < size; i++) {
            TableTools.mergeCellsVertically(table, 0, mergeIndex, mergeIndex + 1);
            TableTools.mergeCellsVertically(table, 1, mergeIndex, mergeIndex + 1);
            TableTools.mergeCellsVertically(table, 2, mergeIndex, mergeIndex + 1);
            TableTools.mergeCellsVertically(table, 3, mergeIndex, mergeIndex + 1);
            TableTools.mergeCellsVertically(table, 4, mergeIndex, mergeIndex + 1);
            TableTools.mergeCellsVertically(table, 7, mergeIndex, mergeIndex + 1);
            TableTools.mergeCellsVertically(table, 8, mergeIndex, mergeIndex + 1);
            TableTools.mergeCellsVertically(table, 9, mergeIndex, mergeIndex + 1);
            mergeIndex += 2;
        }
        int stdIndex = (int) data.get("STDIndex") * 2;
        int qcIndex = (int) data.get("QCIndex") * 2;
        System.out.println(stdIndex);
        //根据标准溶液和质控溶液的数量来确定合并多少行
        TableTools.mergeCellsVertically(table, 0, 2, stdIndex + 1);
        TableTools.mergeCellsVertically(table, 0, stdIndex + 2, stdIndex + 2 + qcIndex - 1);
        List<XWPFParagraph> paragraphs = document.getParagraphs();
        for (int i = 0; i < paragraphs.size(); i++) {
            XWPFParagraph paragraph = paragraphs.get(i);

            // 检查段落内容是否为空
            if (paragraph.getParagraphText().trim().isEmpty()) {
                // 删除空白段落
                document.removeBodyElement(document.getPosOfParagraph(paragraph));
                i--; // 更新索引以考虑删除的段落
            }
        }

        bis.close();
        ByteArrayOutputStream fosWord = new ByteArrayOutputStream();
        template.write(fosWord);
        template.close();
        ByteArrayInputStream fisWord = new ByteArrayInputStream(fosWord.toByteArray());
        fosWord.close();
        document.close();
        return ossFile.fileSave(TestRecordFileUrl.TEST_RECORD_CATALOGUE.getFileUrl() + "/" + businessId + "/" + "生物样本检验记录.docx", fisWord);
    }

    /**
     * 创建缴获物检验记录
     *
     * @param businessId
     * @return
     * @throws Exception
     */
    public boolean createInspectionRecordBySeizure(String businessId) throws Exception {
        Map data = this.getPrintData(businessId);
        String fileName = "缴获物检验记录.docx";
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        TestRecord vo = (TestRecord) data.get("vo");
        ossFile.fileGet(TestRecordFileUrl.TEST_RECORD_CATALOGUE.getFileUrl() + "/" + vo.getId() + "/" + fileName, bos);
        byte[] templateArray = bos.toByteArray();
        ByteArrayInputStream bis = new ByteArrayInputStream(templateArray);
        bos.close();

        List<NPSCaseTestDataDto> npsCaseTestDataDtos = (List<NPSCaseTestDataDto>) data.get("npsCaseTestDataDtos");
        if (npsCaseTestDataDtos != null && npsCaseTestDataDtos.size() != 0) {
            List<NPSCaseTestSampleData> sampleDataList = new ArrayList<>();
            Collections.sort(npsCaseTestDataDtos, new Comparator<NPSCaseTestDataDto>() {
                @Override
                public int compare(NPSCaseTestDataDto o1, NPSCaseTestDataDto o2) {
                    return o1.getSampleNo().compareTo(o2.getSampleNo());
                }
            });
            for (NPSCaseTestDataDto npsCaseTestDataDto : npsCaseTestDataDtos) {
                List<NPSTestDetailDataStruct> testSampleDataList = npsCaseTestDataDto.getTestSampleDataList();
                for (NPSTestDetailDataStruct npsTestDetailDataStruct : testSampleDataList) {
                    NPSCaseTestSampleData sampleData = new NPSCaseTestSampleData();
                    this.disposalData(npsCaseTestDataDto, npsTestDetailDataStruct, sampleData);
                    sampleDataList.add(sampleData);
                }
            }
            data.put("sampleDataList", sampleDataList);
        }

        LoopRowTableRenderPolicy policy = new LoopRowTableRenderPolicy();
        Configure config = Configure.builder()
                .bind("testRecordMethods", policy)
                .bind("testRecordInstruments", policy)
                .bind("testRecordStandardSolutions", policy)
                .bind("testRecordSampleSolutions", policy)
                .bind("npsCaseTestDataDtoList", policy)
                .bind("sampleDataList", policy)
                .build();
        XWPFTemplate template = XWPFTemplate.compile(bis, config).render(
                new HashMap<String, Object>() {{
                    put("testRecordMethods", data.get("testRecordMethods"));
                    put("testRecordInstruments", data.get("testRecordInstruments"));
                    put("testRecordStandardSolutions", data.get("testRecordStandardSolutions"));
                    put("testRecordSampleSolutions", data.get("testRecordSampleSolutions"));
                    put("createTime", data.get("createTime"));
                    put("acceptNo", data.get("acceptNo"));
                    put("method", data.get("method"));
                    put("sampleDataList", data.get("sampleDataList"));
                }}
        );
        bis.close();
        ByteArrayOutputStream fosWord = new ByteArrayOutputStream();
        NiceXWPFDocument xwpfDocument = template.getXWPFDocument();
        List<NPSCaseTestDataDto> stdDataList = (List<NPSCaseTestDataDto>) data.get("stdDataList");
        if (stdDataList != null && stdDataList.size() != 0) {
            //创建标准溶液的数据表格
            XWPFTable stdTable = xwpfDocument.createTable(stdDataList.size() * 6, 4);
            int index = 0;
            for (int i = 0; i < stdDataList.size(); i++) {
                NPSCaseTestDataDto npsCaseTestDataDto = stdDataList.get(i);
                XWPFTableRow row1 = stdTable.getRow(index);
                row1.getCell(0).setText(npsCaseTestDataDto.getCompoundName());
                row1.getCell(1).setText(npsCaseTestDataDto.getStdConcentration());
                row1.getCell(2).setText("保留时间");
                row1.getCell(3).setText(String.valueOf(npsCaseTestDataDto.getStdRtTime()));
                XWPFTableRow row2 = stdTable.getRow(index + 1);
                row2.getCell(0).setText("m/z");
                row2.getCell(1).setText("S/N");
                row2.getCell(2).setText("峰面积");
                row2.getCell(3).setText("丰度比");
                List<NPSTestDetailDataStruct> testSampdleDataList = npsCaseTestDataDto.getTestSampleDataList();
                for (int j = 0; j < testSampdleDataList.size(); j++) {
                    NPSTestDetailDataStruct struct = testSampdleDataList.get(j);
                    XWPFTableRow row3 = stdTable.getRow(index + 2 + j);
                    //判断是不是基峰
                    if (struct.getIsBasePeak() == 1) {
                        row3.getCell(0).setText(String.valueOf((int) Double.parseDouble(struct.getMass())) + "*");
                    } else {
                        row3.getCell(0).setText(String.valueOf((int) Double.parseDouble(struct.getMass())));
                    }
                    row3.getCell(1).setText(String.format("%.02f", struct.getSn()));
                    row3.getCell(2).setText(String.valueOf((int) struct.getArea()));
                    if (Double.compare(struct.getAbundanceRatio(), TestRecordSampleDataConstant.INVALID_VALUE) == 0) {
                        row3.getCell(3).setText("/");
                    } else {
                        row3.getCell(3).setText(String.format("%.02f", struct.getAbundanceRatio()) + "%");
                    }
                }
                index = index + 6;
            }
            //设置文本居中
            for (XWPFTableRow row : stdTable.getRows()) {
                for (XWPFTableCell cell : row.getTableCells()) {
                    for (XWPFParagraph paragraph : cell.getParagraphs()) {
                        paragraph.setAlignment(ParagraphAlignment.CENTER);
                    }
                }
            }
            List<XWPFTableRow> rows = stdTable.getRows();
//            设置宽度
            for (XWPFTableRow row : rows) {
                for (int i = 0; i < 4; i++) {
                    row.getCell(i).setWidth("4200");
                }
            }
            xwpfDocument.setTable(6, stdTable);
            //因为上面的方法会默认在word最后添加这个表格，所以我们要删除掉这个表格
            List<XWPFTable> tables = xwpfDocument.getTables();
            XWPFTable table = tables.get(tables.size() - 1);
            xwpfDocument.removeBodyElement(xwpfDocument.getBodyElements().indexOf(table));
        }

        XWPFTable sampleDataTable = xwpfDocument.getTables().get(7);
        XWPFTable resultTable = xwpfDocument.getTables().get(8);

        //设置需要合并的单元格
        int mergeIndex = 1;
        for (int i = 0; i < npsCaseTestDataDtos.size(); i++) {
            TableTools.mergeCellsVertically(sampleDataTable, 0, mergeIndex, mergeIndex + 3);
            TableTools.mergeCellsVertically(sampleDataTable, 1, mergeIndex, mergeIndex + 3);
            TableTools.mergeCellsVertically(sampleDataTable, 2, mergeIndex, mergeIndex + 3);
            TableTools.mergeCellsVertically(resultTable, 0, mergeIndex, mergeIndex + 3);
            TableTools.mergeCellsVertically(resultTable, 1, mergeIndex, mergeIndex + 3);
            TableTools.mergeCellsVertically(resultTable, 2, mergeIndex, mergeIndex + 3);
            TableTools.mergeCellsVertically(resultTable, 3, mergeIndex, mergeIndex + 3);
            TableTools.mergeCellsVertically(resultTable, 4, mergeIndex, mergeIndex + 3);
            TableTools.mergeCellsVertically(resultTable, 5, mergeIndex, mergeIndex + 3);
            TableTools.mergeCellsVertically(resultTable, 12, mergeIndex, mergeIndex + 3);
            mergeIndex = mergeIndex + 4;
        }
        template.write(fosWord);
        template.close();
        ByteArrayInputStream fisWord = new ByteArrayInputStream(fosWord.toByteArray());
        fosWord.close();
        return ossFile.fileSave(TestRecordFileUrl.TEST_RECORD_CATALOGUE.getFileUrl() + "/" + businessId + "/" + fileName, fisWord);
    }

    /**
     * 处理打印数据
     *
     * @param caseTest   仪器导出数据
     * @param dataStruct 仪器导出数据
     * @param sampleData 样本数据
     */
    public void disposalData(NPSCaseTestDataDto caseTest, NPSTestDetailDataStruct dataStruct, NPSCaseTestSampleData sampleData) {
        DecimalFormat format = new DecimalFormat("#.00");
        sampleData.setStdConcentration(caseTest.getStdConcentration());
        sampleData.setRtTimeWithinError(caseTest.getRtTimeWithinError());
        sampleData.setSampleNo(caseTest.getSampleNo());
        sampleData.setTargetConcentration(caseTest.getTargetConcentration());

        if (Double.compare(caseTest.getStdRtTime(), TestRecordSampleDataConstant.INVALID_VALUE) == 0) {
            sampleData.setStdRtTime("/");
        } else {
            sampleData.setStdRtTime(String.format("%.02f", caseTest.getStdRtTime()) + "%");
        }

        if (Double.compare(caseTest.getRtTimeError(), TestRecordSampleDataConstant.INVALID_VALUE) == 0) {
            sampleData.setRtTimeError("/");
        } else {
            sampleData.setRtTimeError(String.format("%.02f", caseTest.getRtTimeError()) + "%");
        }

        if (Double.compare(caseTest.getTargetRtTime(), TestRecordSampleDataConstant.INVALID_VALUE) == 0) {
            sampleData.setTargetRtTime("/");
        } else {
            sampleData.setTargetRtTime(String.format("%.02f", caseTest.getTargetRtTime()) + "%");
        }

        //判断是不是基峰
        if (dataStruct.getIsBasePeak() == 1) {
            sampleData.setMass(String.valueOf((int) Double.parseDouble(dataStruct.getMass())) + "*");
        } else {
            sampleData.setMass(String.valueOf((int) Double.parseDouble(dataStruct.getMass())));
        }

        if (Double.compare(dataStruct.getAbundanceRatio(), TestRecordSampleDataConstant.INVALID_VALUE) == 0) {
            sampleData.setAbundanceRatio("/");
        } else {
            sampleData.setAbundanceRatio(String.format("%.02f", dataStruct.getAbundanceRatio()) + "%");
        }

        if (Double.compare(dataStruct.getAbundanceRatio_std(), TestRecordSampleDataConstant.INVALID_VALUE) == 0) {
            sampleData.setAbundanceRatio_std("/");
        } else {
            sampleData.setAbundanceRatio_std(String.format("%.02f", dataStruct.getAbundanceRatio_std()) + "%");
        }

        if (Double.compare(dataStruct.getAbundanceRatioError(), TestRecordSampleDataConstant.INVALID_VALUE) == 0) {
            sampleData.setAbundanceRatioError("/");
        } else {
            sampleData.setAbundanceRatioError(String.format("%.02f", dataStruct.getAbundanceRatioError()) + "%");
        }

        if (Double.compare(dataStruct.getErrorRange(), TestRecordSampleDataConstant.INVALID_VALUE) == 0) {
            sampleData.setErrorRange("/");
        } else {
            sampleData.setErrorRange("±" + (int) dataStruct.getErrorRange() + "%");
        }

        if (caseTest.getIsDetected() == 0) {
            sampleData.setCompoundName("未检出" + caseTest.getCompoundName());
        } else if (caseTest.getIsDetected() == 1) {
            sampleData.setCompoundName("检出" + caseTest.getCompoundName());
        }
        sampleData.setWithinError(dataStruct.getWithinError());
        sampleData.setArea(String.valueOf((int) dataStruct.getArea()));
        sampleData.setSn(String.format("%.02f", dataStruct.getSn()));
    }

    /**
     * 创建检验记录
     *
     * @param businessId
     * @param type
     * @return
     * @throws Exception
     */
    @Override
    public boolean createInspectionRecord(String businessId) throws Exception {
        EntrustInfo entrustInfo = entrustInfoService.getById(businessId);
        /**
         * @apiNote invivo 生物样本
         * @apiNote inVitro 缴获物
         */
        if (entrustInfo.getBusinessType().equals(BusinessType.BOINT_CASE.getBusinessType())) {
            return this.createInspectionRecordByBiological(businessId);
        } else {
            return this.createInspectionRecordBySeizure(businessId);
        }
    }

    /**
     * 删除实验
     *
     * @param testId
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean delTestRecord(String testId) {
        TestRecordVo vo = baseMapper.getTestRecordMapById(testId);
        if (vo.getStatus() >= 5) {
            throw new RuntimeException(String.format("该实验已上机，无法删除!"));
        }
        List<String> sampleTestList = vo.getSampleTestList();
        if (sampleTestList != null && sampleTestList.size() > 0) {
            List<SampleInfo> sampleInfoList = sampleInfoService.list(Wrappers.<SampleInfo>lambdaQuery().in(SampleInfo::getId, sampleTestList));
            sampleInfoList.forEach(sampleInfo -> sampleInfo.setStatus(1));
            sampleInfoService.updateBatchById(sampleInfoList);
        }
        testRecordSampleSolutionService.remove(Wrappers.<TestRecordSampleSolution>lambdaQuery().eq(TestRecordSampleSolution::getTestId, testId));
        testRecordStandardSolutionService.remove(Wrappers.<TestRecordStandardSolution>lambdaQuery().eq(TestRecordStandardSolution::getTestId, testId));

        //文件集合：包括仪器条件、检验记录
        List<String> fileNameList = ossFile.fileList(TestRecordFileUrl.TEST_RECORD_CATALOGUE.getFileUrl() + "/" + testId);
        if (fileNameList != null && fileNameList.size() > 0) {
            fileNameList.forEach(fileName -> {
                try {
                    ossFile.fileDelete(TestRecordFileUrl.TEST_RECORD_CATALOGUE.getFileUrl() + "/" + testId + "/" + fileName);
                } catch (Exception e) {
                    // 处理异常，可以记录日志或者回滚事务
                    throw new RuntimeException("Failed to delete file: " + fileName, e);
                }
            });
        }
        testRecordSampleDataService.remove(Wrappers.<TestRecordSampleData>lambdaQuery().eq(TestRecordSampleData::getTestId, testId));
        sampleInjectorService.remove(Wrappers.<SampleInjector>lambdaQuery().eq(SampleInjector::getTestId, testId));
        return this.removeById(vo);
    }

    /**
     * 获取实验中所有的溶液列表
     *
     * @param page
     * @param testId
     * @return
     */
    @Override
    public Page<String> getResultSolutionPage(Page page, String testId) {
        TestRecordVo vo = baseMapper.getTestRecordMapById(testId);
        List<TestRecordStandardSolution> standardSolutions = testRecordStandardSolutionService.list(Wrappers.<TestRecordStandardSolution>lambdaQuery().eq(TestRecordStandardSolution::getTestId, testId).orderByAsc(TestRecordStandardSolution::getNumber));
        List<TestRecordSampleSolution> sampleSolutions = testRecordSampleSolutionService.list(Wrappers.<TestRecordSampleSolution>lambdaQuery().eq(TestRecordSampleSolution::getTestId, testId).orderByAsc(TestRecordSampleSolution::getSampleNo));
        List<String> list = new ArrayList<>();

        if (standardSolutions != null && standardSolutions.size() > 0) {
            standardSolutions.forEach(item -> {
                list.add(item.getNumber());
            });
        }
        if (sampleSolutions != null && sampleSolutions.size() > 0) {
            sampleSolutions.forEach(item -> {
                list.add(item.getSampleNo());
            });
        }
        return new PageUtils().getPages(page.getCurrent(), page.getSize(), list);
    }

    /**
     * 根据业务id获取实验信息
     *
     * @param businessIds
     * @return
     */
    @Override
    public R<Map<String, TestRecordVo>> queryTestRecordInfoByBusinessId(List<String> businessIds) {
        List<DataSolutionSampleDTO> dataSolutionSampleDTOS = testRecordSampleDataMapper
                .queryDataSolutionSampleDTOList(Wrappers.<DataSolutionSampleDTO>query()
                        .in("si.business_id", businessIds));
        Set<String> testIdSet = dataSolutionSampleDTOS
                .stream()
                .map(DataSolutionSampleDTO::getTestId)
                .collect(Collectors.toSet());
        // 以实验id为key
        Map<String, DataSolutionSampleDTO> dataSolutionSampleDTOMap = dataSolutionSampleDTOS
                .stream()
                .collect(
                        Collectors
                                .toMap(
                                        DataSolutionSampleDTO::getTestId,
                                        Function.identity(),
                                        (v1, v2) -> v2
                                )
                );
        List<TestRecordVo> testRecordMapList = baseMapper.getTestRecordMapList(Wrappers.<TestRecord>lambdaQuery().in(TestRecord::getId, testIdSet));
        // 业务id为key
        Map<String, TestRecordVo> map = new HashMap<>();
        for (TestRecordVo testRecordVo : testRecordMapList) {
            DataSolutionSampleDTO dataSolutionSampleDTO = dataSolutionSampleDTOMap.get(testRecordVo.getId());
            map.put(dataSolutionSampleDTO.getBusinessId(), testRecordVo);
        }
        return R.ok(map);
    }

}
