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

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import digital.laboratory.platform.common.core.constant.CommonConstants;
import digital.laboratory.platform.common.core.constant.OSSDirectoryConstants;
import digital.laboratory.platform.common.core.util.R;
import digital.laboratory.platform.common.oss.service.OssFile;
import digital.laboratory.platform.inspection.enums.NumberTransferHanZi;
import digital.laboratory.platform.inspection.dto.ExportSewageAnalystReportsDTO;
import digital.laboratory.platform.inspection.dto.RegionalDrugConsumptionDTO;
import digital.laboratory.platform.inspection.dto.ReportConfigDTO;
import digital.laboratory.platform.inspection.dto.SewageDataDto;
import digital.laboratory.platform.inspection.entity.TaskInfo;
import digital.laboratory.platform.inspection.service.SampleInfoService;
import digital.laboratory.platform.inspection.service.SewageDrugInspectReportService;
import digital.laboratory.platform.inspection.service.TaskInfoService;
import digital.laboratory.platform.inspection.utils.sewagereport.*;
import digital.laboratory.platform.sewage.feign.RemoteSewageJobIdentificationMaterialService;
import digital.laboratory.platform.sewage.vo.SewageJobIdentificationMaterialVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.xwpf.usermodel.BreakType;
import org.apache.poi.xwpf.usermodel.ParagraphAlignment;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.UnsupportedEncodingException;
import java.text.DecimalFormat;
import java.time.LocalDate;
import java.time.Year;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/***
 * 污水专项检测毒品分析报告 相关接口
 */
@Slf4j
@Service
public class SewageDrugInspectReportServiceImpl implements SewageDrugInspectReportService {

    @Resource
    private TaskInfoService taskInfoService;

    @Resource
    private SampleInfoService sampleInfoService;

    @Resource
    private RemoteSewageJobIdentificationMaterialService remoteSewageJobIdentificationMaterialService;

    @Resource
    private OssFile ossFile;

    // 保留1位小数的数值格式
    private final static DecimalFormat keep1DecimalPlace = new DecimalFormat("#.#");

    private final static DateTimeFormatter chineseYearDoubleMonthFormatter  = DateTimeFormatter.ofPattern("yyyy年MM月");
    private final static DateTimeFormatter chineseDateFormatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日");
    private final static DateTimeFormatter chineseYearSingleMonthFormatter  = DateTimeFormatter.ofPattern("yyyy年M月");


    /**
     * 生成污水样品毒品检测分析报告
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String generateSewageDrugInspectReportWord(ExportSewageAnalystReportsDTO dto) throws Exception{
        // 目前的污水报告 只针对于 陕西省的 默认设置参数为陕西省
        dto.setProvinceCode("610000");
        dto.setProvinceName("陕西省");
        // 陕西省各地市名称
        List<String> cityNameList = new ArrayList<>(Arrays.asList("西安市","宝鸡市","咸阳市","铜川市","渭南市","延安市","榆林市","汉中市","安康市","商洛市"));
        // 获取任务信息，得到任务的开始日期
        TaskInfo taskInfo = taskInfoService.getOne(Wrappers.<TaskInfo>lambdaQuery().eq(TaskInfo::getId, dto.getJobId()));
        // 更新任务中报告配置的信息
        taskInfo.setReportConfig(dto.getReportConfig());
        taskInfoService.updateById(taskInfo);
        // 数据
        String yearMonth = taskInfo.getTaskStartDate().format(chineseYearDoubleMonthFormatter );
        // 目前字典中的单位名称不统一，这里暂时写死，后期在改
        String organization = "国家毒品实验室陕西分中心";
        String generateDate = LocalDate.now().format(chineseDateFormatter);
        String province = dto.getProvinceName();
        // 获取目前任务之前的四个任务信息
        List<TaskInfo> taskIdByOrderStartTimeLimit4 = taskInfoService.getTaskIdByOrderStartTimeLimit4(taskInfo);

        // 对任务进行排序， 获取到根据开始时间升序的任务列表
        List<TaskInfo> taskInfoList = getTaskInfos(taskIdByOrderStartTimeLimit4, taskInfo);

        // 这里的数据是按照任务的开始时间升序顺序对应的
        List<List<SewageDataDto>> sewageDataDtoLists = getSewageTaskDataForAYear(taskInfoList, dto);

        // feign 接口调用获取 污水检材
        List<SewageJobIdentificationMaterialVO> data = getSewageJobIdentificationMaterialVOS(dto, taskInfo);

        // 使用stream 流进行排序
        List<SewageJobIdentificationMaterialVO> dateSortedByAcceptTime = data.stream()
                .sorted((d1, d2) -> d1.getAcceptTime().compareTo(d2.getAcceptTime()))
                .collect(Collectors.toList());
        List<SewageJobIdentificationMaterialVO> dateSortedByCollectTime = data.stream()
                .sorted((d1, d2) -> d1.getCollectTime().compareTo(d2.getCollectTime()))
                .collect(Collectors.toList());
        String collectDate1 = dateSortedByAcceptTime.get(0).getAcceptTime().format(chineseDateFormatter);
        String collectDate2 = dateSortedByAcceptTime.get(dateSortedByAcceptTime.size()-1).getAcceptTime().format(chineseDateFormatter);

        String samplingDate1 = dateSortedByCollectTime.get(0).getCollectTime().format(chineseDateFormatter);
        String samplingDate2 = dateSortedByCollectTime.get(dateSortedByCollectTime.size()-1).getCollectTime().format(chineseDateFormatter);
        int materialNum = data.size(); // 本次样本数量

        // 统计这些样本的地市数量
        Set<String> cityCount = data.stream().map(SewageJobIdentificationMaterialVO::getCollectPlaceCityCode).collect(Collectors.toSet());

        // 获取每个样品的数据
        List<SewageDataDto> sewageDataDtos = taskInfoService.processSewageDataDtos(dto.getDailySmokingPerCapita(), data);
        sewageDataDtoLists.add(sewageDataDtos); // 添加到 sewageDataDtoLists 方便后续进行陕西省毒品滥用情况分析
        // 统计这些样本来自多少个污水厂
        Set<String> sewagePlantNum = sewageDataDtos.stream().map(o -> StrUtil.split(o.getSampleName(), "-").get(0)).collect(Collectors.toSet());

        // 获取污水数据并根据地市分组
        Map<String, List<SewageDataDto>> sewageDataGroupByMap = getSewageDataGroupByCityNameMap(sewageDataDtos);
        // 获取陕西省各行政区主要毒品总消费量表数据
        List<RegionalDrugConsumptionDTO> regionalDrugConsumptionDTOList = fetchRegionalDrugConsumption(sewageDataGroupByMap);

        // 取列表最后一个，最后一个是计算全省的
        RegionalDrugConsumptionDTO allProvince = regionalDrugConsumptionDTOList.get(regionalDrugConsumptionDTOList.size() - 1);

        // 根据行政区转map，方便后续的陕西省各污水厂服务区及各行政区毒品千人均消费量表 读取地区平均
        Map<String, RegionalDrugConsumptionDTO> regionalDrugConsumptionDTOMap = regionalDrugConsumptionDTOList.stream().collect(Collectors.toMap(RegionalDrugConsumptionDTO::getRegional, Function.identity()));

        // 取四个任务的开始日期
        List<String> taskStartDateList = getTaskStartDateList(taskInfoList, DateTimeFormatter.ofPattern("yyyy-MM"));
        // 中国格式的日期
        List<String> taskStartDateChinaFormatList = getTaskStartDateList(taskInfoList, chineseYearSingleMonthFormatter);

        List<List<RegionalDrugConsumptionDTO>> allMergeMegionalDrugConsumptionDTOList = getAllMergeBeforeMegionalDrugConsumptionDTOList(sewageDataDtoLists, taskInfoList);

        // 把前面季度的全省毒品消费量和当前季度的放到一个list里，目的是为了后面的报告中 比较使用到
        List<RegionalDrugConsumptionDTO> allTaskProvinceDrugConsumptionList = new ArrayList<>();
        extractedProvinceDrugConsumeData(allMergeMegionalDrugConsumptionDTOList, allTaskProvinceDrugConsumptionList, allProvince);

        // 生成word文档
        XWPFDocument doc = new XWPFDocument();
        // resourceLoader.getResource("classpath:template/templateStyles.docx").getInputStream() 获取resource目录下的模板文件
        ByteArrayInputStream filePath = getTemplateStyleFilePath(); // 获取模板路径
        WordUtils.setDocumentStyle(doc, filePath);

        generateHomePage(doc, yearMonth, organization, generateDate, province);

        WordUtils.createDocTitle(doc, yearMonth + "份"+ province +"污水专项检测毒品分析报告", false);

        WordUtils.createParagraphTitle(doc, "一、工作概况", false, 2);
        WordUtils.createParagraph(
                doc,
                ParagraphAlignment.LEFT,
                organization +
                        "自"+ collectDate1 +"至"+ collectDate2 +
                        "收到"+ province + cityCount.size() +"个地市共"+
                        materialNum +"份污水样品（包含"+ sewagePlantNum.size() +"个污水处理厂），各污水处理厂监测点采样日期在" +
                        samplingDate1 +"至" + samplingDate2 + "。本次检测分析的物质包括以下13种，检测结果为各目标物的浓度数据（单位：纳克/升）：",
                false,
                2);

        // 获取常见毒品及其代谢物  === "甲基苯丙胺、苯丙胺（甲基苯丙胺代谢物）；氯胺酮、去甲氯胺酮（氯胺酮代谢物）；吗啡（海洛因二级代谢产物）、O6-单乙酰吗啡（海洛因一级代谢产物）；" +
        //                        "可待因；可卡因、苯甲酰爱康宁（可卡因代谢物）；3,4－亚甲基二氧甲基苯丙胺（摇头丸MDMA）、3,4－亚甲基二氧基苯丙胺（摇头丸代谢物）；芬太尼。"
        List<ReportConfigDTO> reportConfigDTOSFilterHeroin = dto.getCommonDrugAndMetabolites().stream().filter(o -> !o.getDictLabel().contains("海洛因")).collect(Collectors.toList());
        String commonDrugMetabolites = getCommonDrugMetabolites(reportConfigDTOSFilterHeroin);

        WordUtils.createParagraphAndBold(doc, ParagraphAlignment.LEFT, "常见毒品及其代谢物（"+ reportConfigDTOSFilterHeroin.size() +"种）：",
                commonDrugMetabolites, false, 2);
        // 人口标记物（1种）：可替宁（烟草代谢物）。
        String populationMarkingMaterial = getPopulationMarking(dto.getPopulationMarkers());
        WordUtils.createParagraphAndBold(doc, ParagraphAlignment.LEFT, "人口标记物（"+ dto.getPopulationMarkers().size() +"种）：",
                populationMarkingMaterial, false, 2);

        WordUtils.createParagraph(doc, ParagraphAlignment.LEFT, "根据各毒品目标物的浓度，并综合污水处理厂进水流量、人口标记物浓度，" +
                "计算了各污水处理厂服务区、各行政区的毒品千人均消费量和毒品总消费量，形成本分析报告。", false, 2);

        WordUtils.createParagraphAndBold(doc,
                ParagraphAlignment.LEFT,
                "（注：特别指出，本次检测工作只对收到的" + materialNum + "份污水样品进行分析评估，由于仅采集两天样品进行分析，数据结果仅供参考，个别异常数据还需深入研究，特此说明）",
                "",
                false,
                2);

        // 第二
        WordUtils.createParagraphTitle(doc, "二、全省总体情况", false, 2);
        WordUtils.createParagraph(doc, ParagraphAlignment.LEFT, yearMonth + "份，陕西省毒品消费结构：", false, 2);

        // 设置饼图数据
        PieChartForm pieChartForm = generateCommonDrugConsumeStructureChart(allProvince);
        WordUtils.createPieChart(doc, pieChartForm, "图1 陕西省常见毒品消费结构图");

        Map<String, Object> inspectResultAnalysisMap = getInspectResultAnalysis(pieChartForm, allProvince);
        WordUtils.createParagraphAndBold(doc,
                ParagraphAlignment.LEFT,
                "根据检测结果分析：" + inspectResultAnalysisMap.get("resultAnalysis").toString(),
                "",
                false,
                2);

        // 全省监测区域内容生成
        String provinceMonitorsDrugConsumeContent = getProvinceMonitorsDrugConsumeContent(inspectResultAnalysisMap, allProvince);
        WordUtils.createParagraph(doc, ParagraphAlignment.LEFT, provinceMonitorsDrugConsumeContent, false, 2);

        // todo 待完成
        WordUtils.createParagraphAndBold(doc,
                ParagraphAlignment.LEFT,
                "各毒品滥用现状如下：",
                "",
                false,
                2);


        // 循环生成各毒品的滥用现状
        /*
         * 原文
         * 冰毒 2023年2月份在54%的污水厂有冰毒及其代谢物检出。
         * 全省冰毒的千人均消费量为2.5毫克/千人/天，核算陕西省冰毒的总消费量为39.4克/天。
         * 西安冰毒总消费量为32.5克/天，占全省总消费量的82.5%。
         */
        String methamphetamineAbuseCurrentSituation = getMethamphetamineAbuseCurrentSituation(yearMonth, sewageDataDtos, regionalDrugConsumptionDTOMap, allProvince, sewagePlantNum);
        WordUtils.createParagraphAndBold(doc,
                ParagraphAlignment.LEFT,
                "冰毒 ",
                methamphetamineAbuseCurrentSituation,
                false,
                2);

        /* 原文
         * 海洛因 2023年2月在98%的污水处理厂均有吗啡检出（海洛因的代谢物）。
         * 陕西省海洛因的千人均消费量18毫克/千人/天，核算海洛因的总消费量为285.3克/天。
         * 特别指出，根据污水中的吗啡和可待因的浓度计算海洛因消费量时，
         * 粗略扣除了合法药物、医用吗啡以及磷酸可待因代谢吗啡的干扰，
         * 但未充分考虑干扰的区域性和季节性差异，因而存在一定误差，结果仅供参考。
         */
        String heroinAbuseCurrentSituation = getHeroinAbuseCurrentSituation(yearMonth, sewageDataDtos, regionalDrugConsumptionDTOMap, allProvince, sewagePlantNum);
        WordUtils.createParagraphAndBold(doc,
                ParagraphAlignment.LEFT,
                "海洛因 ",
                heroinAbuseCurrentSituation,
                false,
                2);

        /* 原文
         * 氯胺酮 2023年2月在9.3%的污水处理厂均有氯胺酮检出，
         * 与2022年相比，2023年2月份氯胺酮滥用明显增加，在西安、宝鸡、安康、渭南和榆林5个地市（19个污水处理厂）中均有检出，
         * 氯胺酮浓度范围为0.3~2.9 ng/L，后续检测中可持续关注。
         */
        String ketamineAbuseCurrentSituation = getDrugAbuseCurrentSituation(
                yearMonth,
                sewageDataDtos,
                allTaskProvinceDrugConsumptionList,
                sewagePlantNum,
                sewageDataDto -> sewageDataDto.getKetamineConcentration() != 0,
                Comparator.comparing(SewageDataDto::getKetamineConcentration),
                SewageDataDto::getKetamineConcentration,
                "氯胺酮");
        WordUtils.createParagraphAndBold(doc,
                ParagraphAlignment.LEFT,
                "氯胺酮 ",
                ketamineAbuseCurrentSituation,
                false,
                2);
        /* 原文
         * 可卡因 本次监测中，在汉中市宁强县污水处理厂中有12.7ng/L的可卡因检出，
         * 其他地市均未检出，说明全省范围的明显滥用，需长期关注，掌握变化趋势。
         */
        String cocaineAbuseCurrentSituation = getDrugAbuseCurrentSituation(
                yearMonth,
                sewageDataDtos,
                allTaskProvinceDrugConsumptionList,
                sewagePlantNum,
                sewageDataDto -> sewageDataDto.getCocaineConcentration() != 0,
                Comparator.comparing(SewageDataDto::getCocaineConcentration),
                SewageDataDto::getCocaineConcentration,
                "可卡因");
        WordUtils.createParagraphAndBold(doc,
                ParagraphAlignment.LEFT,
                "可卡因 ",
                cocaineAbuseCurrentSituation,
                false,
                2);

        /* 原文
         * 摇头丸（MDMA）在本次监测中均未检出，说明不存在摇头丸的明显滥用。
         */
        String mdmaAbuseCurrentSituation = getDrugAbuseCurrentSituation(
                yearMonth,
                sewageDataDtos,
                allTaskProvinceDrugConsumptionList,
                sewagePlantNum,
                sewageDataDto -> sewageDataDto.getMdmaConcentration() != 0 && sewageDataDto.getMdaConcentration() != 0,
                Comparator.comparing(SewageDataDto::getMdmaConcentration),
                SewageDataDto::getMdmaConcentration,
                "摇头丸（MDMA）");
        WordUtils.createParagraphAndBold(doc,
                ParagraphAlignment.LEFT,
                "摇头丸（MDMA） ",
                mdmaAbuseCurrentSituation,
                false,
                2);

        WordUtils.createParagraphAndBold(doc,
                ParagraphAlignment.LEFT,
                "芬太尼 ",
                "在本次监测中均未检出，说明不存在芬太尼的明显滥用。",
                false,
                2);

        if (dto.getGenerateEtomidateTable()) {
            // 判断是否要生成依托咪酯的滥用现状
            WordUtils.createParagraphAndBold(doc,
                    ParagraphAlignment.LEFT,
                    "依托咪酯 ",
                    getEtomidateAbuseSCurrentSituation(yearMonth, sewageDataDtos),
                    false,
                    2);
        }

        WordUtils.createParagraphAndBold(doc,
                ParagraphAlignment.CENTER,
                "表1 陕西省各行政区主要毒品总消费量表 ",
                "",
                false,
                0);
        // 陕西省各行政区主要毒品总消费量表
        List<String> nameList = new ArrayList<>(Arrays.asList("海洛因", "冰毒", "氯胺酮", "MDMA", "可卡因", "芬太尼", "综合"));
        TableCreateUtils.fetchRegionalDrugConsumptionTable(doc, regionalDrugConsumptionDTOList.size() + 2,15, nameList, regionalDrugConsumptionDTOList);

        // 柱状图
        BarLineChartForm chartForm1 = getServiceAreaDrugConsumeBarLineChartForm(doc, regionalDrugConsumptionDTOList, pieChartForm, cityNameList);
        WordUtils.createBarLineCharts(doc, chartForm1, "图2 陕西省各行政区毒品千人均消费量",true);

        // 折线柱状图 todo
        doc.createParagraph().createRun().addBreak();
        BarLineChartForm chartForm = generateDrugConsumePerThousandTrendChartData(
                taskStartDateList,
                pieChartForm.getXData(),
                allTaskProvinceDrugConsumptionList);
        WordUtils.createBarLineCharts(doc, chartForm, "图3 陕西省毒品千人均消费量变化趋势",true);
        // 生成对这几个任务中陕西省的毒品千人均消费量变化趋势进行描述
        String drugConsumePerThousandTrendDescribe = getDrugConsumePerThousandTrendDescribe(allTaskProvinceDrugConsumptionList, taskInfoList);
        // 判断当前任务前面是否存在任务
        TaskInfo taskInfo1 = taskIdByOrderStartTimeLimit4.get(0) == null ? taskInfo : taskIdByOrderStartTimeLimit4.get(0);
        WordUtils.createParagraph(doc, ParagraphAlignment.LEFT,
                "陕西省" + taskInfo1.getTaskStartDate().format(chineseYearDoubleMonthFormatter ) +
                        "至" + yearMonth +"综合千人均消费量变化趋势如图3所示。" +
                        drugConsumePerThousandTrendDescribe,
                false, 2);
        // 生成全省海洛因的消费量变化趋势描述
        String heroinProvinceAbuseTendDescribe = analysisHeroinProvinceAbuseTendDescribe(allTaskProvinceDrugConsumptionList, taskStartDateChinaFormatList);
        /* 原文
         * 全省海洛因千人均消费量与综合千人均消费量变化趋势一致。
         * 2023年2月（18毫克/千人/天）与2022年3月（24.5毫克/千人/天）相比，同比下降36%，
         * 与2022年12月（46.5毫克/千人/天）相比，环比下降47%，海洛因滥用趋势逐渐下降。
         */
        WordUtils.createParagraph(doc, ParagraphAlignment.LEFT,
                "全省海洛因千人均消费量与综合千人均消费量变化趋势一致。" + heroinProvinceAbuseTendDescribe,
                false, 2);

        /* 原文
         * 全省冰毒千人均消费量2023年2月（2.5毫克/千人/天）与2022年3月（3毫克/千人/天）相比，同比下降16.6%，
         * 与2022年12月（1.1毫克/千人/天）相比，环比增加127%。全省冰毒滥用明显增加。
         */
        String methamphetamineProvinceAbuseTendDescribe = analysisMethamphetamineProvinceAbuseTendDescribe(allTaskProvinceDrugConsumptionList, taskStartDateChinaFormatList);
        WordUtils.createParagraph(doc, ParagraphAlignment.LEFT,
                methamphetamineProvinceAbuseTendDescribe,
                false, 2);

        WordUtils.createParagraphAndBold(doc,
                ParagraphAlignment.CENTER,
                "表2 陕西省各污水厂服务区及各行政区毒品千人均消费量表 ",
                "",
                false,
                0);

        // 表2 陕西省各污水厂服务区及各行政区毒品千人均消费量表
        generateServiceAreaDrugConsumptionPerThousandTable(sewageDataGroupByMap, regionalDrugConsumptionDTOMap, doc);

        // 空两行，模仿得到的模板格式
        doc.createParagraph();
        doc.createParagraph();
        WordUtils.createParagraph(doc, ParagraphAlignment.LEFT, "注：1.因西安和铜川各行政区之间无独立污水处理厂，因此除西安和铜川外按照污水处理厂计算千人均消费量量，其余地市按照区县计算。", false, 2);
        WordUtils.createParagraphCustomTab(doc, ParagraphAlignment.LEFT, "2.不合格样品：定义为检测出该样品中可替宁浓度低于500 ng/L。", false, 2, 2);
        if (dto.getGenerateEtomidateTable()) {
            doc.createParagraph().createRun().addBreak(BreakType.PAGE); // 创建新的一页
            TableCreateUtils.subsection(doc, TableCreateUtils.SUBSECTIONONE);
            WordUtils.createParagraphAndBold(doc,
                    ParagraphAlignment.CENTER,
                    "表3 陕西省第"+ yearMonth +"份各行政区污水厂检出依托咪酯统计表",
                    "",
                    false,
                    0);
            List<Object> headerList = new ArrayList<>(Arrays.asList("行政区", "污水厂数量", "污水样品数量", "污水厂检出数量", "样品检出数量", "样品检出百分比", "污水厂检出百分比", "依托咪酯检出污水厂名称"));
            List<List<Object>> tableDataList = new ArrayList<>();
            tableDataList.add(headerList);
            // 组装依托咪酯统计数据
            getEtomidateDetectedStatisticalData(sewageDataGroupByMap, tableDataList);
            TableCreateUtils.fetchEtomidateDetectedStatisticalTable(doc, tableDataList.size(), headerList.size(), tableDataList);
            WordUtils.createParagraph(doc,
                    ParagraphAlignment.LEFT,
                    "注：榆林、杨凌送样时未建立依托咪酯检测方法，故未检。",
                    false,
                    2);
            TableCreateUtils.subsection(doc, TableCreateUtils.SUBSECTIONTWO);

        }
        // 三、各区域情况
//        doc.createParagraph().createRun().addBreak(BreakType.PAGE);
        WordUtils.createParagraphTitle(doc, "三、各区域情况", false, 2);
        WordUtils.createParagraphAndBothSidesBold(doc,
                ParagraphAlignment.LEFT,
                "（详细数据见表1）",
                "根据" + yearMonth + "监测结果分析，陕西省各行政区和各监测污水厂服务片区的常规毒品滥用情况分析如下",
                "：",
                false,
                2);
        WordUtils.createParagraphAndMiddleBold(
                doc,
                ParagraphAlignment.LEFT,
                "从",
                "综合总消费量",
                "（冰毒、K粉、海洛因、可卡因、摇头丸和芬太尼五种毒品折算总量，反映某区域内全体人口的毒品滥用总量）分析。",
                false,
                2);

        // 对当前季度任务的各行政区的综合总消费量进行降序排序
        List<RegionalDrugConsumptionDTO> tcRegionalDrugConsumptionDTOS = getRegionalDrugConsumptionDTOSSortedDesc(regionalDrugConsumptionDTOList, true, true);
        // 根据得到的降序数组进行描述
        String combinedTotalConsumptionDescribe = compareEachCityCombinedTotalConsumptionDescribe(tcRegionalDrugConsumptionDTOS);
        /**
         * 按行政区，西安市的综合总消费量最高为178.1克/天，占全省监测区域消费总量的54.8%；
         * 其次是咸阳市，综合总消费量为33.6克/天，分别占全省监测区域消费总量的10.3%；
         * 位居第三、第四、第五位的是榆林市、渭南市和宝鸡市，综合总消费量分别为31.4克/天、27.1克/天和24.9克/天，
         * 占全省监测区域消费总量的9.6%、8.3%和7.6%；其余五个行政区综合总消费量均低于20克/天，占比均低于6%。
         */
        WordUtils.createParagraph(
                doc,
                ParagraphAlignment.LEFT,
                combinedTotalConsumptionDescribe,
                true,
                2);
        // 如图所示各行政区毒品滥用总量排序（即综合总消费量排序）：西安市＞咸阳市＞榆林市＞渭南市＞宝鸡市＞延安市＞汉中市＞安康市＞商洛市＞铜川市＞杨凌区。
        String combinedTotalConsumptionSortedDescStr = getCombinedTotalConsumptionSortedDesc(tcRegionalDrugConsumptionDTOS);
        WordUtils.createParagraphAndBold(
                doc,
                ParagraphAlignment.LEFT,
                "如图所示各行政区毒品滥用总量排序（即综合总消费量排序）：",
                combinedTotalConsumptionSortedDescStr,
                true,
                2);

        // 折线图  各行政区毒品总消费量变化趋势
        WordUtils.createBarLineCharts(doc,
                getRendsInTotalDrugConsumptionInEachAdministrativeRegion(
                        allMergeMegionalDrugConsumptionDTOList,
                        regionalDrugConsumptionDTOList,
                        taskStartDateList,
                        cityNameList),
                "",
                false);

        WordUtils.createParagraphAndBold(
                doc,
                ParagraphAlignment.CENTER,
                "图4 各行政区毒品总消费量变化趋势",
                "",
                false,
                0);
        // todo 这里的文字描述不太好生， 暂时不做
        WordUtils.createHighLightParagraph(doc,
                ParagraphAlignment.LEFT,
                "如图4所示，"+
                        taskStartDateChinaFormatList.get(0) +
                        "至" +
                        taskStartDateChinaFormatList.get(taskStartDateChinaFormatList.size() - 1) +
                        "各市毒品消费总量变化趋势基本一致，" +
                        "2022年3月至2022年9月各市毒品消费总量呈现下降趋势，至2022年12月呈现上升趋势，2023年2月出现明显下降。其中西安变化趋势最为明显，" +
                        "2023年2月与2022年3月相比，西安总消费量同比下降16.2%，与2022年12月相比，西安总费量环比下降70%。",
                false,
                2);

        WordUtils.createParagraphAndMiddleBold(
                doc,
                ParagraphAlignment.LEFT,
                "综合千人均消费量",
                "从",
                "（冰毒、K粉、海洛因、可卡因、摇头丸和芬太尼五种毒品千人均消费量的折算总量，" +
                        "反映单位人口的毒品滥用量）分析。",
                false,
                2);

        // 对当前季度任务的各行政区的综合总消费量进行降序排序
        List<RegionalDrugConsumptionDTO> pccRegionalDrugConsumptionDTOS = getRegionalDrugConsumptionDTOSSortedDesc(regionalDrugConsumptionDTOList, false, true);
        String combinedThousandPerConsumptionCompareDescribe = getCombinedThousandPerConsumptionCompareDescribe(pccRegionalDrugConsumptionDTOS, allProvince);
        /**
         * 全省各行政区千人均变化趋势如图2所示，全省平均千人均消费量为20.5毫克/千人/天，
         * 咸阳市的综合千人均消费量最高，为63.9毫克/千人/天，是全省平均水平的3.1倍；
         * 其次是汉中市和宝鸡市，综合千人均消费量为38毫克/千人/天和31.9毫克/千人/天；
         * 位居第四的是商洛市，综合千人均消费量分别为为27.4毫克/千人/天；
         * 位居第五至十一位的分别是杨凌区、安康市、西安市、渭南市、延安市、铜川市和榆林市，
         * 分别为27.5毫克/千人/天、22.3毫克/千人/天、20.9毫克/千人/天、20.5毫克/千人/天、19.3毫克/千人/天、16.4毫克/千人/天和13.9毫克/千人/天
         */
        WordUtils.createParagraph(doc,
                ParagraphAlignment.LEFT,
                combinedThousandPerConsumptionCompareDescribe,
                false,
                2);

        // 对当前季度任务的各行政区的综合总消费量进行降序排序（全省的也包含在排序的数组里）
        List<RegionalDrugConsumptionDTO> pccRegionalDrugConsumptionDTOS1 = getRegionalDrugConsumptionDTOSSortedDesc(regionalDrugConsumptionDTOList, false, false);
        String combinedThousandPerConsumptionSortedDesc = getCombinedThousandPerConsumptionSortedDesc(pccRegionalDrugConsumptionDTOS1);
        WordUtils.createParagraphAndBold(
                doc,
                ParagraphAlignment.LEFT,
                "各行政区毒品滥用水平排序（即综合千人均消费量排序）：",
                combinedThousandPerConsumptionSortedDesc,
                false,
                2);

        // 折线图  各行政区毒品千人均消费量变化趋势
        WordUtils.createBarLineCharts(doc, getTrendsInPerCapitaDrugConsumptionEachAdministrativeRegion(allMergeMegionalDrugConsumptionDTOList, regionalDrugConsumptionDTOList, taskStartDateList, cityNameList), "",false);

        WordUtils.createParagraphAndBold(
                doc,
                ParagraphAlignment.CENTER,
                "图5 各行政区毒品千人均消费量变化趋势",
                "",
                false,
                0);

        WordUtils.createHighLightParagraph(doc,
                ParagraphAlignment.LEFT,
                "如图5所示，2023年2月全省千人均消费量（除汉中市和咸阳市外）整体变化趋势与全省总消费量变化趋势保持一致。" +
                        "2022年3月至2022年9月各市千人均消费量呈现下降趋势，至2022年12月呈现上升趋势，2023年2月出现明显下降，" +
                        "其中西安市和汉中市变化最为明显。汉中市2022年3月至2022年6月有所下降，" +
                        "后3个月基本保持平稳。咸阳市2022年3月至2022年9月有所下降，其后2个月保持上升趋势。" +
                        "各市具体增长率/下降率见下表。",
                false,
                2);
        WordUtils.createParagraphAndBold(
                doc,
                ParagraphAlignment.CENTER,
                "表3 陕西省各污水厂服务区及各行政区毒品千人均消费量表",
                "",
                false,
                0);

        // 表3 陕西省各污水厂服务区及各行政区毒品千人均消费量表
        List<List<Object>> cityDrugConsumptionGrowthRatesData = fetchCityDrugConsumptionGrowthRatesData(allMergeMegionalDrugConsumptionDTOList, regionalDrugConsumptionDTOList, taskInfoList, cityNameList);
        TableCreateUtils.fetchCityDrugConsumptionGrowthRatesTable(doc, 3, 11, cityNameList, cityDrugConsumptionGrowthRatesData);

        WordUtils.createParagraphAndBothSidesBold(
                doc,
                ParagraphAlignment.LEFT,
                "生活污水处理厂水样中“人口标记物”可替宁浓度过低或过高，会被公安部禁毒情报技术中心判定为",
                "另有一点需要再次说明：",
                "数据不合格，",
                false,
                2);

        WordUtils.createParagraph(doc,
                ParagraphAlignment.LEFT,
                "影响陕西省的检测数据质量。请各市禁毒部门除关注毒品消费量数据之外，" +
                        "重点寻找部分污水处理厂水样中可替宁浓度异常的原因（公安部禁毒情报技术中心给出的经验值范围：500~5000纳克/升）。" +
                        "对于可替宁浓度持续低于经验值的区县，建议在条件允许的情况下，申请连续采样送检，以供陕西分中心技术人员判断原因。",
                false,
                2);

        // 换页
        doc.createParagraph().createRun().addBreak(BreakType.PAGE);
        WordUtils.createParagraphTitle(doc, "四、2023年2月陕西省毒品滥用情况分析", false, 2);
        WordUtils.createParagraphTitle(doc, "1、冰毒（甲基苯丙胺）", false, 3);

        List<String> tableName2List = new ArrayList<>(Arrays.asList("污水处理厂名称","甲基苯丙胺（ng/L）"));
        // 对数据进行排序
        List<List<SewageDataDto>> sortSewageDataDtos = getSortSewageDataDtos(sewageDataDtoLists,
                Comparator.comparing(SewageDataDto::getMethamphetamineConcentration).reversed());
        List<List<Object>> cityDrugAbuseConditionAnalysisData = fetchCityDrugAbuseConditionAnalysisData(
                sortSewageDataDtos,
                SewageDataDto::getMethamphetamineConcentration,
                10);
        // 描述 冰毒（甲基苯丙胺） 的排名情况
        String describeMethamphetamineConcentrationRank = getDescribeMethamphetamineConcentrationRank(sortSewageDataDtos.get(sortSewageDataDtos.size()-1));
        WordUtils.createParagraph(doc,
                ParagraphAlignment.LEFT,
                yearMonth + "份，"
                        + describeMethamphetamineConcentrationRank,
                false,
                2);

        WordUtils.createParagraphAndBold(
                doc,
                ParagraphAlignment.CENTER,
                "表4 陕西省污水处理厂冰毒浓度排名表",
                "",
                false,
                0);

        int analysisTableCols = taskStartDateChinaFormatList.size() * 2 + 1;
        TableCreateUtils.fetchCityDrugAbuseConditionAnalysisTable(doc, 12, analysisTableCols, taskStartDateChinaFormatList, tableName2List, cityDrugAbuseConditionAnalysisData);

        WordUtils.createParagraphTitle(doc, "2、吗啡", false, 3);

        // 对数据进行排序
        List<List<SewageDataDto>> sortSewageDataDtos1 = getSortSewageDataDtos(sewageDataDtoLists,
                Comparator.comparing(SewageDataDto::getMorphineConcentration).reversed());
        List<List<Object>> cityDrugAbuseConditionAnalysisData1 = fetchCityDrugAbuseConditionAnalysisData(
                sortSewageDataDtos1,
                SewageDataDto::getMorphineConcentration,
                15); // 吗啡是列表前15名的污水厂

        WordUtils.createParagraph(doc,
                ParagraphAlignment.LEFT,
                yearMonth + "份吗啡对全省毒品消费总量贡献较大，" + getDescribeMorphineConcentrationRank(sortSewageDataDtos1.get(sortSewageDataDtos1.size() - 1)),
                false,
                2);

        WordUtils.createParagraphAndBold(
                doc,
                ParagraphAlignment.CENTER,
                "表5 陕西省污水处理厂吗啡检测情况",
                "",
                false,
                0);

        List<String> tableName4List = new ArrayList<>(Arrays.asList("污水处理厂名称","吗啡（ng/L）"));

        TableCreateUtils.fetchCityDrugAbuseConditionAnalysisTable(doc, 17, analysisTableCols, taskStartDateChinaFormatList, tableName4List,cityDrugAbuseConditionAnalysisData1);

        WordUtils.createParagraphAndBold(doc, ParagraphAlignment.LEFT, "注：", "100 ng/L为公安部禁毒情报技术中心定义的警戒值。", false, 2);

        TableCreateUtils.subsection(doc, TableCreateUtils.SUBSECTIONONE);
        WordUtils.createParagraphAndBold(
                doc,
                ParagraphAlignment.CENTER,
                "附表：陕西省各污水厂监测点主要毒品及其代谢物每日浓度表",
                "",
                false,
                0);
        List<String> tableName5List = getDailyDrugAndMetaboliteConcentrationName(reportConfigDTOSFilterHeroin, dto.getPopulationMarkers());
//        List<String> tableName5List = new ArrayList<>(Arrays.asList("可替宁","可待因","MDA","MDMA","可卡因","苯甲酰爱康宁","吗啡","O6-单乙酰吗啡","甲基苯丙胺","苯丙胺","氯胺酮","去甲氯胺酮","芬太尼"));
        Map<String, List<List<Object>>> map = generateDailyDrugAndMetaboliteConcentrationData(sewageDataGroupByMap, tableName5List);
        // 加2 的原因是有两行表头
        TableCreateUtils.fetchDailyDrugAndMetaboliteConcentrationTable(doc, sewageDataDtos.size() + 2, 17, tableName5List, map);
        TableCreateUtils.subsection(doc, TableCreateUtils.SUBSECTIONTWO);
        // 因为设置横向后，多出了一页空白页，直接删除最后一页
//        int pages = doc.getBodyElements().size();
//        doc.removeBodyElement(pages - 1);
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        doc.write(outputStream);
        doc.close();
        String fileSaveMinioPath = OSSDirectoryConstants.DOCUMENT_SEWAGE_REPORTS_DIRECTORY + "/" + yearMonth + "/" + taskInfo.getId() + "/" + yearMonth + "份"+ province +"污水专项检测毒品分析报告.docx";
//        WordUtils.saveAs(doc, "污水报告测试-111.docx", "C:\\Users\\27109\\Desktop\\DesktopFiles\\PersonalFile\\workFile\\LianChuang\\task\\污水报告\\");
        ossFile.fileSave(fileSaveMinioPath, new ByteArrayInputStream(outputStream.toByteArray()));
        return fileSaveMinioPath;
    }

    /**
     * 获取附表：陕西省各污水厂监测点主要毒品及其代谢物每日浓度表中的毒品名称
     * @param commonDrugAndMetabolites
     * @param populationMarkers
     * @return
     */
    private List<String> getDailyDrugAndMetaboliteConcentrationName(List<ReportConfigDTO> commonDrugAndMetabolites, List<ReportConfigDTO> populationMarkers) {
        List<String> dailyDrugAndMetaboliteConcentrationName = populationMarkers.stream().map(ReportConfigDTO::getDictLabel).collect(Collectors.toList());
        dailyDrugAndMetaboliteConcentrationName.addAll(commonDrugAndMetabolites.stream().map(ReportConfigDTO::getDictLabel).collect(Collectors.toList()));
        return dailyDrugAndMetaboliteConcentrationName;
    }

    /**
     * 生成每常见毒品及代谢物的描述
     * @param reportConfigDTOSFilterHeroin
     * @return
     */
    private String getCommonDrugMetabolites(List<ReportConfigDTO> reportConfigDTOSFilterHeroin) {
        StringBuilder stringBuilder = new StringBuilder();
        Map<String, List<ReportConfigDTO>> reportConfigGroupMap = reportConfigDTOSFilterHeroin.stream().collect(Collectors.groupingBy(ReportConfigDTO::getDictType));
        reportConfigGroupMap.forEach((dictType, reportConfigs) -> {
            Map<String, List<ReportConfigDTO>> reportConfigsByType = reportConfigs.stream().collect(Collectors.groupingBy(ReportConfigDTO::getType));
            List<ReportConfigDTO> commonDrugs = reportConfigsByType.getOrDefault("1", Collections.emptyList());
            List<ReportConfigDTO> metabolites = reportConfigsByType.getOrDefault("2", Collections.emptyList());
            // 常见毒品和代谢物都不可能为空
            if (CollUtil.isNotEmpty(commonDrugs)) {
                stringBuilder.append(commonDrugs.get(0).getDictLabel());
                if (CollUtil.isNotEmpty(metabolites)) {
                    String metaboliteLabels = metabolites.stream()
                            .map(configDTO -> configDTO.getDictLabel() + configDTO.getRemark())
                            .collect(Collectors.joining("、"));
                    stringBuilder.append("、").append(metaboliteLabels);
                }
                stringBuilder.append("；");
            } else {
                String metaboliteLabels = metabolites.stream()
                        .map(configDTO -> configDTO.getDictLabel() + configDTO.getRemark())
                        .collect(Collectors.joining("、"));
                stringBuilder.append(metaboliteLabels).append("；");
            }
        });
        String resultStr = stringBuilder.toString();
        if (StrUtil.isNotBlank(resultStr)) {
            return resultStr.substring(0, resultStr.length() - 1) + "。";
        }
        return "";
    }

    /**
     * 生成人口标记物的描述
     * @param reportConfigDTOS
     * @return
     */
    private String getPopulationMarking(List<ReportConfigDTO> reportConfigDTOS) {
        StringBuilder stringBuilder = new StringBuilder();
        Map<String, List<ReportConfigDTO>> reportConfigGroupMap = reportConfigDTOS.stream().collect(Collectors.groupingBy(ReportConfigDTO::getDictType));
        reportConfigGroupMap.forEach((dictType, reportConfigs) -> {
            Map<String, List<ReportConfigDTO>> reportConfigsByType = reportConfigs.stream().collect(Collectors.groupingBy(ReportConfigDTO::getType));
            List<ReportConfigDTO> commonDrugs = reportConfigsByType.getOrDefault("1", Collections.emptyList());
            List<ReportConfigDTO> metabolites = reportConfigsByType.getOrDefault("2", Collections.emptyList());
            // 常见毒品和代谢物都不可能为空
            if (CollUtil.isNotEmpty(commonDrugs)) {
                stringBuilder.append(commonDrugs.get(0).getDictLabel()).append(commonDrugs.get(0).getRemark());
                if (CollUtil.isNotEmpty(metabolites)) {
                    String metaboliteLabels = metabolites.stream()
                            .map(configDTO -> configDTO.getDictLabel() + configDTO.getRemark())
                            .collect(Collectors.joining("、"));
                    stringBuilder.append("、").append(metaboliteLabels);
                }
                stringBuilder.append("；");
            } else {
                String metaboliteLabels = metabolites.stream()
                        .map(configDTO -> configDTO.getDictLabel() + configDTO.getRemark())
                        .collect(Collectors.joining("、"));
                stringBuilder.append(metaboliteLabels).append("；");
            }
        });
        String resultStr = stringBuilder.toString();
        if (StrUtil.isNotBlank(resultStr)) {
            return resultStr.substring(0, resultStr.length() - 1) + "。";
        }
        return "";
    }

    /**
     * 组装生成依托咪酯的统计数据
     * @param sewageDataGroupByMap
     * @param tableDataList
     */
    private void getEtomidateDetectedStatisticalData(Map<String, List<SewageDataDto>> sewageDataGroupByMap, List<List<Object>> tableDataList) {
//        int materialSize = sewageDataDtos.size(); // 污水样品的总数量
//        int plantSize = sewagePlantNum.size();
        sewageDataGroupByMap.forEach((k,v) -> {
            int materialSize = v.size(); // 污水样品的总数量
            Set<String> sewagePlantNum = v.stream().map(sewageDataDto -> StrUtil.split(sewageDataDto.getSampleName(), "-").get(0)).collect(Collectors.toSet());
            int plantSize = sewagePlantNum.size();
            List<Object> subDataList = new ArrayList<>();
            subDataList.add(k); // 行政区名称
            subDataList.add(plantSize);// 污水厂数量
            subDataList.add(materialSize); // 污水样品数量
            // 检出依托咪酯的污水厂
            List<SewageDataDto> detectedEtomidateList = v.stream().filter(sewageDataDto -> sewageDataDto.getEtomidateConcentration() != 0).collect(Collectors.toList());
            Set<String> sewagePlantNameList = detectedEtomidateList.stream().map(sewageDataDto -> StrUtil.split(sewageDataDto.getSampleName(), "-").get(0)).collect(Collectors.toSet());
            int detectedEtomidatePlantSize = sewagePlantNameList.size();
            int detectedEtomidateMaterialSize = detectedEtomidateList.size();
            subDataList.add(detectedEtomidatePlantSize);// 污水厂检出数量
            subDataList.add(detectedEtomidateMaterialSize); // 样品检出数量
            subDataList.add(Math.ceil(detectedEtomidateMaterialSize / (double) materialSize * 100) + "%"); // 样品检出百分比
            subDataList.add(Math.ceil(detectedEtomidatePlantSize / (double) plantSize * 100) + "%"); // 污水厂检出百分比
            subDataList.add(sewagePlantNameList.stream().collect(Collectors.joining("，")));
            tableDataList.add(subDataList);
        });
    }

    /**
     * 生成相关依托咪酯相关的滥用现状
     * @param yearMonth
     * @param sewageDataDtos
     * @return
     */
    private String getEtomidateAbuseSCurrentSituation(String yearMonth, List<SewageDataDto> sewageDataDtos) {
        StringBuilder stringBuilder = new StringBuilder();
        List<SewageDataDto> sewageDataDtosDetectedEtomidate = sewageDataDtos.stream().filter(o -> o.getEtomidateConcentration() != 0).collect(Collectors.toList());
        Set<String> sewagePlantNum = sewageDataDtosDetectedEtomidate.stream().map(o -> StrUtil.split(o.getSampleName(), "-").get(0)).collect(Collectors.toSet());

        stringBuilder
                .append(yearMonth)
                .append("检测的全省污水中依托咪酯含量，发现全省有")
                .append(sewagePlantNum.size())
                .append("家污水厂共")
                .append(sewageDataDtosDetectedEtomidate.size())
                .append("份污水样品存在依托咪酯检出，详情见表3。");

        return stringBuilder.toString();
    }

    /**
     * 生成氯胺酮/可卡因/摇头丸的滥用现状描述
     *
     * @param yearMonth
     * @param sewageDataDtos
     * @param provinceDataList
     * @param sewagePlantNum
     * @param predicate        筛选的函数式条件
     * @param comparator       排序的函数式条件
     * @param drugName 毒品名称
     * @return
     */
    private String getDrugAbuseCurrentSituation(String yearMonth,
                                                    List<SewageDataDto> sewageDataDtos,
                                                    List<RegionalDrugConsumptionDTO> provinceDataList,
                                                    Set<String> sewagePlantNum,
                                                    Predicate<SewageDataDto> predicate,
                                                    Comparator<SewageDataDto> comparator,
                                                    Function<SewageDataDto, Double> function,
                                                    String drugName) {
        StringBuilder stringBuilder = new StringBuilder();
        // 筛选出氯胺酮浓度不为0的检材
        List<SewageDataDto> sewageDataDtosByDetected = sewageDataDtos.stream().filter(predicate).collect(Collectors.toList());
        if (CollUtil.isNotEmpty(sewageDataDtosByDetected)) {
            if (sewageDataDtosByDetected.size() == 1) {
                SewageDataDto sewageDataDto = sewageDataDtosByDetected.get(0);
                stringBuilder.append("本次监测中，在")
                        .append(sewageDataDto.getCityName())
                        .append(StrUtil.split(sewageDataDto.getSampleName(), "-").get(0))
                        .append("中有")
                        .append(keep1DecimalPlace.format(function.apply(sewageDataDto)))
                        .append("ng/L的"+drugName+"检出，其他地市均未检出，说明全省范围内无明显滥用，需长期关注，掌握变化趋势。");
            } else {
                // 列表不为空
                // 计算有多少个污水厂检出
                Set<String> detectedKetaminePlant = sewageDataDtosByDetected.stream().map(o -> StrUtil.split(o.getSampleName(), "-").get(0)).collect(Collectors.toSet());
                List<String> detectedCity = new ArrayList<>(sewageDataDtosByDetected.stream().map(SewageDataDto::getCityName).collect(Collectors.toSet()));
                List<SewageDataDto> sewageDataDtosByDetectedKetamineSorted = sewageDataDtosByDetected.stream().sorted(comparator).collect(Collectors.toList());
                int lastYear = Year.now().minusYears(1).getValue();
                stringBuilder
                        .append(yearMonth)
                        .append("在")
                        .append(keep1DecimalPlace.format(
                                detectedKetaminePlant.size() / (double) sewagePlantNum.size() * 100))
                        .append("%")
                        .append("的污水处理厂均有")
                        .append(drugName)
                        .append("检出，与")
                        .append(lastYear)
                        .append("相比，")
                        .append(yearMonth)
                        .append("份")
                        .append(drugName)
                        .append("滥用");
                RegionalDrugConsumptionDTO yearOnYear = provinceDataList.get(0);
                RegionalDrugConsumptionDTO dto = provinceDataList.get(provinceDataList.size() - 1);
                double yoyKetamineCon = Double.parseDouble(yearOnYear.getTcK());
                double ketamineCon = Double.parseDouble(dto.getTcK());
                if (yoyKetamineCon > ketamineCon) {
                    stringBuilder.append("逐渐下降，");
                } else if (yoyKetamineCon < ketamineCon) {
                    stringBuilder.append("明显增加，");
                } else {
                    stringBuilder.append("程度没有变化，");
                }

                int citySize = detectedCity.size();
                for (String city : detectedCity) {
                    int indexOf = detectedCity.indexOf(city);
                    if (indexOf == 0) {
                        stringBuilder.append(city);
                    } else if (indexOf == citySize - 1 && citySize >= 2) {
                        stringBuilder.append("和").append(city);
                    } else {
                        stringBuilder.append("、").append(city);
                    }
                }
                stringBuilder
                        .append(citySize)
                        .append("个地市（")
                        .append(detectedKetaminePlant.size())
                        .append("个污水处理厂）中均有检出，")
                        .append(drugName)
                        .append("浓度范围为")
                        .append(keep1DecimalPlace.format(function.apply(sewageDataDtosByDetectedKetamineSorted
                                .get(0)
                                        )
                                )
                        )
                        .append("~")
                        .append(keep1DecimalPlace.format(function.apply(sewageDataDtosByDetectedKetamineSorted
                                .get(sewageDataDtosByDetectedKetamineSorted.size() - 1)
                                        )
                                )
                        )
                        .append(" ng/L，后续检测中可持续关注。");
            }
        } else {
            stringBuilder.append("在本次监测中均未检出，说明不存在").append(drugName).append("的明显滥用。");
        }
        return stringBuilder.toString();
    }

    /**
     * 生成海洛因的滥用现状
     *
     * @param yearMonth
     * @param sewageDataDtos
     * @param regionalDrugConsumptionDTOMap
     * @param allProvince
     * @param sewagePlantNum
     * @return
     */
    private String getHeroinAbuseCurrentSituation(String yearMonth, List<SewageDataDto> sewageDataDtos, Map<String, RegionalDrugConsumptionDTO> regionalDrugConsumptionDTOMap, RegionalDrugConsumptionDTO allProvince, Set<String> sewagePlantNum) {
        StringBuilder stringBuilder = new StringBuilder();
        List<SewageDataDto> sewageDataDtosByMorphine = sewageDataDtos.stream().filter(o -> o.getMorphineConcentration() != 0).collect(Collectors.toList());
        Set<String> detectedMorphineNum = sewageDataDtosByMorphine.stream().map(o -> StrUtil.split(o.getSampleName(), "-").get(0)).collect(Collectors.toSet());
        double pccHeroinProvince = Double.parseDouble(allProvince.getPccHeroin());
        double tcHeroinProvince = Double.parseDouble(allProvince.getTcHeroin());
        if (pccHeroinProvince != 0 && tcHeroinProvince != 0) {
            stringBuilder
                    .append(yearMonth)
                    .append("在")
                    .append(keep1DecimalPlace.format(
                            detectedMorphineNum.size() / (double) sewagePlantNum.size() * 100))
                    .append("%的污水处理厂均有吗啡检出（海洛因的代谢物）。")
                    .append("陕西省海洛因的千人均消费量")
                    .append(allProvince.getPccHeroin())
                    .append("毫克/千人/天，核算海洛因的总消费量为")
                    .append(allProvince.getTcHeroin())
                    .append("克/天。特别指出，根据污水中的吗啡和可待因的浓度计算海洛因消费量时，粗略扣除了合法药物、医用吗啡以及磷酸可待因代谢吗啡的干扰，但未充分考虑干扰的区域性和季节性差异，因而存在一定误差，结果仅供参考。");
        } else {
            stringBuilder.append("在本次监测中均未检出，说明不存在海洛因的明显滥用。");
        }

        return stringBuilder.toString();
    }

    /**
     * 生成冰毒的滥用现状描述
     * @param yearMonth
     * @param sewageDataDtos
     * @param regionalDrugConsumptionDTOMap
     * @param allProvince
     * @return
     */
    private String getMethamphetamineAbuseCurrentSituation(String yearMonth, List<SewageDataDto> sewageDataDtos, Map<String, RegionalDrugConsumptionDTO> regionalDrugConsumptionDTOMap, RegionalDrugConsumptionDTO allProvince, Set<String> sewagePlantNum) {
        StringBuilder stringBuilder = new StringBuilder();
        // 筛选检出冰毒的污水厂
        List<SewageDataDto> sewageDataDtosByDetectedMa = sewageDataDtos.stream().filter(o -> o.getPccMa() != 0).collect(Collectors.toList());
        if (CollUtil.isNotEmpty(sewageDataDtosByDetectedMa)) {
            // 计算出检测冰毒的污水厂比例
            Set<String> detectedMaSewagePlantNum = sewageDataDtosByDetectedMa.stream().map(o -> StrUtil.split(o.getSampleName(), "-").get(0)).collect(Collectors.toSet());
            RegionalDrugConsumptionDTO dto = regionalDrugConsumptionDTOMap.get("西安市");
            double maPercent = Double.parseDouble(dto.getTcMa()) / Double.parseDouble(allProvince.getTcMa());
            stringBuilder
                    .append(yearMonth)
                    .append("份在")
                    .append(keep1DecimalPlace.format(
                            detectedMaSewagePlantNum.size() / (double) sewagePlantNum.size() * 100)
                    )
                    .append("%的污水厂有冰毒及其代谢物检出。")
                    .append("全省冰毒的千人均消费量为")
                    .append(allProvince.getPccMa())
                    .append("毫克/千人/天，核算陕西省冰毒的总消费量为")
                    .append(allProvince.getTcMa())
                    .append("克/天。").append("西安冰毒总消费量为")
                    .append(dto.getTcMa())
                    .append("克/天，占全省总消费量的")
                    .append(keep1DecimalPlace.format(maPercent))
                    .append("%");
        } else {
            stringBuilder.append("在本次监测中均未检出，说明不存在冰毒的明显滥用。");
        }

        return stringBuilder.toString();
    }

    /**
     * 全省冰毒千人均消费量 描述
     * @param allTaskProvinceDrugConsumptionList
     * @param taskStartDateChinaFormatList
     * @return
     */
    private String analysisMethamphetamineProvinceAbuseTendDescribe(List<RegionalDrugConsumptionDTO> allTaskProvinceDrugConsumptionList, List<String> taskStartDateChinaFormatList) {
        StringBuilder stringBuilder = new StringBuilder();
        int size = allTaskProvinceDrugConsumptionList.size();
        if (size < 2) {
            return "";
        }
        RegionalDrugConsumptionDTO nowTaskData = allTaskProvinceDrugConsumptionList.get(size - 1);
        double nowTaskMaData = Double.parseDouble(nowTaskData.getPccMa());
        boolean yearDecline = false; // 标记同比是否下降， true 是在下降， false 不是
        boolean monthDecline = false; // 标记环比是否下降， true 是在下降， false 不是
        for (int i = 0; i < size - 1; i++) {
            RegionalDrugConsumptionDTO dto = allTaskProvinceDrugConsumptionList.get(i);
            double oldTaskMaData = Double.parseDouble(dto.getPccMa());
            double abs = Math.abs((nowTaskMaData - oldTaskMaData) / oldTaskMaData * 100);
            switch (i) {
                case 0: {
                    stringBuilder
                            .append("全省冰毒千人均消费量")
                            .append(taskStartDateChinaFormatList.get(size - 1))
                            .append("（")
                            .append(nowTaskMaData)
                            .append("毫克/千人/天）")
                            .append("与")
                            .append(taskStartDateChinaFormatList.get(i))
                            .append("（")
                            .append(oldTaskMaData)
                            .append("毫克/千人/天）相比，");
                    if (size == 2) {
                        stringBuilder.append("环比");
                    } else {
                        stringBuilder.append("同比");
                    }
                    if (oldTaskMaData > nowTaskMaData) {
                        yearDecline = true;
                        stringBuilder.append("下降");
                    } else {
                        yearDecline = false;
                        stringBuilder.append("增加");
                    }
                    stringBuilder.append(keep1DecimalPlace.format(abs)).append("%");
                    if (size == 2) {
                        stringBuilder.append("全省冰毒滥用趋势");
                        if (yearDecline) {
                            stringBuilder.append("逐渐下降");
                        } else {
                            stringBuilder.append("明显增加");
                        }
                        stringBuilder.append("。");
                    } else {
                        stringBuilder.append("，");
                    }
                    break;
                }
                default: {
                    if (i == size - 2) {
                        stringBuilder
                                .append("与")
                                .append(taskStartDateChinaFormatList.get(i))
                                .append("（")
                                .append(oldTaskMaData)
                                .append("毫克/千人/天）相比，")
                                .append("环比");
                        if (oldTaskMaData > nowTaskMaData) {
                            monthDecline = true;
                            stringBuilder.append("下降");
                        } else {
                            monthDecline = false;
                            stringBuilder.append("增加");
                        }
                        stringBuilder
                                .append(keep1DecimalPlace.format(
                                                abs
                                        )
                                )
                                .append("%，");
                        if(yearDecline && monthDecline || monthDecline) {
                            stringBuilder.append("全省冰毒滥用趋势逐渐下降。");
                        } else if (yearDecline) {
                            stringBuilder.append("全省冰毒滥用趋势明显增加。");
                        }
                    }
                }
            }
        }
        return stringBuilder.toString();
    }

    /**
     * 生成全省海洛因的消费量变化趋势描述
     *
     * @param allTaskProvinceDrugConsumptionList
     * @param taskStartDateChinaFormatList
     * @return
     */
    private String analysisHeroinProvinceAbuseTendDescribe(List<RegionalDrugConsumptionDTO> allTaskProvinceDrugConsumptionList, List<String> taskStartDateChinaFormatList) {

        StringBuilder stringBuilder = new StringBuilder();
        int size = allTaskProvinceDrugConsumptionList.size();
        if (size < 2) {
            return "";
        }
        RegionalDrugConsumptionDTO nowTaskData = allTaskProvinceDrugConsumptionList.get(size - 1);
        double nowTaskHeroinData = Double.parseDouble(nowTaskData.getPccHeroin());
        boolean yearDecline = false; // 标记同比是否下降， true 是在下降， false 不是
        boolean monthDecline = false; // 标记环比是否下降， true 是在下降， false 不是
        for (int i = 0; i < size - 1; i++) {
            RegionalDrugConsumptionDTO dto = allTaskProvinceDrugConsumptionList.get(i);
            double oldTaskHeroinData = Double.parseDouble(dto.getPccHeroin());
            double abs = Math.abs((nowTaskHeroinData - oldTaskHeroinData) / oldTaskHeroinData * 100);
            switch (i) {
                case 0: {
                    stringBuilder
                            .append(taskStartDateChinaFormatList.get(size - 1))
                            .append("（")
                            .append(nowTaskHeroinData)
                            .append("毫克/千人/天）")
                            .append("与")
                            .append(taskStartDateChinaFormatList.get(i))
                            .append("（")
                            .append(oldTaskHeroinData)
                            .append("毫克/千人/天）相比，");
                    if (size == 2) {
                        stringBuilder.append("环比");
                    } else {
                        stringBuilder.append("同比");
                    }
                    if (oldTaskHeroinData > nowTaskHeroinData) {
                        yearDecline = true;
                        stringBuilder.append("下降");
                    } else {
                        yearDecline = false;
                        stringBuilder.append("增加");
                    }
                    stringBuilder.append(keep1DecimalPlace.format(abs)).append("%");
                    if (size == 2) {
                        stringBuilder.append("海洛因滥用趋势");
                        if (yearDecline) {
                            stringBuilder.append("逐渐下降");
                        } else {
                            stringBuilder.append("明显增加");
                        }
                        stringBuilder.append("。");
                    } else {
                        stringBuilder.append("，");
                    }
                    break;
                }
                default: {
                    if (i == size - 2) {
                        stringBuilder
                                .append("与")
                                .append(taskStartDateChinaFormatList.get(i))
                                .append("（")
                                .append(oldTaskHeroinData)
                                .append("毫克/千人/天）相比，")
                                .append("环比");
                        if (oldTaskHeroinData > nowTaskHeroinData) {
                            monthDecline = true;
                            stringBuilder.append("下降");
                        } else {
                            monthDecline = false;
                            stringBuilder.append("增加");
                        }
                        stringBuilder
                                .append(keep1DecimalPlace.format(
                                        abs
                                        )
                                )
                                .append("%，");
                        if(yearDecline && monthDecline || monthDecline) {
                            stringBuilder.append("海洛因滥用趋势逐渐下降。");
                        } else if (yearDecline) {
                            stringBuilder.append("海洛因滥用趋势明显增加。");
                        }
                    }
                }
            }
        }
        return stringBuilder.toString();
    }

    /**
     * 各行政区毒品滥用水平排序（即综合千人均消费量排序）
     * @param pccRegionalDrugConsumptionDTOS1
     * @return
     */
    private String getCombinedThousandPerConsumptionSortedDesc(List<RegionalDrugConsumptionDTO> pccRegionalDrugConsumptionDTOS1) {
        StringBuilder stringBuilder = new StringBuilder();
        for (RegionalDrugConsumptionDTO regionalDrugConsumptionDTO : pccRegionalDrugConsumptionDTOS1) {
            if (pccRegionalDrugConsumptionDTOS1.indexOf(regionalDrugConsumptionDTO) == 0) {
                stringBuilder.append(regionalDrugConsumptionDTO.getRegional());
            } else {
                if (regionalDrugConsumptionDTO.getRegional().contains("全省平均")) {
                    stringBuilder.append("≧");
                } else {
                    stringBuilder.append(">");
                }
                stringBuilder.append(regionalDrugConsumptionDTO.getRegional());
            }
        }
        stringBuilder.append("。");
        return stringBuilder.toString();
    }

    /**
     * 全省各行政区 综合千人均消费量 排名描述
     *
     * @param pccRegionalDrugConsumptionDTOS
     * @param allProvince 当前季度全省的消费量统计
     * @return
     */
    private String getCombinedThousandPerConsumptionCompareDescribe(List<RegionalDrugConsumptionDTO> pccRegionalDrugConsumptionDTOS, RegionalDrugConsumptionDTO allProvince) {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder
                .append("全省各行政区千人均变化趋势如图2所示，全省平均千人均消费量为")
                .append(allProvince.getPccTotal()).append("毫克/千人/天，");

        int size = pccRegionalDrugConsumptionDTOS.size();
        for (int i = 0; i < size && i < 5; i++) { // 只用循环5次
            RegionalDrugConsumptionDTO dto = pccRegionalDrugConsumptionDTOS.get(i);
            double pccTotal = Double.parseDouble(dto.getPccTotal());
            switch (i) {
                case 0: {
                    stringBuilder
                            .append(dto.getRegional())
                            .append("的综合千人均消费量最高，为")
                            .append(dto.getPccTotal())
                            .append("毫克/千人/天，是全省平均水平的")
                            .append(
                                    keep1DecimalPlace.format(
                                            pccTotal / Double.parseDouble(allProvince.getPccTotal())))
                            .append("倍；");
                    break;
                }
                case 1: {
                    stringBuilder.append("其次是").append(dto.getRegional());
                    if (size >= 3) {
                        stringBuilder.append("和").append(pccRegionalDrugConsumptionDTOS.get(i + 1).getRegional());
                    }
                    stringBuilder.append("，综合千人均消费量为").append(dto.getPccTotal()).append("毫克/千人/天");
                    if (size >= 3) {
                        stringBuilder.append("和").append(pccRegionalDrugConsumptionDTOS.get(i + 1).getPccTotal()).append("毫克/千人/天");
                    }
                    stringBuilder.append("；");
                    break;
                }
                case 3: {
                    stringBuilder
                            .append("位居第四的是")
                            .append(dto.getRegional())
                            .append("，综合千人均消费量分别为")
                            .append(dto.getPccTotal())
                            .append("毫克/千人/天；");
                    break;
                }
                case 4: {
                    stringBuilder.append("位居第五");
                    if (size > 5) {
                        stringBuilder.append("至").append(NumberTransferHanZi.getHanZiNameByNumber(size)).append("位的分别是");
                    } else {
                        stringBuilder.append("位的是");
                    }
                    stringBuilder.append(dto.getRegional());
                    for (int j = 5; j < size; j++) {
                        RegionalDrugConsumptionDTO consumptionDTO = pccRegionalDrugConsumptionDTOS.get(j);
                        if (pccRegionalDrugConsumptionDTOS.indexOf(consumptionDTO) == (size - 1)) {
                            stringBuilder.append("和").append(consumptionDTO.getRegional());
                        } else {
                            stringBuilder.append("、").append(consumptionDTO.getRegional());
                        }
                    }
                    stringBuilder.append("，综合千人均消费量分别为").append(dto.getPccTotal()).append("毫克/千人/天");
                    for (int j = 5; j < size; j++) {
                        RegionalDrugConsumptionDTO consumptionDTO = pccRegionalDrugConsumptionDTOS.get(j);
                        if (pccRegionalDrugConsumptionDTOS.indexOf(consumptionDTO) == (size - 1)) {
                            stringBuilder.append("和").append(consumptionDTO.getPccTotal()).append("毫克/千人/天");
                        } else {
                            stringBuilder.append("、").append(consumptionDTO.getPccTotal()).append("毫克/千人/天");
                        }
                    }
                    stringBuilder.append("。");
                }
            }
        }

        return stringBuilder.toString();
    }

    /***
     * 各行政区毒品滥用总量排序（即综合总消费量排序）
     * @param regionalDrugConsumptionDTOS
     * @return
     */
    private String getCombinedTotalConsumptionSortedDesc(List<RegionalDrugConsumptionDTO> regionalDrugConsumptionDTOS) {
        StringBuilder stringBuilder = new StringBuilder();
        for (RegionalDrugConsumptionDTO regionalDrugConsumptionDTO : regionalDrugConsumptionDTOS) {
            stringBuilder.append(regionalDrugConsumptionDTO.getRegional());
            if (regionalDrugConsumptionDTOS.indexOf(regionalDrugConsumptionDTO) == (regionalDrugConsumptionDTOS.size() - 1)) {
                stringBuilder.append("。");
            } else {
                stringBuilder.append(">");
            }
        }
        return stringBuilder.toString();
    }

    /**
     * 各行政区中的综合总消费量比较和描述
     * @param regionalDrugConsumptionDTOS
     * @return
     */
    private String compareEachCityCombinedTotalConsumptionDescribe(List<RegionalDrugConsumptionDTO> regionalDrugConsumptionDTOS) {

        StringBuilder stringBuilder = new StringBuilder();
        double sum = regionalDrugConsumptionDTOS.stream().mapToDouble(o -> Double.parseDouble(o.getTcTotal())).sum();
        int size = regionalDrugConsumptionDTOS.size();
        for (int i = 0; i < size && i < 6; i++) {
            RegionalDrugConsumptionDTO dto = regionalDrugConsumptionDTOS.get(i);
            double v = Double.parseDouble(dto.getTcTotal()); // 当前地区的综合消费量
            switch (i) {
                case 0: {
                    stringBuilder
                            .append("按行政区，")
                            .append(dto.getRegional())
                            .append("的综合总消费量最高为")
                            .append(dto.getTcTotal())
                            .append("克/天，占全省监测区域消费总量的")
                            .append(keep1DecimalPlace.format(v / sum * 100))
                            .append("%；");
                    break;
                }
                case 1: {
                    stringBuilder
                            .append("其次是")
                            .append(dto.getRegional())
                            .append("，综合总消费量为")
                            .append(dto.getTcTotal())
                            .append("克/天，")
                            .append("分别占全省监测区域消费总量的")
                            .append(keep1DecimalPlace.format(v / sum * 100))
                            .append("%；");
                    break;
                }
                case 2: {
                    stringBuilder.append("位居第三");
                    if (size >= 4) {
                        // 判断当前季度任务下送检的样品地市是否超过
                        stringBuilder.append("、第四");
                    }
                    if (size >= 5) {
                        stringBuilder.append("、第五");
                    }
                    stringBuilder.append("的是").append(dto.getRegional());
                    if (size >= 4) {
                        stringBuilder.append("、").append(regionalDrugConsumptionDTOS.get(i + 1).getRegional());
                    }
                    if (size >= 5) {
                        stringBuilder.append("、").append(regionalDrugConsumptionDTOS.get(i + 2).getRegional());
                    }

                    stringBuilder.append("，综合总消费量分别为").append(dto.getTcTotal()).append("克/天");
                    if (size >= 4) {
                        stringBuilder.append("、").append(regionalDrugConsumptionDTOS.get(i + 1).getTcTotal()).append("克/天");
                    }
                    if (size >= 5) {
                        stringBuilder.append("、").append(regionalDrugConsumptionDTOS.get(i + 2).getTcTotal()).append("克/天");
                    }

                    stringBuilder
                            .append("，占全省监测区域消费总量的")
                            .append(keep1DecimalPlace.format(v / sum * 100))
                            .append("%");
                    if (size >= 4) {
                        stringBuilder.append("、")
                                .append(keep1DecimalPlace.format(
                                        Double.parseDouble(regionalDrugConsumptionDTOS.get(i + 1).getTcTotal()) / sum * 100))
                                .append("%");
                    }
                    if (size >= 5) {
                        stringBuilder.append("、")
                                .append(keep1DecimalPlace.format(
                                        Double.parseDouble(regionalDrugConsumptionDTOS.get(i + 2).getTcTotal()) / sum * 100))
                                .append("%");
                    }
                    stringBuilder.append("；");
                    break;
                }
                case 5: {
                    stringBuilder
                            .append("其余")
                            .append(size - i)
                            .append("个行政区综合总消费量均低于")
                            .append(Math.ceil(v))
                            .append("克/天，")
                            .append("占比均低于")
                            .append(Math.ceil(v / sum * 100))
                            .append("%");
                    break;
                }
            }
        }
        stringBuilder.append("。");
        return stringBuilder.toString();
    }

    /**
     * 根据flag决定时根据综合总消费量进行排序还是综合千人均消费量排序
     * @param regionalDrugConsumptionDTOList
     * @param flag true -> 综合总消费量  false 综合千人均消费量
     * @param remove 是否移除最后一个全省的消费量
     * @return
     */
    private static List<RegionalDrugConsumptionDTO> getRegionalDrugConsumptionDTOSSortedDesc(List<RegionalDrugConsumptionDTO> regionalDrugConsumptionDTOList, boolean flag, boolean remove) {
        List<RegionalDrugConsumptionDTO> regionalDrugConsumptionDTOS = new ArrayList<>(regionalDrugConsumptionDTOList);
        if (remove) {
            // 对综合总消费量排序时要排除全省的
            regionalDrugConsumptionDTOS.remove(regionalDrugConsumptionDTOS.size() - 1);
        }
        Collections.sort(regionalDrugConsumptionDTOS, new Comparator<RegionalDrugConsumptionDTO>() {
            @Override
            public int compare(RegionalDrugConsumptionDTO o1, RegionalDrugConsumptionDTO o2) {
                Double o1V = Double.parseDouble(flag ? o1.getTcTotal() : o1.getPccTotal());
                Double o2V = Double.parseDouble(flag ? o2.getTcTotal() : o2.getPccTotal());
                return o2V.compareTo(o1V);
            }
        });
        return regionalDrugConsumptionDTOS;
    }

    /**
     * 描述 冰毒（甲基苯丙胺） 在 陕西省污水处理厂冰毒浓度排名
     * @param sewageDataDtos
     * @return
     */
    private String getDescribeMethamphetamineConcentrationRank(List<SewageDataDto> sewageDataDtos) {

        String resultStr = "";
        for (int i = 0; i < sewageDataDtos.size() && i < 3; i++) {
            SewageDataDto sewageDataDto = sewageDataDtos.get(i);
            String sewagePlantName = StrUtil.split(sewageDataDto.getSampleName(), "-").get(0);
            Double methamphetamineConcentration = sewageDataDto.getMethamphetamineConcentration();
            switch (i) {
                case 0: {
                    resultStr = resultStr + "陕西省的甲基苯丙胺浓度范围为0（表示未检出，下同）~"+
                            keep1DecimalPlace.format(methamphetamineConcentration) +"ng/L。" + "各污水厂检测数据中，浓度最高的" +
                            sewagePlantName +
                            "，" + "甲基苯丙胺含量为"+
                            keep1DecimalPlace.format(methamphetamineConcentration) +" ng/L；";
                    break;
                }
                case 1: {
                    resultStr = resultStr +
                            "排名第二的是" +
                            sewagePlantName + "，" +
                            "甲基苯丙胺含量为"+
                            keep1DecimalPlace.format(methamphetamineConcentration) +" ng/L；";
                    break;
                }
                case 2: {
                    resultStr = resultStr +
                            "排名第三的是" +
                            sewagePlantName + "，" +
                            "甲基苯丙胺含量为"+
                            keep1DecimalPlace.format(methamphetamineConcentration) +" ng/L。";
                    break;
                }
            }

        }
        return resultStr;
    }

    /**
     * 描述 吗啡 在 陕西省污水处理厂冰毒浓度排名
     * @param sewageDataDtos
     * @return
     */
    private String getDescribeMorphineConcentrationRank(List<SewageDataDto> sewageDataDtos) {
        // 筛选大于100ng/L的数量
        long count = sewageDataDtos.stream().filter(o -> o.getMorphineConcentration() > 100).count();
        String resultStr = "";
        for (int i = 0; i < sewageDataDtos.size() && i < 3; i++) {
            SewageDataDto sewageDataDto = sewageDataDtos.get(i);
            String sewagePlantName = StrUtil.split(sewageDataDto.getSampleName(), "-").get(0);
            Double morphineConcentration = sewageDataDto.getMorphineConcentration();
            switch (i) {
                case 0: {
                    resultStr = resultStr +
                            "吗啡日浓度范围为0（表示未检出，下同）~"+
                            keep1DecimalPlace.format(morphineConcentration) +"ng/L。" + "其中全省" +
                            (count == 0 ? "没有" : ("有" + count + "个")) +
                            "污水处理厂的吗啡浓度高于100 ng/L。" +
                            "表5列出了吗啡浓度排名前15的污水处理厂。" + "浓度最高的是" +
                            sewagePlantName +
                            "，" + "吗啡浓度为"+
                            keep1DecimalPlace.format(morphineConcentration) +" ng/L；";
                    break;
                }
                case 1: {
                    resultStr = resultStr +
                            "排名第二的是" +
                            sewagePlantName + "，" +
                            "吗啡浓度为"+
                            keep1DecimalPlace.format(morphineConcentration) +" ng/L；";
                    break;
                }
                case 2: {
                    resultStr = resultStr +
                            "排名第三的是" +
                            sewagePlantName +
                            "吗啡浓度为"+
                            keep1DecimalPlace.format(morphineConcentration) +" ng/L。";
                    break;
                }
            }
        }
        return resultStr;
    }

    /**
     * 组装陕西省毒品滥用情况分析 - 各污水厂毒品浓度排名表
     * @param sortSewageDataDtos
     * @param mapper
     * @param cols 表格的行数
     * @return
     */
    private List<List<Object>> fetchCityDrugAbuseConditionAnalysisData(List<List<SewageDataDto>> sortSewageDataDtos, Function<SewageDataDto, Double> mapper, Integer cols) {
        List<List<Object>> list = new ArrayList<>();
//        List<List<SewageDataDto>> sortSewageDataDtos = getSortSewageDataDtos(sewageDataDtoLists, sort);
        // 11 是表格的列数,
        for (int i = 0; i < cols; i++) {
            List<Object> subList = new ArrayList<>();
            // 添加数据
            subList.add(i + 1);
            list.add(subList);
        }
        for (int i = 0; i < sortSewageDataDtos.size(); i++) {
            List<SewageDataDto> sewageDataDtos = sortSewageDataDtos.get(i);
            List<Double> concentrationList = sewageDataDtos.stream().map(mapper::apply).collect(Collectors.toList());
            for (int j = 0; j < cols; j++) {
                List<Object> subList = null;

                subList = list.get(j);
                if (j < sewageDataDtos.size()) {
                    SewageDataDto sewageDataDto = sewageDataDtos.get(j);
                    subList.add(StrUtil.split(sewageDataDto.getSampleName(), "-").get(0));
                    subList.add(keep1DecimalPlace.format(concentrationList.get(j)));
                } else {
                    subList.add("");
                    subList.add("");
                }
            }
        }

        return list;
    }

    /**
     * 根据检材检测出的毒品浓度进行排序
     * @param sewageDataDtoLists
     * @param sort
     * @return
     */
    private static List<List<SewageDataDto>> getSortSewageDataDtos(List<List<SewageDataDto>> sewageDataDtoLists, Comparator<SewageDataDto> sort) {
        List<List<SewageDataDto>> sortSewageDataDtos = new ArrayList<>();
        // 进行排序-升序
        for (List<SewageDataDto> sewageDataDtoList : sewageDataDtoLists) {
            List<SewageDataDto> sewageDataDtos = sewageDataDtoList.stream().sorted(sort).collect(Collectors.toList());
            sortSewageDataDtos.add(sewageDataDtos);
        }
        return sortSewageDataDtos;
    }

    /**
     * 获取一年内的任务检材数据
     * @param taskInfoList
     * @param dto
     * @return
     */
    private List<List<SewageDataDto>> getSewageTaskDataForAYear(List<TaskInfo> taskInfoList, ExportSewageAnalystReportsDTO dto) {
        List<List<SewageDataDto>> resultList = new ArrayList<>();
        for (int i = 0; i < taskInfoList.size() - 1; i++) {
            TaskInfo taskInfo = taskInfoList.get(i);
            // feign 接口调用获取 污水检材
            List<SewageJobIdentificationMaterialVO> data = getSewageJobIdentificationMaterialVOS(dto, taskInfo);
            // 获取每个样品的数据
            List<SewageDataDto> sewageDataDtos = taskInfoService.processSewageDataDtos(dto.getDailySmokingPerCapita(), data);
            resultList.add(sewageDataDtos);
        }
        return resultList;
    }

    /**
     * 组装 表3 陕西省各污水厂服务区及各行政区毒品千人均消费量表 的数据
     *
     * @param allMergeMegionalDrugConsumptionDTOList 之前季度的消费量数据
     * @param regionalDrugConsumptionDTOList         当前季度的消费数据
     * @param taskInfoList                           任务列表， 目的是取同期的月份， 上期的月份， 本期的月份
     * @param cityNameList
     * @return
     */
    private List<List<Object>> fetchCityDrugConsumptionGrowthRatesData(List<List<RegionalDrugConsumptionDTO>> allMergeMegionalDrugConsumptionDTOList, List<RegionalDrugConsumptionDTO> regionalDrugConsumptionDTOList, List<TaskInfo> taskInfoList, List<String> cityNameList) {
        // 取上期的数据， 取0的原因是在计算时任务列表的顺序是升序的， 所有第一个元素就是上期的数据， 同理最后一个则是同期的数据
        List<RegionalDrugConsumptionDTO> regionalDrugConsumptionDTOList_LastYear = allMergeMegionalDrugConsumptionDTOList.get(0);
        List<RegionalDrugConsumptionDTO> regionalDrugConsumptionDTOList_Last = allMergeMegionalDrugConsumptionDTOList.get(allMergeMegionalDrugConsumptionDTOList.size() - 1);

        // 上期数据
        List<Object> lastList = compareDiffQuarterCityDrugConsumptionGrowthRatesData(
                regionalDrugConsumptionDTOList_Last,
                regionalDrugConsumptionDTOList,
                taskInfoList.get(taskInfoList.size() - 2),
                cityNameList, true);
        // 同期数据
        List<Object> lastYearList = compareDiffQuarterCityDrugConsumptionGrowthRatesData(
                regionalDrugConsumptionDTOList_LastYear,
                regionalDrugConsumptionDTOList,
                taskInfoList.get(0),
                cityNameList,
                false);

        return new ArrayList<>(Arrays.asList(lastYearList, lastList));
    }

    /**
     * 比较两个不同季度的消费量数据
     * @param sourceList 本期的数据
     * @param compareList 上期或者同期数据
     * @param taskInfo 比较的任务信息
     * @param cityNameList
     * @param isMonthToMonth  是否是环比
     * @return
     */
    private List<Object> compareDiffQuarterCityDrugConsumptionGrowthRatesData(List<RegionalDrugConsumptionDTO> compareList,
                                                                              List<RegionalDrugConsumptionDTO> sourceList,
                                                                              TaskInfo taskInfo,
                                                                              List<String> cityNameList,
                                                                              boolean isMonthToMonth) {
        // 先把两个列表转出map, 城市名是key
        Map<String, RegionalDrugConsumptionDTO> sourceMap = sourceList.stream().collect(Collectors.toMap(RegionalDrugConsumptionDTO::getRegional, Function.identity()));
        Map<String, RegionalDrugConsumptionDTO> compareMap = compareList.stream().collect(Collectors.toMap(RegionalDrugConsumptionDTO::getRegional, Function.identity()));
        // 存数据的列表
        List<Object> dataList = new ArrayList<>();
        dataList.add("与" + taskInfo.getTaskStartDate().format(chineseYearSingleMonthFormatter) + (isMonthToMonth ? "环比" : "同比"));// 比较的任务月份

        for (String cityName : cityNameList) {
            RegionalDrugConsumptionDTO source = sourceMap.get(cityName);
            RegionalDrugConsumptionDTO compare = compareMap.get(cityName);
            if (source != null && compare != null) {
                double compareV = Double.parseDouble(compare.getPccTotal());
                double sourceV = Double.parseDouble(source.getPccTotal());
                double rate = (sourceV - compareV) / compareV * 100;
                dataList.add(keep1DecimalPlace.format(rate) + "%");
            } else {
                // 默认添加空字符串
                dataList.add("");
            }
        }

        return dataList;
    }

    /**
     * 根据得到的各个季度任务的全省毒品消费量值 去 描述 陕西省毒品千人均消费量变化趋势
     * 在这里说明一下本期，同期和上期之间的关系
     * 1 本期 指的是当前季度
     * 2 上期 是 当前季度的上一个季度
     * 3 同期指的是去年的同一季度
     * @param allTaskProvinceDrugConsumptionList
     * @param taskInfoList 已经根据任务开始时间进行升序排序的列表
     * @return
     */
    private String getDrugConsumePerThousandTrendDescribe(List<RegionalDrugConsumptionDTO> allTaskProvinceDrugConsumptionList, List<TaskInfo> taskInfoList) {
        String resultStr = "";
        boolean turnPoint = false; // 上升是true 下降是false
        int size = allTaskProvinceDrugConsumptionList.size();
        if (size < 4) {
            return resultStr;
        }
        for (int i = 0; i < size; i++) {
            RegionalDrugConsumptionDTO dto = allTaskProvinceDrugConsumptionList.get(i);
            switch (i) {
                case 0: {
                    if (Double.parseDouble(dto.getPccTotal()) > Double.parseDouble(allTaskProvinceDrugConsumptionList.get(i + 1).getPccTotal())) {
                        resultStr = resultStr + "全省千人均消费量自" +
                                taskInfoList.get(0).getTaskStartDate().format(chineseYearSingleMonthFormatter) +
                                "以来呈下降趋势，";
                        turnPoint = false;
                    } else {
                        resultStr = resultStr + "全省千人均消费量自" +
                                taskInfoList.get(0).getTaskStartDate().format(chineseYearSingleMonthFormatter) +
                                "以来呈上升趋势，";
                        turnPoint = true;
                    }
                    break;
                }
                case 1: {
                    if (turnPoint && (Double.parseDouble(dto.getPccTotal()) > Double.parseDouble(allTaskProvinceDrugConsumptionList.get(i + 1).getPccTotal())) ) {
                        // 如果之前是上升，现在 是下降则出现转折点
                        resultStr = resultStr + "于" + taskInfoList.get(1).getTaskStartDate().format(chineseYearSingleMonthFormatter) + "开始下降，";
                        turnPoint = false;
                    } else if (!turnPoint && (Double.parseDouble(dto.getPccTotal()) < Double.parseDouble(allTaskProvinceDrugConsumptionList.get(i + 1).getPccTotal()))) {
                        // 如果之前是下降，现在 是上升则出现转折点
                        resultStr = resultStr + "于" + taskInfoList.get(1).getTaskStartDate().format(chineseYearSingleMonthFormatter) + "开始上升，";
                        turnPoint = true;
                    }
                    break;
                }
                case 2: {
                    if (turnPoint && (Double.parseDouble(dto.getPccTotal()) > Double.parseDouble(allTaskProvinceDrugConsumptionList.get(i + 1).getPccTotal())) ) {
                        // 如果之前是上升，现在 是下降则出现转折点
                        resultStr = resultStr + taskInfoList.get(2).getTaskStartDate().format(chineseYearSingleMonthFormatter) + "下降，";
                        turnPoint = false;
                    } else if (!turnPoint && (Double.parseDouble(dto.getPccTotal()) < Double.parseDouble(allTaskProvinceDrugConsumptionList.get(i + 1).getPccTotal()))) {
                        // 如果之前是下降，现在 是上升则出现转折点
                        resultStr = resultStr + taskInfoList.get(2).getTaskStartDate().format(chineseYearSingleMonthFormatter) + "上升，";
                        turnPoint = true;
                    }
                    break;
                }
                case 3: {
                    if (turnPoint && (Double.parseDouble(dto.getPccTotal()) > Double.parseDouble(allTaskProvinceDrugConsumptionList.get(i + 1).getPccTotal())) ) {
                        // 如果之前是上升，现在 是下降则出现转折点
                        resultStr = resultStr + "至" + taskInfoList.get(4).getTaskStartDate().format(chineseYearSingleMonthFormatter) + "开始下降，";
                        turnPoint = false;
                    } else if (!turnPoint && (Double.parseDouble(dto.getPccTotal()) < Double.parseDouble(allTaskProvinceDrugConsumptionList.get(i + 1).getPccTotal()))) {
                        // 如果之前是下降，现在 是上升则出现转折点
                        resultStr = resultStr + "至" + taskInfoList.get(4).getTaskStartDate().format(chineseYearSingleMonthFormatter) + "开始上升，";
                        turnPoint = true;
                    }
                    break;
                }
            }
        }
        if (size > 3) {

            RegionalDrugConsumptionDTO dto1 = allTaskProvinceDrugConsumptionList.get(0);
            RegionalDrugConsumptionDTO dto2 = allTaskProvinceDrugConsumptionList.get(size -2);
            RegionalDrugConsumptionDTO dto3 = allTaskProvinceDrugConsumptionList.get(size -1);
            resultStr = resultStr + taskInfoList.get(4).getTaskStartDate().format(chineseYearSingleMonthFormatter)
                    + "与"
                    + taskInfoList.get(0).getTaskStartDate().format(chineseYearSingleMonthFormatter) + "相比，";
            // 同比 (本期-同期) / 同期 * 100%
            double v1 = Double.parseDouble(dto1.getPccTotal()); // 同期数据
            double v2 = Double.parseDouble(dto2.getPccTotal()); // 上期期数据
            double v3 = Double.parseDouble(dto3.getPccTotal()); // 本期期期数据
            double yearOnYear = (v3 - v1) / v1 * 100; // 同比
            double monthOnMonth = (v3 - v2) / v2 * 100; // 环比， (本期-上期) / 上期 * 100%

            if (v3 < v1) {
                // 小于0 证明下降了
                resultStr = resultStr + "同比下降了" + keep1DecimalPlace.format(Math.abs(yearOnYear)) + "%，";
            } else {
                resultStr = resultStr + "同比上升了" + keep1DecimalPlace.format(Math.abs(yearOnYear)) + "%，";
            }

            // 环比
            resultStr = resultStr + "与" + taskInfoList.get(3).getTaskStartDate().format(chineseYearSingleMonthFormatter) + "相比，";
            if (v3 < v2) {
                // 小于0 证明下降了
                resultStr = resultStr + "环比下降了" + keep1DecimalPlace.format(Math.abs(monthOnMonth)) + "%。";
            } else {
                resultStr = resultStr + "环比上升了" + keep1DecimalPlace.format(Math.abs(monthOnMonth)) + "%。";
            }
        }
        return resultStr;
    }

    /**
     * 获取 前面四个季度的毒品消费量数据，也不定是4个
     * @param sewageDataDtoLists
     * @param taskIdByOrderStartTimeLimit4
     * @return
     */
    private List<List<RegionalDrugConsumptionDTO>> getAllMergeBeforeMegionalDrugConsumptionDTOList(List<List<SewageDataDto>> sewageDataDtoLists, List<TaskInfo> taskIdByOrderStartTimeLimit4) {
        List<List<RegionalDrugConsumptionDTO>> allMergeMegionalDrugConsumptionDTOList = new ArrayList<>();
        // 因为目前只获取之前4个任务的信息，所以列表的长度为4
        if (CollUtil.isNotEmpty(taskIdByOrderStartTimeLimit4)) {
            for (int i = 0; i < taskIdByOrderStartTimeLimit4.size() - 1; i++) { // 这里之所以减一的原因是 参数任务列表中最后一个这里不用求
                List<RegionalDrugConsumptionDTO> beforeDrugConsumptionDTOList = fetchRegionalDrugConsumption(getSewageDataGroupByCityNameMap(sewageDataDtoLists.get(i)));
                allMergeMegionalDrugConsumptionDTOList.add(beforeDrugConsumptionDTOList);
            }
        }
        return allMergeMegionalDrugConsumptionDTOList;
    }

    /**
     * 获取 陕西省毒品千人均消费量变化趋势 执行图的x轴名称
     *
     * @param taskInfoList
     * @return
     */
    private List<String> getTaskStartDateList(List<TaskInfo> taskInfoList, DateTimeFormatter dateTimeFormatter) {
        List<String> xAxisNameList = taskInfoList
                .stream()
                .map(o -> o.getTaskStartDate().format(dateTimeFormatter)) // 在获取任务的开始年月
                .collect(Collectors.toList());

        return xAxisNameList;
    }

    /**
     * 对任务进行开始时间的升序排序
     * @param taskIdByOrderStartTimeLimit4
     * @param taskInfo
     * @return
     */
    private List<TaskInfo> getTaskInfos(List<TaskInfo> taskIdByOrderStartTimeLimit4, TaskInfo taskInfo) {
        List<TaskInfo> tempList = new ArrayList<>(taskIdByOrderStartTimeLimit4);
        tempList.add(taskInfo);
        List<TaskInfo> taskInfoList = tempList
                .stream()
                .sorted(Comparator.comparing(TaskInfo::getTaskStartDate)) // 根据任务开始时间进行排序
                .collect(Collectors.toList());
        return taskInfoList;
    }

    /**
     * 根据任务查询污水检材，筛选出某个省份的已经受理了的检材
     * @param dto
     * @param taskInfo
     * @return
     */
    private List<SewageJobIdentificationMaterialVO> getSewageJobIdentificationMaterialVOS(ExportSewageAnalystReportsDTO dto, TaskInfo taskInfo) {
        R<List<SewageJobIdentificationMaterialVO>> voListByJobIdR = remoteSewageJobIdentificationMaterialService.getSewageJobIdentificationMaterialVOListByJobId(taskInfo.getOriginalId());
        if (voListByJobIdR.getCode() == CommonConstants.FAIL) {
            log.error("根据任务id 获取检材失败！");
            throw new RuntimeException("根据任务id 获取检材失败！");
        }
        List<SewageJobIdentificationMaterialVO> data = voListByJobIdR
                .getData()
                .stream()
                .filter(vo ->
                        vo.getProvinceName()
                                .equals(dto.getProvinceName()) && vo.getAcceptTime() != null)  // 根据省编码筛选
                .collect(Collectors.toList());
        return data;
    }

    /**
     * 根据当前任务去比较之前四个任务的数据
     *
     * @param taskStartDateList                 要比较的毒品消费量数据
     * @param legendNameList                 要比较的毒品消费量数据
     * @param regionalDrugConsumptionDTOList 保存着全省毒品消费量数据
     * @return
     */
    private BarLineChartForm generateDrugConsumePerThousandTrendChartData(List<String> taskStartDateList,
                                                                          List<String> legendNameList,
                                                                          List<RegionalDrugConsumptionDTO> regionalDrugConsumptionDTOList) {

        List<AxisYVal> yData = new ArrayList<>();
        for (String drugName : legendNameList) {
            List<Double> thousandPerCapitaConsumptionList = getThousandPerCapitaConsumptionByDrug(regionalDrugConsumptionDTOList, drugName, false);
            AxisYVal axisYVal = new AxisYVal();
            axisYVal.setTitle(drugName);
            axisYVal.setVal(thousandPerCapitaConsumptionList.toArray(new Double[]{}));
            yData.add(axisYVal);
        }
        // 折线数据组装
        List<Double> pccTotals = regionalDrugConsumptionDTOList.stream()
                .map(o -> Double.parseDouble(o.getPccTotal()))
                .collect(Collectors.toList());
        AxisYVal zx = new AxisYVal();
        zx.setTitle("综合");
        // 这里的值不能跌倒过来
        zx.setVal(pccTotals.toArray(new Double[]{}));
        List<AxisYVal> yLintData = Collections.singletonList(zx); // 使用不可变列表

        BarLineChartForm chartForm = new BarLineChartForm();
        chartForm.setTitle("");
        chartForm.setXTitle("");
        chartForm.setYTitle("千人均消费量（毫克/千人/天）");
        chartForm.setXData(taskStartDateList);
        chartForm.setYBarData(yData);
        chartForm.setYLineData(yLintData);
        return chartForm;
    }

    /**
     * 取出各行政区主要毒品总消费量表 数据DTO 列表中的全省数据
     *
     * @param regionalDrugConsumptionDTOList
     * @param list
     * @param allProvince 当前报告中的全省毒品消费量
     */
    private void extractedProvinceDrugConsumeData(List<List<RegionalDrugConsumptionDTO>> regionalDrugConsumptionDTOList, List<RegionalDrugConsumptionDTO> list, RegionalDrugConsumptionDTO allProvince) {
        for (int i = 0; i < regionalDrugConsumptionDTOList.size(); i++) {
            List<RegionalDrugConsumptionDTO> regionalDrugConsumptionDTOSubList = regionalDrugConsumptionDTOList.get(i);
            list.add(regionalDrugConsumptionDTOSubList.get(regionalDrugConsumptionDTOSubList.size()-1));
        }
        list.add(allProvince);
    }

    /**
     * 循环生成陕西省各污水厂服务区及各行政区毒品千人均消费量表
     * @param sewageDataGroupByMap
     * @param regionalDrugConsumptionDTOMap
     * @param doc
     */
    private void generateServiceAreaDrugConsumptionPerThousandTable(Map<String, List<SewageDataDto>> sewageDataGroupByMap, Map<String, RegionalDrugConsumptionDTO> regionalDrugConsumptionDTOMap, XWPFDocument doc) {
        List<String> nameList1 = new ArrayList<>(Arrays.asList("海洛因", "冰毒", "氯胺酮", "MDMA", "可卡因", "芬太尼", "综合折算"));

        for (String cityName : sewageDataGroupByMap.keySet()) {

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

            // 取这个市的所有污水检材
            List<SewageDataDto> sewageDataDtos = sewageDataGroupByMap.get(cityName);
            Map<String, List<SewageDataDto>> sewageDataGroupByAreaName = null;
            if (cityName.equals("西安市") || cityName.equals("铜川市")) {
                // 根据检材的名称分组，因为检材名称的组成是 污水厂名称 + "-" +节假日或工作日样本组成, 所以这里通过 - 分隔 取第一个元素
                sewageDataGroupByAreaName = sewageDataDtos.stream().collect(Collectors.groupingBy(o -> StrUtil.split(o.getSampleName(), "-").get(0)));
            } else {
                sewageDataGroupByAreaName = sewageDataDtos.stream().collect(Collectors.groupingBy(SewageDataDto::getDistrictName));
            }
            List<List<Object>> list = new ArrayList<>();
            for (String areaName : sewageDataGroupByAreaName.keySet()) {
                List<SewageDataDto> sewageDataDtosByAreaName = sewageDataGroupByAreaName.get(areaName);
                // 总测算人口
                double estimatedPopulationSum = sewageDataDtosByAreaName.stream().mapToDouble(SewageDataDto::getEstimatedPopulation).sum();
                // 这里变量名取错了，这里的变量名的意思是 每个区县的千人均消费量，以tc开头
                Double tcHeroinSum = sewageDataDtosByAreaName.stream().mapToDouble(item -> item.getPccHeroin() * item.getEstimatedPopulation()).sum() / estimatedPopulationSum;
                Double tcMaSum = sewageDataDtosByAreaName.stream().mapToDouble(item -> item.getPccMa() * item.getEstimatedPopulation()).sum() /estimatedPopulationSum;
                Double tcKSum = sewageDataDtosByAreaName.stream().mapToDouble(item -> item.getPccK() * item.getEstimatedPopulation()).sum() / estimatedPopulationSum;
                Double tcMdmaSum = sewageDataDtosByAreaName.stream().mapToDouble(item -> item.getPccMdma() * item.getEstimatedPopulation()).sum() /estimatedPopulationSum;
                Double tcCocSum = sewageDataDtosByAreaName.stream().mapToDouble(item -> item.getPccCoc() * item.getEstimatedPopulation()).sum() / estimatedPopulationSum;
                Double tcFenSum = sewageDataDtosByAreaName.stream().mapToDouble(item -> item.getPccFen() * item.getEstimatedPopulation()).sum() / estimatedPopulationSum;
                // 对计算出来的数据进行NAN判断处理
                ValidDrugConsumptionResult result = getValidDrugConsumptionResult(tcHeroinSum, tcMaSum, tcKSum, tcMdmaSum, tcCocSum, tcFenSum);
                Double tcTotal = result.tcKSum + result.tcMaSum + result.tcHeroinSum + result.tcMdmaSum + result.tcCocSum;
                List<Object> rowDataList = new ArrayList<>();
                rowDataList.add(areaName); // 区县
                for (String name : nameList1) {
                    if (name.equals("海洛因")) {
                        rowDataList.add(keep1DecimalPlace.format(result.tcHeroinSum));
                    } else if (name.equals("冰毒")) {
                        rowDataList.add(keep1DecimalPlace.format(result.tcMaSum));
                    } else if (name.equals("氯胺酮")) {
                        rowDataList.add(keep1DecimalPlace.format(result.tcKSum));
                    } else if (name.equals("MDMA")) {
                        rowDataList.add(keep1DecimalPlace.format(result.tcMdmaSum));
                    } else if (name.equals("可卡因")) {
                        rowDataList.add(keep1DecimalPlace.format(result.tcCocSum));
                    } else if (name.equals("芬太尼")) {
                        rowDataList.add(keep1DecimalPlace.format(result.tcFenSum));
                    } else if (name.equals("综合折算")) {
                        rowDataList.add(keep1DecimalPlace.format(tcTotal));
                    }
                }
                // 样品中可替宁浓度低于500 ng/L 为 不合格样品
                boolean exist = whetExistsUnqualifiedSamples(sewageDataDtosByAreaName);
                rowDataList.add(exist ? "存在不合格样品" : "");
                list.add(rowDataList);
            }
            // 添加地区平均
            List<Object> lastRowDataList = new ArrayList<>();
            RegionalDrugConsumptionDTO regionalDrugConsumptionDTO = regionalDrugConsumptionDTOMap.get(cityName);
            lastRowDataList.add("地区平均");
            for (String name : nameList1) {
                if (name.equals("海洛因")) {
                    lastRowDataList.add(regionalDrugConsumptionDTO.getPccHeroin());
                } else if (name.equals("冰毒")) {
                    lastRowDataList.add(regionalDrugConsumptionDTO.getPccMa());
                } else if (name.equals("氯胺酮")) {
                    lastRowDataList.add(regionalDrugConsumptionDTO.getPccK());
                } else if (name.equals("MDMA")) {
                    lastRowDataList.add(regionalDrugConsumptionDTO.getPccMdma());
                } else if (name.equals("可卡因")) {
                    lastRowDataList.add(regionalDrugConsumptionDTO.getPccCoc());
                } else if (name.equals("芬太尼")) {
                    lastRowDataList.add(regionalDrugConsumptionDTO.getPccFen());
                } else if (name.equals("综合折算")) {
                    lastRowDataList.add(regionalDrugConsumptionDTO.getPccTotal());
                }
            }
            list.add(lastRowDataList);
            dataMap.put(cityName, list); // 行政区城市名
            // 加2 的原因是有两行表头
            TableCreateUtils.fetchServiceAreaDrugConsumptionPerThousandTable(doc,list.size() + 2, 10, nameList1, dataMap);
            doc.createParagraph();
            doc.createParagraph();
        }
    }

    /**
     * 判断是否存在不合格样品
     * 不合格样品：定义为检测出该样品中可替宁浓度低于500 ng/L。
     * @param sewageDataDtosByAreaName
     * @return
     */
    private boolean whetExistsUnqualifiedSamples(List<SewageDataDto> sewageDataDtosByAreaName) {

        for (SewageDataDto sewageDataDto : sewageDataDtosByAreaName) {
            if (sewageDataDto.getCotinineConcentration() < 500) {
                return true;
            }
        }
        return false;
    }

    /**
     * 图2 陕西省各行政区毒品千人均消费量 柱状图
     *
     * @param doc
     * @param regionalDrugConsumptionDTOList
     * @param pieChartForm
     * @param cityNameList
     * @return
     */
    private BarLineChartForm getServiceAreaDrugConsumeBarLineChartForm(XWPFDocument doc, List<RegionalDrugConsumptionDTO> regionalDrugConsumptionDTOList, PieChartForm pieChartForm, List<String> cityNameList) {
        List<RegionalDrugConsumptionDTO> list = new ArrayList<>(regionalDrugConsumptionDTOList); // new 一个新的列表操作

        for (int i = 0; i < 2; i++) { // 空两行
            doc.createParagraph().createRun().addBreak();
        }
        List<String> xData = new ArrayList<>();
        List<AxisYVal> yData = new ArrayList<>();
//        for (int i = 0; i < regionalDrugConsumptionDTOList.size() - 1; i++) { // 不去最后一个的原因是，最后一个元素计算的是全省的
//            RegionalDrugConsumptionDTO dto = regionalDrugConsumptionDTOList.get(i);
//            xData.add(dto.getRegional());
//        }
        for (int i = 0; i < cityNameList.size(); i++) {
            String cityName = cityNameList.get(i);
            if (i < regionalDrugConsumptionDTOList.size() - 1) { // 不取最后一个的原因是，最后一个元素计算的是全省的
                RegionalDrugConsumptionDTO dto = regionalDrugConsumptionDTOList.get(i);
                xData.add(dto.getRegional());
            } else if (!xData.contains(cityName)){
                xData.add(cityName);
                RegionalDrugConsumptionDTO dto = new RegionalDrugConsumptionDTO();
                dto.setRegional(cityName);
                dto.setPccHeroin("0");
                dto.setPccFen("0");
                dto.setPccMa("0");
                dto.setPccMdma("0");
                dto.setPccK("0");
                dto.setPccCoc("0");
                list.add(dto);
            }
        }
        for (String drugName : pieChartForm.getXData()) {
            List<Double> thousandPerCapitaConsumptionList = getThousandPerCapitaConsumptionByDrug(list, drugName, true);
            AxisYVal axisYVal = new AxisYVal();
            axisYVal.setTitle(drugName);
            axisYVal.setVal(thousandPerCapitaConsumptionList.toArray(new Double[]{}));
            yData.add(axisYVal);
        }

        BarLineChartForm chartForm1 = new BarLineChartForm();
        chartForm1.setTitle("");
        chartForm1.setXTitle("");
        chartForm1.setYTitle("千人均消费量（毫克/千人/天）");
        // new ArrayList<>(Arrays.asList("西安市", "咸阳市", "宝鸡市", "渭南市", "铜川市", "榆林市", "延安市", "汉中市", "商洛市", "安康市", "杨凌区"))
        chartForm1.setXData(xData);
        chartForm1.setYBarData(yData);
        chartForm1.setYLineData(null);
        return chartForm1;
    }

    /**
     * 根据毒品的名称获取对应各行政区的千人均消费量列表
     * @param regionalDrugConsumptionDTOList
     * @param drugName
     * @param isRemoveLastElement 是否移除最后一个元素
     * @return
     */
    private List<Double> getThousandPerCapitaConsumptionByDrug(List<RegionalDrugConsumptionDTO> regionalDrugConsumptionDTOList, String drugName, boolean isRemoveLastElement) {
        List<Double> thousandPerCapitaConsumptionList = new ArrayList<>();
        List<RegionalDrugConsumptionDTO> list = new ArrayList<>(regionalDrugConsumptionDTOList); // 因为参数是引用类型，直接移除会影响其他地方的调用，这里通过新new 一个实例来操作
        if (isRemoveLastElement) {
            // 因为最后一个是全省的所以移除
            list.remove(list.size() -1);
        }
        if (drugName.equals("冰毒")) {
            thousandPerCapitaConsumptionList = list.stream().map(o -> Double.parseDouble(o.getPccMa())).collect(Collectors.toList());
        } else if (drugName.equals("海洛因")) {
            thousandPerCapitaConsumptionList = list.stream().map(o -> Double.parseDouble(o.getPccHeroin())).collect(Collectors.toList());
        } else if (drugName.equals("氯胺酮")) {
            thousandPerCapitaConsumptionList = list.stream().map(o -> Double.parseDouble(o.getPccK())).collect(Collectors.toList());
        } else if (drugName.equals("摇头丸（MDMA）")) {
            thousandPerCapitaConsumptionList = list.stream().map(o -> Double.parseDouble(o.getPccMdma())).collect(Collectors.toList());
        } else if (drugName.equals("可卡因")) {
            thousandPerCapitaConsumptionList = list.stream().map(o -> Double.parseDouble(o.getPccCoc())).collect(Collectors.toList());
        } else if (drugName.equals("芬太尼")) {
            thousandPerCapitaConsumptionList = list.stream().map(o -> Double.parseDouble(o.getPccFen())).collect(Collectors.toList());
        }
        return thousandPerCapitaConsumptionList;
    }

    /**
     * 全省监测区域 内容生成
     * @param inspectResultAnalysisMap 在生成检测结果分析时计算的百分比
     * @param allProvince
     * @return
     */
    private String getProvinceMonitorsDrugConsumeContent(Map<String, Object> inspectResultAnalysisMap, RegionalDrugConsumptionDTO allProvince) {
        String provinceMonitorsDrugConsumeContent = "";
        for (String key : inspectResultAnalysisMap.keySet()) {
            if (key.equals("resultAnalysis")) {
                continue;
            }
            String consumption = getConsumption(allProvince, key);
            String percent =  keep1DecimalPlace.format((Double) inspectResultAnalysisMap.get(key) * 100d);
            provinceMonitorsDrugConsumeContent = StrUtil.isNotBlank(provinceMonitorsDrugConsumeContent)
                    ? provinceMonitorsDrugConsumeContent + key + "消费量为" + consumption + "克/天，占比" + percent + "%；"
                    : "全省监测区域内，" + key + "消费量为" + consumption + "克/天，占比" + percent + "%；" ;
        }
        // 删除最后一个；,替换成 。
        return provinceMonitorsDrugConsumeContent.substring(0, provinceMonitorsDrugConsumeContent.length()-1) + "（受合法药物干扰，存在一定误差，仅供参考）。";
    }

    /**
     * 获取不同毒品的消费量
     * @param allProvince
     * @param key
     * @return
     */
    private String getConsumption(RegionalDrugConsumptionDTO allProvince, String key) {
        String consumption = "";
        if (key.equals("冰毒")) {
            consumption = allProvince.getTcMa();
        } else if (key.equals("海洛因")) {
            consumption = allProvince.getTcHeroin();
        } else if (key.equals("氯胺酮")) {
            consumption = allProvince.getTcK();
        } else if (key.equals("摇头丸（MDMA）")) {
            consumption = allProvince.getTcMdma();
        } else if (key.equals("可卡因")) {
            consumption = allProvince.getTcCoc();
        } else if (key.equals("芬太尼")) {
            consumption = allProvince.getTcFen();
        }
        return consumption;
    }

    /**
     * 根据饼图数据生成检测结果分析
     *
     * @param pieChartForm
     * @param allProvince
     * @return
     */
    private Map<String, Object> getInspectResultAnalysis(PieChartForm pieChartForm, RegionalDrugConsumptionDTO allProvince) {


        List<String> drugNameList = pieChartForm.getXData(); // 毒品名称列表
        Double[] numbers = (Double[]) pieChartForm.getYData().getVal(); // 饼图各毒品的消费量
        boolean flagMaster = false; // 标记是否存在大于10%的毒品，默认没有
        // 求数组的和
        Double sum = calculateArraySum(numbers);
        Map<String, Object> map = new HashMap<>();
        for (int i = 0; i < numbers.length; i++) {
            double percent = numbers[i] / sum;
            if (percent > 0.1) {
                flagMaster = true;
            }
            map.put(drugNameList.get(i), percent);
        }

        // 拼接检出的字符串
        String detectedDrug = "";
        if (drugNameList.size() > 0) {
            detectedDrug = getDetectedDrugStr(numbers, flagMaster, drugNameList, map);
        }

        // 未检出的
        String noDetected = getNoDetectedStr(allProvince);

        map.put("resultAnalysis", detectedDrug + noDetected);
        return map;
    }

    /**
     * 获取检出的毒品字符串
     *
     * @param numbers
     * @param drugNameList
     * @param map
     * @return
     */
    private String getDetectedDrugStr(Double[] numbers, boolean flagMaster, List<String> drugNameList, Map<String, Object> map) {

        String detectedDrug = "";
        if (flagMaster) {
            String masterStr = ""; // 主要毒品
            String secondStr = ""; // 其次
            for (String key : map.keySet()) {
                Double v = (Double) map.get(key);
                if (v > 0.1) {
                    masterStr = StrUtil.isNotBlank(masterStr) ? masterStr + "、" + key : "陕西省存在滥用的主要毒品为" + key;
                } else {
                    secondStr = StrUtil.isNotBlank(secondStr) ? secondStr + "、" + key : "其次为" + key;
                }
            }
            // 因为flag 为true 那么肯定是有大于0.1的
            detectedDrug = masterStr + (StrUtil.isBlank(secondStr) ? "。" : "," + secondStr + "。");
        } else {
            detectedDrug = "陕西省存在滥用的毒品为" + drugNameList.stream().collect(Collectors.joining("、")) + "。";
        }

        return detectedDrug;
    }

    /**
     * 获取结果分析中未检出的字符串
     * @param allProvince
     * @return
     */
    private String getNoDetectedStr(RegionalDrugConsumptionDTO allProvince) {
        String noDetected = "";
        if (Double.parseDouble(allProvince.getTcHeroin()) == 0) {
            noDetected = noDetected + "海洛因";
        }
        // 冰毒
        if (Double.parseDouble(allProvince.getTcMa()) == 0) {
            noDetected = StrUtil.isNotBlank(noDetected) ? noDetected + "、冰毒" : "冰毒";
        }
        // 氯胺酮
        if (Double.parseDouble(allProvince.getTcK()) == 0) {
            noDetected = StrUtil.isNotBlank(noDetected) ? noDetected + "、氯胺酮" : "氯胺酮";
        }
        // 摇头丸
        if (Double.parseDouble(allProvince.getTcMdma()) == 0) {
            noDetected = StrUtil.isNotBlank(noDetected) ? noDetected + "、摇头丸" : "摇头丸";
        }
        // 可卡因
        if (Double.parseDouble(allProvince.getTcCoc()) == 0) {
            noDetected = StrUtil.isNotBlank(noDetected) ? noDetected + "、可卡因" : "可卡因";
        }
        // 芬太尼
        if (Double.parseDouble(allProvince.getTcFen()) == 0) {
            noDetected = StrUtil.isNotBlank(noDetected) ? noDetected + "、芬太尼" : "芬太尼";
        }
        // 如果确实有未检出的，则拼接 均未检出，暂未发现明显滥用。
        if (StrUtil.isNotBlank(noDetected)) {
            noDetected = noDetected + "均未检出，暂未发现明显滥用。";
        }
        return noDetected;
    }

    /**
     * 数组求和
     * @param numbers
     * @return
     */
    private Double calculateArraySum(Number[] numbers) {
        Double sum = 0d;
        for (Number number : numbers) {
            sum = sum + (Double) number;
        }
        return sum;
    }

    /**
     * 获取文件模板样式路径
     *
     * @return
     * @throws UnsupportedEncodingException
     */
    private ByteArrayInputStream getTemplateStyleFilePath() throws Exception {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
//        ossFile.fileGet(OSSDirectoryConstants.TEMPLATE_DIRECTORY + "/" + "templateStyles.docx", bos);
        String path = null;
        try {
//            path = new ClassPathResource("template\\templateStyles.docx").getPath();
            ossFile.fileGet(OSSDirectoryConstants.TEMPLATE_DIRECTORY + "/" + "templateStyles.docx", bos);
        } catch (Exception e) {
            log.error("文件名为 {} 的模板路径获取失败！", "template\\templateStyles.docx");
            e.printStackTrace();
        }
//        String filePath = URLDecoder.decode(path, "UTF-8");
        return new  ByteArrayInputStream(bos.toByteArray());
    }

    /**
     * 生成文档首页
     * @param doc
     * @param yearMonth 报告的月份
     * @param organization 国家毒品实验室陕西分中心
     * @param generateDate 生成日期
     * @param province 省份
     */
    private void generateHomePage(XWPFDocument doc, String yearMonth, String organization, String generateDate, String province) {
        // 空三行
        for (int i = 0; i < 3; i++) {
            doc.createParagraph().createRun().addBreak();
        }
        WordUtils.createDocHomePage(doc, yearMonth + province + "污水样品毒品监测分析报告", "",16, 24);
        WordUtils.createDocHomePage(doc, organization, "",0, 18);
        WordUtils.createDocHomePage(doc, generateDate, "",0, 18);
    }

    /**
     * 各污水厂监测点主要毒品及其代谢物每日浓度表 的 数据组装
     * @param sewageDataGroupByMap
     * @param tableName5List
     * @return
     */
    private Map<String, List<List<Object>>> generateDailyDrugAndMetaboliteConcentrationData(Map<String, List<SewageDataDto>> sewageDataGroupByMap, List<String> tableName5List) {
        Map<String, List<List<Object>>> dataMap = new HashMap<>();
        Map<String, Function<SewageDataDto, Double>> drugConcentrationMap = new HashMap<>();
        drugConcentrationMap.put("可替宁", SewageDataDto::getCotinineConcentration);
        drugConcentrationMap.put("可待因", SewageDataDto::getCodeineConcentration);
        drugConcentrationMap.put("MDA", SewageDataDto::getMdaConcentration);
        drugConcentrationMap.put("MDMA", SewageDataDto::getMdmaConcentration);
        drugConcentrationMap.put("可卡因", SewageDataDto::getCocaineConcentration);
        drugConcentrationMap.put("苯甲酰爱康宁", SewageDataDto::getBenzoylecgonineConcentration);
        drugConcentrationMap.put("吗啡", SewageDataDto::getMorphineConcentration);
        drugConcentrationMap.put("O6-单乙酰吗啡", SewageDataDto::getAcetylmorphineConcentration);
        drugConcentrationMap.put("甲基苯丙胺", SewageDataDto::getMethamphetamineConcentration);
        drugConcentrationMap.put("苯丙胺", SewageDataDto::getAmphetamineConcentration);
        drugConcentrationMap.put("氯胺酮", SewageDataDto::getKetamineConcentration);
        drugConcentrationMap.put("去甲氯胺酮", SewageDataDto::getNorketamineConcentration);
        drugConcentrationMap.put("芬太尼", SewageDataDto::getFenConcentration);
        sewageDataGroupByMap.forEach((k,v) -> {
            List dataList = new ArrayList();
            for (SewageDataDto sewageDataDto : v) {
                ArrayList<Object> subDataList = new ArrayList<>();
                // 根据检材的名称分组，因为检材名称的组成是 污水厂名称 + "-" +节假日或工作日样本组成, 所以这里通过 - 分隔 取第一个元素
                subDataList.add(StrUtil.split(sewageDataDto.getSampleName(), "-").get(0));// 污水厂名称
                subDataList.add(sewageDataDto.getCollectTime().format(DateTimeFormatter.ofPattern("yyyy-M-d")));
                for (String name : tableName5List) {
                    Function<SewageDataDto, Double> concentrationGetterFunction = drugConcentrationMap.get(name);
                    if (concentrationGetterFunction != null) {
                        subDataList.add(keep1DecimalPlace.format(concentrationGetterFunction.apply(sewageDataDto)));
                    } else {
                        // 或者添加null，表示该数据不存在
                    }
                }
                subDataList.add("备注");
                dataList.add(subDataList);
            }
            dataMap.put(k, dataList);
        });

        return dataMap;
    }

    /**
     * 各行政区毒品总消费量变化趋势 折线图
     *
     * @param allMergeMegionalDrugConsumptionDTOList
     * @param regionalDrugConsumptionDTOList
     * @param taskStartDateList
     * @param cityNameList
     * @return
     */
    private BarLineChartForm getRendsInTotalDrugConsumptionInEachAdministrativeRegion(List<List<RegionalDrugConsumptionDTO>> allMergeMegionalDrugConsumptionDTOList, List<RegionalDrugConsumptionDTO> regionalDrugConsumptionDTOList, List<String> taskStartDateList, List<String> cityNameList) {

        List<AxisYVal> yLintData1 = getAxisYValsByLiineChart(allMergeMegionalDrugConsumptionDTOList, regionalDrugConsumptionDTOList, cityNameList,true);

        BarLineChartForm lineChartForm = new BarLineChartForm();
        lineChartForm.setTitle("");
        lineChartForm.setXTitle("");
        lineChartForm.setYTitle("总消费量（克/天）");
        lineChartForm.setXData(taskStartDateList);
        lineChartForm.setYBarData(null);
        lineChartForm.setYLineData(yLintData1);
        return lineChartForm;
    }

    /**
     * 获取折线图的数据
     * @param allMergeMegionalDrugConsumptionDTOList
     * @param regionalDrugConsumptionDTOList
     * @param cityNameList
     * @param flag 取值是综合总消费量还是千人均总消费量
     * @return
     */
    private static List<AxisYVal> getAxisYValsByLiineChart(List<List<RegionalDrugConsumptionDTO>> allMergeMegionalDrugConsumptionDTOList, List<RegionalDrugConsumptionDTO> regionalDrugConsumptionDTOList, List<String> cityNameList, boolean flag) {
        List<List<RegionalDrugConsumptionDTO>> list = new ArrayList<>(allMergeMegionalDrugConsumptionDTOList);
        list.add(regionalDrugConsumptionDTOList);

        List<Map<String, RegionalDrugConsumptionDTO>> mapList = list.stream().map(item -> {
            return item.stream().collect(Collectors.toMap(RegionalDrugConsumptionDTO::getRegional, Function.identity()));
        }).collect(Collectors.toList());

        List<AxisYVal> yLintData1 = new ArrayList<>();
        for (String cityName : cityNameList) {
            AxisYVal axisYVal = new AxisYVal();
            axisYVal.setTitle(cityName);
            Double[] doubles = new Double[list.size()];
            for (int i = 0; i < mapList.size(); i++) {
                Map<String, RegionalDrugConsumptionDTO> map = mapList.get(i);
                if (map.containsKey(cityName)) {
                    RegionalDrugConsumptionDTO dto = map.get(cityName);
                    doubles[i] = Double.valueOf(flag ? dto.getTcTotal() : dto.getPccTotal());
                } else {
                    doubles[i] = 0d;
                }
            }
            axisYVal.setVal(doubles);
            yLintData1.add(axisYVal);
        }
        return yLintData1;
    }

    /**
     * 图5 各行政区毒品千人均消费量变化趋势
     * @param allMergeMegionalDrugConsumptionDTOList
     * @param regionalDrugConsumptionDTOList
     * @param taskStartDateList
     * @param cityNameList
     * @return
     */
    private BarLineChartForm getTrendsInPerCapitaDrugConsumptionEachAdministrativeRegion(List<List<RegionalDrugConsumptionDTO>> allMergeMegionalDrugConsumptionDTOList, List<RegionalDrugConsumptionDTO> regionalDrugConsumptionDTOList, List<String> taskStartDateList, List<String> cityNameList) {

        List<AxisYVal> yLintData1 = getAxisYValsByLiineChart(allMergeMegionalDrugConsumptionDTOList, regionalDrugConsumptionDTOList, cityNameList, false);

        BarLineChartForm lineChartForm = new BarLineChartForm();
        lineChartForm.setTitle("");
        lineChartForm.setXTitle("");
        lineChartForm.setYTitle("千人均消费量/(毫克/千人/天)");
        lineChartForm.setXData(taskStartDateList);
        lineChartForm.setYBarData(null);
        lineChartForm.setYLineData(yLintData1);
        return lineChartForm;
    }

    /**
     * 获取陕西省各行政区主要毒品总消费量表数据 列表
     * @param sewageDataGroupByMap
     * @return
     */
    private List<RegionalDrugConsumptionDTO> fetchRegionalDrugConsumption(Map<String, List<SewageDataDto>> sewageDataGroupByMap) {

        List<RegionalDrugConsumptionDTO> regionalDrugConsumptionDTOS = new ArrayList<>();

        sewageDataGroupByMap.forEach((k,v) -> {
            RegionalDrugConsumptionDTO dto = getRegionalDrugConsumptionDTO(v);
            regionalDrugConsumptionDTOS.add(dto);
        });
        //  全省平均/总计 计算方式目前还不清楚
        regionalDrugConsumptionDTOS.add(getAllProvinceAvgAndSumDataDTO(regionalDrugConsumptionDTOS));

        return regionalDrugConsumptionDTOS;
    }

    /**
     * 根据污水检材 去计算污水毒品千人均消费量，然后根据城市名称进行分组
     * @param sewageDataDtos
     * @return
     */
    private Map<String, List<SewageDataDto>> getSewageDataGroupByCityNameMap(List<SewageDataDto> sewageDataDtos) {
        // 根据市级编码 分组
        Map<String, List<SewageDataDto>> sewageDataGroupByMap = sewageDataDtos
                .stream()
                .collect(Collectors
                        .groupingBy(SewageDataDto::getCityName));
        return sewageDataGroupByMap;
    }

    /**
     * 获取全省各种毒品的千人均消费量平均值和总消费量
     * @param regionalDrugConsumptionDTOS
     * @return
     */
    private RegionalDrugConsumptionDTO getAllProvinceAvgAndSumDataDTO(List<RegionalDrugConsumptionDTO> regionalDrugConsumptionDTOS) {
        // 这部分是先计算全省各毒品平均消耗量
        double allProvinceHeroinAvg = calculateAverage(regionalDrugConsumptionDTOS, regionalDrugConsumptionDTO -> Double.parseDouble(regionalDrugConsumptionDTO.getPccHeroin()));
        double allProvinceCocAvg = calculateAverage(regionalDrugConsumptionDTOS, regionalDrugConsumptionDTO -> Double.parseDouble(regionalDrugConsumptionDTO.getPccCoc()));
        double allProvinceKAvg = calculateAverage(regionalDrugConsumptionDTOS, regionalDrugConsumptionDTO -> Double.parseDouble(regionalDrugConsumptionDTO.getPccK()));
        double allProvinceMaAvg = calculateAverage(regionalDrugConsumptionDTOS, regionalDrugConsumptionDTO -> Double.parseDouble(regionalDrugConsumptionDTO.getPccMa()));
        double allProvinceMdmaAvg = calculateAverage(regionalDrugConsumptionDTOS, regionalDrugConsumptionDTO -> Double.parseDouble(regionalDrugConsumptionDTO.getPccMdma()));
        double allProvinceFenAvg = calculateAverage(regionalDrugConsumptionDTOS, regionalDrugConsumptionDTO -> Double.parseDouble(regionalDrugConsumptionDTO.getPccFen()));
        double allProvincePccTotalAvg = calculateAverage(regionalDrugConsumptionDTOS, regionalDrugConsumptionDTO -> Double.parseDouble(regionalDrugConsumptionDTO.getPccTotal()));
        // 全省各毒品的总消耗量
        double allProvinceHeroinTotalSum = calculateConsumeSum(regionalDrugConsumptionDTOS, regionalDrugConsumptionDTO -> Double.parseDouble(regionalDrugConsumptionDTO.getTcHeroin()));
        double allProvinceMaTotalSum = calculateConsumeSum(regionalDrugConsumptionDTOS, regionalDrugConsumptionDTO -> Double.parseDouble(regionalDrugConsumptionDTO.getTcMa()));
        double allProvinceKTotalSum = calculateConsumeSum(regionalDrugConsumptionDTOS, regionalDrugConsumptionDTO -> Double.parseDouble(regionalDrugConsumptionDTO.getTcK()));
        double allProvinceMdmaTotalSum = calculateConsumeSum(regionalDrugConsumptionDTOS, regionalDrugConsumptionDTO -> Double.parseDouble(regionalDrugConsumptionDTO.getTcMdma()));
        double allProvinceCocTotalSum = calculateConsumeSum(regionalDrugConsumptionDTOS, regionalDrugConsumptionDTO -> Double.parseDouble(regionalDrugConsumptionDTO.getTcCoc()));
        double allProvinceFenTotalSum = calculateConsumeSum(regionalDrugConsumptionDTOS, regionalDrugConsumptionDTO -> Double.parseDouble(regionalDrugConsumptionDTO.getTcFen()));
        double allProvinceTotalSum = calculateConsumeSum(regionalDrugConsumptionDTOS, regionalDrugConsumptionDTO -> Double.parseDouble(regionalDrugConsumptionDTO.getTcTotal()));

        RegionalDrugConsumptionDTO dto = new RegionalDrugConsumptionDTO();
        dto.setRegional("全省平均/总计");
        // 平均
        dto.setPccHeroin(keep1DecimalPlace.format(allProvinceHeroinAvg));
        dto.setPccMdma(keep1DecimalPlace.format(allProvinceMdmaAvg));
        dto.setPccK(keep1DecimalPlace.format(allProvinceKAvg));
        dto.setPccMa(keep1DecimalPlace.format(allProvinceMaAvg));
        dto.setPccCoc(keep1DecimalPlace.format(allProvinceCocAvg));
        dto.setPccFen(keep1DecimalPlace.format(allProvinceFenAvg));
        dto.setPccTotal(keep1DecimalPlace.format(allProvincePccTotalAvg));

        // 总计
        dto.setTcHeroin(keep1DecimalPlace.format(allProvinceHeroinTotalSum));
        dto.setTcMdma(keep1DecimalPlace.format(allProvinceMdmaTotalSum));
        dto.setTcCoc(keep1DecimalPlace.format(allProvinceCocTotalSum));
        dto.setTcMa(keep1DecimalPlace.format(allProvinceMaTotalSum));
        dto.setTcK(keep1DecimalPlace.format(allProvinceKTotalSum));
        dto.setTcFen(keep1DecimalPlace.format(allProvinceFenTotalSum));
        dto.setTcTotal(keep1DecimalPlace.format(allProvinceTotalSum));

        return dto;
    }

    /**
     * 全省各毒品平均消耗量
     * @param regionalDrugConsumptionDTOS
     * @param mapper
     * @return
     */
    private double calculateAverage(List<RegionalDrugConsumptionDTO> regionalDrugConsumptionDTOS, Function<RegionalDrugConsumptionDTO, Double> mapper) {
        return regionalDrugConsumptionDTOS.stream()
                .mapToDouble(mapper::apply)
                .average()
                .orElse(0.0);
    }

    /**
     * 全省各毒品总消耗量
     * @param regionalDrugConsumptionDTOS
     * @param mapper
     * @return
     */
    private double calculateConsumeSum(List<RegionalDrugConsumptionDTO> regionalDrugConsumptionDTOS, Function<RegionalDrugConsumptionDTO, Double> mapper) {
        return regionalDrugConsumptionDTOS.stream().mapToDouble(mapper::apply).sum();
    }

    /**
     * 获取各行政区主要毒品总消费量 数据传输对象
     * @param v
     * @return
     */
    private RegionalDrugConsumptionDTO getRegionalDrugConsumptionDTO(List<SewageDataDto> v) {
        // 对小数进行保留1位小数的操作
        DecimalFormat df = new DecimalFormat("#.##");

        RegionalDrugConsumptionDTO dto = new RegionalDrugConsumptionDTO();
        SewageDataDto sewageDataDto = v.get(0);
        dto.setRegional(sewageDataDto.getCityName()); // 城市名称
        // 总测算人口
//        double estimatedPopulationSum = v.stream().mapToDouble(SewageDataDto::getEstimatedPopulation).sum();
//        // 各类毒品的总消耗量
//        Double tcHeroinSum = v.stream().mapToDouble(item -> item.getPccHeroin() * item.getEstimatedPopulation()).sum() ;
//        Double tcMaSum = v.stream().mapToDouble(item -> item.getPccMa() * item.getEstimatedPopulation()).sum();
//        Double tcKSum = v.stream().mapToDouble(item -> item.getPccK() * item.getEstimatedPopulation()).sum();
//        Double tcMdmaSum = v.stream().mapToDouble(item -> item.getPccMdma() * item.getEstimatedPopulation()).sum();
//        Double tcCocSum = v.stream().mapToDouble(item -> item.getPccCoc() * item.getEstimatedPopulation()).sum();
//        Double tcFenSum = v.stream().mapToDouble(item -> item.getPccFen() * item.getEstimatedPopulation()).sum();
        // 总测算人口及各毒品消耗量的统计
        double estimatedPopulationSum = 0;
        double tcHeroinSum = 0, tcMaSum = 0, tcKSum = 0, tcMdmaSum = 0, tcCocSum = 0, tcFenSum = 0;
        for (SewageDataDto item : v) {
            estimatedPopulationSum += item.getEstimatedPopulation();
            tcHeroinSum += item.getPccHeroin() * item.getEstimatedPopulation();
            tcMaSum += item.getPccMa() * item.getEstimatedPopulation();
            tcKSum += item.getPccK() * item.getEstimatedPopulation();
            tcMdmaSum += item.getPccMdma() * item.getEstimatedPopulation();
            tcCocSum += item.getPccCoc() * item.getEstimatedPopulation();
            tcFenSum += item.getPccFen() * item.getEstimatedPopulation();
        }
        // 对计算出来的数据进行NAN判断处理
        // 对计算出来的数据进行NAN判断处理
        ValidDrugConsumptionResult result = getValidDrugConsumptionResult(tcHeroinSum, tcMaSum, tcKSum, tcMdmaSum, tcCocSum, tcFenSum);
        Double tcTotal = result.tcKSum + result.tcMaSum + result.tcHeroinSum + result.tcMdmaSum + result.tcCocSum;
        // 总消费量（克/天）, 因为算出来的单位是毫克，所以这里需要除以1000， 综合
        dto.setTcTotal(df.format(tcTotal / 1000));
        dto.setTcHeroin(df.format(result.tcHeroinSum / 1000));
        dto.setTcMa(df.format(result.tcMaSum / 1000));
        dto.setTcK(df.format(result.tcKSum / 1000));
        dto.setTcMdma(df.format(result.tcMdmaSum / 1000));
        dto.setTcCoc(df.format(result.tcCocSum / 1000));
        dto.setTcFen(df.format(result.tcFenSum / 1000));

        // 千人均消费量（毫克/千人/天）
        double pccHeroin = estimatedPopulationSum == 0.0 ? 0 : result.tcHeroinSum / estimatedPopulationSum;
        double pccK = estimatedPopulationSum == 0.0 ? 0 : result.tcKSum / estimatedPopulationSum;
        double pccCoc = estimatedPopulationSum == 0.0 ? 0 : result.tcCocSum / estimatedPopulationSum;
        double pccMa = estimatedPopulationSum == 0.0 ? 0 : result.tcMaSum / estimatedPopulationSum;
        double pccMdma = estimatedPopulationSum == 0.0 ? 0 : result.tcMdmaSum / estimatedPopulationSum;
        double pccFen = estimatedPopulationSum == 0.0 ? 0 : result.tcFenSum / estimatedPopulationSum;
        double pccTotal = estimatedPopulationSum == 0.0 ? 0 : pccCoc + pccHeroin + pccFen + pccMa + pccMdma;
        dto.setPccHeroin(df.format(pccHeroin));
        dto.setPccK(df.format(pccK));
        dto.setPccCoc(df.format(pccCoc));
        dto.setPccMa(df.format(pccMa));
        dto.setPccMdma(df.format(pccMdma));
        dto.setPccFen(df.format(pccFen));
        dto.setPccTotal(df.format(pccTotal));
        return dto;
    }

    /**
     * 图1 陕西省常见毒品消费结构图
     * @param dto  陕西省各行政区主要毒品总消费量表数据 中计算全省的dto
     * @return
     */
    private PieChartForm generateCommonDrugConsumeStructureChart(RegionalDrugConsumptionDTO dto) {
        // 取列表最后一个，最后一个是计算全省的
        List<String> xDataSet = new ArrayList<>(); // 饼图图例名称
        AxisYVal val3 = new AxisYVal(); // 创建封装饼图数据的类
        val3.setTitle("");
        List<Double> dataList = new ArrayList<>();

        // 封装转换与添加逻辑，减少重复代码
        addIfNotZero("海洛因", dto.getTcHeroin(), xDataSet, dataList);
        addIfNotZero("冰毒", dto.getTcMa(), xDataSet, dataList);
        addIfNotZero("氯胺酮", dto.getTcK(), xDataSet, dataList);
        addIfNotZero("摇头丸（MDMA）", dto.getTcMdma(), xDataSet, dataList);
        addIfNotZero("可卡因", dto.getTcCoc(), xDataSet, dataList);
        addIfNotZero("芬太尼", dto.getTcFen(), xDataSet, dataList);

        val3.setVal(dataList.toArray(new Double[]{}));

        //拼图
        PieChartForm pieChartFrom = new PieChartForm();
        pieChartFrom.setTitle("");
        pieChartFrom.setXData(new ArrayList<>(xDataSet));
        pieChartFrom.setYData(val3);

        return pieChartFrom;
    }

    // 辅助方法，用于简化主方法中的逻辑， 减少不必要的Double.parseDouble调用，提高了代码的执行效率
    private void addIfNotZero(String drugName, String valueStr, List<String> xDataSet, List<Double> dataList) {
        double value = Double.parseDouble(valueStr);
        if (value != 0) {
            xDataSet.add(drugName);
            dataList.add(value);
        }
    }

    /**
     * 判断各类毒品消费量是不是NAN
     * @param tcHeroinSum
     * @param tcMaSum
     * @param tcKSum
     * @param tcMdmaSum
     * @param tcCocSum
     * @param tcFenSum
     * @return
     */
    private static ValidDrugConsumptionResult getValidDrugConsumptionResult(Double tcHeroinSum, Double tcMaSum, Double tcKSum, Double tcMdmaSum, Double tcCocSum, Double tcFenSum) {
        tcHeroinSum = tcHeroinSum.toString().equals("NaN") ? 0 : tcHeroinSum;
        tcMaSum = tcMaSum.toString().equals("NaN") ? 0 : tcMaSum;
        tcKSum = tcKSum.toString().equals("NaN") ? 0 : tcKSum;
        tcMdmaSum = tcMdmaSum.toString().equals("NaN") ? 0 : tcMdmaSum;
        tcCocSum = tcCocSum.toString().equals("NaN") ? 0 : tcCocSum;
        tcFenSum = tcFenSum.toString().equals("NaN") ? 0 : tcFenSum;
        ValidDrugConsumptionResult result = new ValidDrugConsumptionResult(tcHeroinSum, tcMaSum, tcKSum, tcMdmaSum, tcCocSum, tcFenSum);
        return result;
    }

    /**
     * 封装处理数据计算结果是nan的毒品消费量数据的 静态内部类
     */
    private static class ValidDrugConsumptionResult {
        public final Double tcHeroinSum;
        public final Double tcMaSum;
        public final Double tcKSum;
        public final Double tcMdmaSum;
        public final Double tcCocSum;
        public final Double tcFenSum;

        public ValidDrugConsumptionResult(Double tcHeroinSum, Double tcMaSum, Double tcKSum, Double tcMdmaSum, Double tcCocSum, Double tcFenSum) {
            this.tcHeroinSum = tcHeroinSum;
            this.tcMaSum = tcMaSum;
            this.tcKSum = tcKSum;
            this.tcMdmaSum = tcMdmaSum;
            this.tcCocSum = tcCocSum;
            this.tcFenSum = tcFenSum;
        }
    }

}
