package com.yeejoin.amos.boot.module.cylinder.flc.biz.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import com.yeejoin.amos.boot.biz.common.utils.DateUtils;
import com.yeejoin.amos.boot.module.cylinder.api.dto.CylinderFillingRecordStatisticsUnitDayDto;
import com.yeejoin.amos.boot.module.cylinder.api.dto.WarningMsgDto;
import com.yeejoin.amos.boot.module.cylinder.api.entity.CylinderFillingRecordStatisticsUnitDay;
import com.yeejoin.amos.boot.module.cylinder.api.entity.ESCylinderInfoDto;
import com.yeejoin.amos.boot.module.cylinder.api.entity.MsgLog;
import com.yeejoin.amos.boot.module.cylinder.api.enums.EarlyWarningLevelEnum;
import com.yeejoin.amos.boot.module.cylinder.api.mapper.ScheduleMapper;
import com.yeejoin.amos.boot.module.cylinder.biz.dao.ESCylinderInfoRepository;
import com.yeejoin.amos.boot.module.cylinder.biz.service.impl.CylinderFillingRecordStatisticsUnitDayServiceImpl;
import com.yeejoin.amos.boot.module.cylinder.biz.service.impl.MsgLogServiceImpl;
import com.yeejoin.amos.boot.module.cylinder.biz.service.impl.StartPlatformTokenService;
import com.yeejoin.amos.boot.module.cylinder.biz.service.impl.TzsAuthServiceImpl;
import com.yeejoin.amos.boot.module.cylinder.flc.api.dto.*;
import com.yeejoin.amos.boot.module.cylinder.flc.api.entity.CylinderInfo;
import com.yeejoin.amos.boot.module.cylinder.flc.api.entity.CylinderIntegrityData;
import com.yeejoin.amos.boot.module.cylinder.flc.api.entity.CylinderTags;
import com.yeejoin.amos.boot.module.cylinder.flc.api.entity.CylinderUnit;
import com.yeejoin.amos.boot.module.cylinder.flc.api.mapper.CylinderInfoMapper;
import com.yeejoin.amos.boot.module.cylinder.flc.api.service.ICylinderInfoService;
import com.yeejoin.amos.component.rule.RuleTrigger;
import com.yeejoin.amos.feign.privilege.Privilege;
import com.yeejoin.amos.feign.privilege.model.CompanyModel;
import com.yeejoin.amos.feign.systemctl.Systemctl;
import com.yeejoin.amos.feign.systemctl.model.RegionModel;
import lombok.extern.slf4j.Slf4j;
import net.javacrumbs.shedlock.spring.annotation.SchedulerLock;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.ParsedCardinality;
import org.elasticsearch.search.aggregations.metrics.Sum;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StopWatch;
import org.typroject.tyboot.core.foundation.utils.ValidationUtil;
import org.typroject.tyboot.core.rdbms.service.BaseService;

import java.io.IOException;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
import java.util.function.IntConsumer;
import java.util.stream.Collectors;

/**
 * 气瓶基本信息服务实现类
 *
 * @author system_generator
 * @date 2021-12-14
 */
@Service
@Slf4j
public class CylinderInfoServiceImpl extends BaseService<CylinderInfoDto, CylinderInfo, CylinderInfoMapper>
        implements ICylinderInfoService {

    public static final String REGION_CODE = "regionCode";
    public static final String FILLING_TIMES = "fillingTimes";
    public static final String FILLING_QUANTITY = "fillingQuantity";
    public static final String REGION_NAME = "regionName";
    public static final String FILLING_END_TIME = "fillingEndTime";
    public static final String TOTAL_FILLING_COUNT = "total_filling_count";
    public static final String SEQUENCE_NBR = "sequenceNbr";
    public static final String TOTAL_FILLING_QUANTITY = "total_filling_quantity";
    private static final String INDEX_NAME = "cylinder_filling";

    private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");

    @Autowired
    CylinderUnitServiceImpl cylinderUnitServiceImpl;

    @Autowired
    CylinderUnitDataServiceImpl cylinderUnitDataServiceImpl;

    @Autowired
    CylinderInfoDataServiceImpl cylinderInfoDataServiceImpl;

    @Autowired
    CylinderTagsServiceImpl cylinderTagsServiceImpl;

    @Autowired
    CylinderFillingServiceImpl cylinderFillingServiceImpl;

    @Autowired
    CylinderIntegrityDataServiceImpl cylinderIntegrityDataServiceImpl;

    @Autowired
    CylinderFillingRecordServiceImpl cylinderFillingRecordServiceImpl;

    @Autowired
    CylinderFillingCheckServiceImpl cylinderFillingCheckServiceImpl;

    @Autowired
    CylinderAreaDataServiceImpl cylinderAreaDataServiceImpl;

    @Autowired
    CylinderFillingUnloadDataServiceImpl cylinderFillingUnloadDataServiceImpl;

    @Autowired
    CylinderInfoDataUnitServiceImpl cylinderInfoDataUnitServiceImpl;

    @Autowired
    CylinderFillingDataUnitServiceImpl cylinderFillingDataUnitServiceImpl;

    @Autowired
    CylinderTagsDataUnitServiceImpl cylinderTagsDataUnitServiceImpl;

    @Autowired
    CylinderIntegrityDataUnitServiceImpl cylinderIntegrityDataUnitServiceImpl;

    @Autowired
    CylinderFillingCheckDataUnitServiceImpl cylinderFillingCheckDataUnitServiceImpl;

    @Autowired
    CylinderFillingUnloadDataUnitServiceImpl cylinderFillingUnloadDataUnitServiceImpl;

    @Autowired
    CylinderFillingRecordStatisticsUnitDayServiceImpl cylinderFillingRecordStatisticsUnitDayServiceImpl;

    @Autowired
    MsgLogServiceImpl msgLogService;
    @Autowired
    TzsAuthServiceImpl tzsAuthService;
    @Autowired
    RestHighLevelClient restHighLevelClient;
    @Autowired
    StartPlatformTokenService startPlatformTokenService;
    @Autowired
    CylinderInfoMapper cylinderInfoMapper;
    @Autowired
    ESCylinderInfoRepository esCylinderInfoRepository;
    @Autowired
    private RuleTrigger ruleTrigger;
    @Autowired
    private ScheduleMapper scheduleMapper;
    @Value("${cylinder-early-warning-packageId:气瓶监管/cylwarningmsg}")
    private String packageId;
    @Value("${cylinder-early-warning-packageId:气瓶消息预警/cylwarningmsg}")
    private String cylPackageId;

    private static Map<String, Object> assemblingHistogramData(List<Map<String, Object>> collect) {
        // 组装数据
        // {
        //     "seriesData": [
        //         {
        //             "data": [
        //                 3,
        //                 2,
        //                 2,
        //                 3,
        //                 1
        //             ],
        //             "name": "正常点",
        //             "stack": "正常点"
        //         },
        //         {
        //             "data": [
        //                 1,
        //                 1,
        //                 2,
        //                 1,
        //                 2
        //             ],
        //             "name": "异常点",
        //             "stack": "巡检点"
        //         }
        //     ],
        //     "axisData": [
        //         "A",
        //         "B",
        //         "C",
        //         "D",
        //         "E"
        //     ]
        // }
        Map<String, Object> result = new HashMap<>();
        List<Map<String, Object>> seriesData = new ArrayList<>();
        List<String> axisData = new ArrayList<>();
        List<String> seriesFillingTimesDataList = new ArrayList<>();
        List<String> seriesCumulativeFillingQuantityDataList = new ArrayList<>();
        collect.forEach(x -> {
            axisData.add(String.valueOf(x.get("regionName")));
            seriesFillingTimesDataList.add(String.valueOf(x.get("fillingTimes")));
            seriesCumulativeFillingQuantityDataList.add(String.valueOf(x.get(FILLING_QUANTITY)));
        });
        HashMap<String, Object> temMap1 = new HashMap<>();
        temMap1.put("data", seriesFillingTimesDataList);
        temMap1.put("name", "充装次数");
        // temMap1.put("stack","充装次数");
        HashMap<String, Object> temMap2 = new HashMap<>();
        temMap2.put("data", seriesCumulativeFillingQuantityDataList);
        temMap2.put("name", "累计充装量");
        // temMap2.put("stack","累计充装量");
        seriesData.add(temMap1);
        seriesData.add(temMap2);
        result.put("seriesData", seriesData);
        result.put("axisData", axisData);
        return result;
    }

    /**
     * 分页查询
     */
    public Page<CylinderInfoDto> queryForCylinderInfoPage(Page<CylinderInfoDto> page) {
        return queryForPage(page, null, false);
    }

    /**
     * 列表查询 示例
     */
    public List<CylinderInfoDto> queryForCylinderInfoList() {
        return queryForList("", false);
    }

    @Override
    public Map<String, String> queryNumAndOutOfDateNum(String unitCode) throws IOException {

        SearchRequest searchRequest = new SearchRequest("idx_biz_view_jg_all"); // 替换为你的索引名
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        // 构建查询条件
        searchSourceBuilder.query(
                QueryBuilders.boolQuery()
                        .must(QueryBuilders.termQuery("EQU_CATEGORY_CODE", "2300"))
                        .must(QueryBuilders.matchQuery("USE_UNIT_CREDIT_CODE ", unitCode)) // 假设你只匹配以50开头的ORG_BRANCH_CODE
        );
        searchSourceBuilder.size(1000);
        searchRequest.source(searchSourceBuilder);

        // 执行搜索并获取响应
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        SearchHits hits = searchResponse.getHits();
        int cylinderNum = hits.getHits().length;

        Map<String, String> result = baseMapper.queryNumAndOutOfDateNum(unitCode);
        result.put("cylinderNum", cylinderNum + "");
        return result;
    }

    /**
     * 获取上个月气瓶总量
     */
    public Integer getMonthInfoTotal(String regionCode) {
        return baseMapper.getMonthInfoTotal(regionCode);
    }

    /**
     * 获取上个月气瓶总量
     */
    public Integer getLastMonthInfoTotal(String regionCode) {
        return baseMapper.getLastMonthInfoTotal(regionCode);
    }

    /**
     * 获取上上个月气瓶总量
     */
    public Integer getMonthBeforeLastInfoTotal(String regionCode) {
        return baseMapper.getMonthBeforeLastInfoTotal(regionCode);
    }

    public Integer getInfoTotalByRegionCode(String regionCode) {
        return baseMapper.getInfoTotalByRegionCode(regionCode);
    }

    public Double queryIntegirtyByAppId(String appId) {
        return this.baseMapper.queryIntegirtyByAppId(appId);
    }

    public Integer getWarnNum(String code) {
        return baseMapper.getWarnNum(code);
    }

    public Integer getOutOfDateNum(String code) {
        return baseMapper.getOutOfDateNum(code);
    }

    /**
     * 按单位统计
     */
    @Transactional(rollbackFor = Exception.class)
    @Scheduled(cron = "0 0 2 * * ?")
    @SchedulerLock(name = "synFillingUnloadDataTask", lockAtMostFor = "PT6H")
    public void synFillingUnloadDataJob() {
        this.synFillingUnloadData();
    }

    @Override
    public void synFillingUnloadData() {
        cylinderFillingUnloadDataServiceImpl.remove(new LambdaQueryWrapper<>());
        countByRegion(regionModel -> {
            Calendar now = Calendar.getInstance();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            for (int i = 0; i < 30; i++) {
                now.add(Calendar.DATE, -1);
                CylinderFillingUnloadDataDto temp = new CylinderFillingUnloadDataDto();
                Double fillingSum = cylinderFillingRecordServiceImpl
                        .getFillingSum(String.valueOf(regionModel.getRegionCode()), now.getTime());
                Double unloadSum = 0d;
                temp.setFillingSum(fillingSum);
                temp.setRegionCode(String.valueOf(regionModel.getRegionCode()));
                temp.setStatisDate(now.getTime());
                temp.setStatisDateStr(sdf.format(now.getTime()));
                temp.setUnloadSum(unloadSum);
                cylinderFillingUnloadDataServiceImpl.createWithModel(temp);
            }
        });
    }

    /**
     * 按区域统计
     */
    @Transactional(rollbackFor = Exception.class)
    @Scheduled(cron = "0 0 4 * * ?")
    @SchedulerLock(name = "synAreaDataTask", lockAtMostFor = "PT60M")
    public void synAreaDataJob() {
        this.synAreaData();
    }

    @Override
    public void synAreaData() {
        cylinderAreaDataServiceImpl.remove(new LambdaQueryWrapper<>());
        countByRegion(regionModel -> {
            CylinderAreaDataDto temp = new CylinderAreaDataDto();
            temp.setAreaName(regionModel.getRegionName());
            String code = regionModel.getRegionCode() + "";
            Integer cylinderTotal = this.getInfoTotalByRegionCode(code);
            Integer cylinderUnitTotal = cylinderUnitServiceImpl.getUnitTotalByRegionCode(code);
            Integer cylinderUnitWarn = cylinderUnitServiceImpl.getWarnNum(code);
            Integer cylinderInfoWarn = this.getWarnNum(code);
            int warmTotal = cylinderUnitWarn + cylinderInfoWarn;
            String parentCode = "";
            if ("610000".equals(code)) {
                parentCode = "-1";
            } else if (!"00".equals(code.substring(4, 6))) {
                parentCode = code.substring(0, 4) + "00";
            } else {
                parentCode = "610000";
            }
            temp.setCylinderNum(Long.valueOf(cylinderTotal));
            temp.setParentRegionCode(parentCode);
            temp.setRegionCode(regionModel.getRegionCode() + "");
            temp.setUnitNum(Long.valueOf(cylinderUnitTotal));
            temp.setWarnNum((long) warmTotal);
            temp.setLevel(regionModel.getLevel().trim());
            temp.setLatitude(regionModel.getLatitude());
            temp.setLongitude(regionModel.getLongitude());
            temp.setOutOfDateNum(this.getOutOfDateNum(code));
            cylinderAreaDataServiceImpl.createWithModel(temp);
        });
    }

    /**
     * 按区域统计
     */
    @Transactional(rollbackFor = Exception.class)
    @Scheduled(cron = "0 0 2 * * ?")
    @SchedulerLock(name = "addIntegrityDataTask", lockAtMostFor = "PT60M")
    public void addIntegrityDataJob() {
        this.addIntegrityData();
    }

    @Override
    public void addIntegrityData() {
        System.out.println("====================数据完整性开始============================");
        cylinderIntegrityDataServiceImpl.remove(new LambdaQueryWrapper<CylinderIntegrityData>());
        countByRegion(regionModel -> {
            List<CylinderUnit> unitlist = cylinderUnitServiceImpl.list(new LambdaQueryWrapper<CylinderUnit>()
                    .like(CylinderUnit::getRegionCode, regionModel.getRegionCode()));
            List<CylinderIntegrityDataDto> tempList = new LinkedList<>();
            System.out.println(
                    "====================regioncode: " + regionModel.getRegionCode() + "============================");
            unitlist.forEach(t -> {
                System.out.println("====================appId: " + t.getAppId() + "============================");
                CylinderIntegrityDataDto temp = new CylinderIntegrityDataDto();
                String appId = t.getAppId();
                Double totalIntegirty = 0d;
                // tz_cylinder_info
                Double unitIntegirty = t.getIntegrity();
                unitIntegirty = unitIntegirty == null ? 0d : unitIntegirty;
                // tz_cylinder_tags
                Double tagIntegirty = cylinderTagsServiceImpl.queryIntegirtyByAppId(appId);
                tagIntegirty = tagIntegirty == null ? 0d : tagIntegirty;
                // tz_cylinder_info
                Double infoIntegirty = this.queryIntegirtyByAppId(appId);
                infoIntegirty = infoIntegirty == null ? 0d : infoIntegirty;
                // tz_cylinder_filling_record 30天内
                Double recordIntegirty = cylinderFillingRecordServiceImpl.queryIntegirtyByAppId(appId);
                recordIntegirty = recordIntegirty == null ? 0d : recordIntegirty;
                // tz_cylinder_filling 30天内ji
                Double fillingIntegirty = cylinderFillingServiceImpl.queryIntegirtyByAppId(appId);
                fillingIntegirty = fillingIntegirty == null ? 0d : fillingIntegirty;
                // tz_cylinder_filling_check 30天内
                Double checkIntegirty = cylinderFillingCheckServiceImpl.queryIntegirtyByAppId(appId);
                checkIntegirty = checkIntegirty == null ? 0d : checkIntegirty;
                totalIntegirty = (unitIntegirty + tagIntegirty + infoIntegirty + recordIntegirty + fillingIntegirty
                        + checkIntegirty) / 6;
                BigDecimal bg = new BigDecimal(totalIntegirty);
                totalIntegirty = bg.setScale(4, BigDecimal.ROUND_HALF_UP).doubleValue() * 100;
                temp.setAppId(appId);
                temp.setRegionCode(String.valueOf(regionModel.getRegionCode()));
                temp.setIntegrity(totalIntegirty);
                temp.setUnitName(t.getUnitName());
                // 判断list 是否达到5个，如果达到则对比最后一个进行替换
                if (tempList.size() == 5) {
                    if (tempList.get(0).getIntegrity() < totalIntegirty) {
                        tempList.set(0, temp);
                    }
                } else {
                    tempList.add(temp);
                }
                tempList.sort(Comparator.comparing(CylinderIntegrityDataDto::getIntegrity));
            });
            tempList.forEach(t -> {
                System.out.println("====================unitName: " + t.getUnitName() + "============================");
                t.setUpdateTime(new Date());
                cylinderIntegrityDataServiceImpl.createWithModel(t);
            });
        });
        System.out.println("====================数据完整性结束============================");
    }

    /**
     * 企业总量按区域统计
     */

    @Transactional(rollbackFor = Exception.class)
    @Scheduled(cron = "0 0 2 * * ?")
    @SchedulerLock(name = "cylinderUnitInfoTask", lockAtMostFor = "PT60M")
    public void getCylinderUnitInfoJob() {
        this.getCylinderUnitInfo();
    }

    @Override
    public void getCylinderUnitInfo() {
        cylinderUnitDataServiceImpl.remove(new LambdaQueryWrapper<>());
        countByRegion(regionModel -> {
            String regionCode = String.valueOf(regionModel.getRegionCode());
            Integer cylinderUnitTotal = cylinderUnitServiceImpl.getUnitTotalByRegionCode(regionCode);
            Integer thisMonthUnitTotal = cylinderUnitServiceImpl.getThisMonthUnitTotalByRegionCode(regionCode);
            Double lastUnitTotal = Double.valueOf(cylinderUnitServiceImpl.getLastMonthUnitTotal(regionCode));
            Double monthUnitLastInfo = Double.valueOf(cylinderUnitServiceImpl.getMonthBeforeLastUnitTotal(regionCode));
            double changeNum = thisMonthUnitTotal - lastUnitTotal;
            double percent = 0d;
            if (monthUnitLastInfo != 0) {
                percent = (lastUnitTotal - monthUnitLastInfo) / monthUnitLastInfo;
                BigDecimal bg = new BigDecimal(percent);
                percent = bg.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
            } else {
                percent = 0;
                BigDecimal bg = new BigDecimal(percent);
                percent = bg.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
            }
            CylinderUnitDataDto temp = new CylinderUnitDataDto();
            temp.setChangeSum((long) changeNum);
            temp.setChangePercent((int) (percent * 100) + "");
            temp.setRegionCode(regionCode);
            temp.setTotalSum((long) cylinderUnitTotal);
            temp.setUpdateTime(new Date());
            cylinderUnitDataServiceImpl.createWithModel(temp);
        });
    }

    /**
     * 气瓶总量按区域统计
     */
    @Transactional(rollbackFor = Exception.class)
    @Scheduled(cron = "0 0 2 * * ?")
    @SchedulerLock(name = "cylinderInfoTask", lockAtMostFor = "PT6H")
    public void getCylinderInfoJob() {
        this.getCylinderInfo();
    }

    @Override
    public void getCylinderInfo() {
        cylinderInfoDataServiceImpl.remove(new LambdaQueryWrapper<>());
        countByRegion(regionModel -> {
            CylinderInfoDataDto temp = new CylinderInfoDataDto();
            String regionCode = String.valueOf(regionModel.getRegionCode());
            Integer cylinderTotal = this.getInfoTotalByRegionCode(regionCode);// 当前总数
            Double InfoTotal = Double.valueOf(this.getMonthInfoTotal(regionCode));
            Double lastInfoTotal = Double.valueOf(this.getLastMonthInfoTotal(regionCode));// 上月总数
            Double monthBeforeLastInfo = Double.valueOf(this.getMonthBeforeLastInfoTotal(regionCode));// 上上月总数
            double percent = 0d;
            if (monthBeforeLastInfo != 0) {
                percent = (lastInfoTotal - monthBeforeLastInfo) / monthBeforeLastInfo;
                BigDecimal bg = new BigDecimal(percent);
                percent = bg.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
            } else {
                percent = 0;
                BigDecimal bg = new BigDecimal(percent);
                percent = bg.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
            }
            temp.setChangeSum((long) (InfoTotal - lastInfoTotal));
            temp.setChangePercent((int) (percent * 100) + "");
            temp.setRegionCode(regionCode);
            temp.setTotalSum((long) cylinderTotal);
            cylinderInfoDataServiceImpl.createWithModel(temp);
        });
    }

    /**
     * 按单位统计
     */
    @Transactional(rollbackFor = Exception.class)
    @Scheduled(cron = "0 0 2 * * ?")
    @SchedulerLock(name = "synUnitCylinderInfoDataTask", lockAtMostFor = "PT6H")
    public void synUnitCylinderInfoDataJob() {
        this.synUnitCylinderInfoData();
    }

    @Override
    public void synUnitCylinderInfoData() {
        cylinderInfoDataUnitServiceImpl.remove(new LambdaQueryWrapper<>());
        countByUnit(cylinderUnit -> {
            CylinderInfoDataUnitDto temp = new CylinderInfoDataUnitDto();
            temp.setAppId(cylinderUnit.getAppId());
            int count = this
                    .count(new LambdaQueryWrapper<CylinderInfo>().eq(CylinderInfo::getAppId, cylinderUnit.getAppId()));// 当前总数
            temp.setTotalSum((long) count);
            Double month = Double.valueOf(baseMapper.getMonthInfoTotalUnit(cylinderUnit.getAppId()));
            Double thismonth = Double.valueOf(baseMapper.getLastMonthInfoTotalUnit(cylinderUnit.getAppId()));// 上月总数
            Double lastmonth = Double.valueOf(baseMapper.getMonthBeforeLastInfoTotalUnit(cylinderUnit.getAppId()));// 上上月总数
            double percent = 0d;
            if (lastmonth != 0) {
                percent = (thismonth - lastmonth) / lastmonth;
                BigDecimal bg = new BigDecimal(percent);
                percent = bg.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
            } else {
                percent = 0;
                BigDecimal bg = new BigDecimal(percent);
                percent = bg.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
            }
            temp.setChangeSum((long) (month - thismonth));
            temp.setChangePercent((int) (percent * 100) + "");
            cylinderInfoDataUnitServiceImpl.createWithModel(temp);
        });
    }

    /**
     * 充装量按单位和月统计
     */
    @Transactional(rollbackFor = Exception.class)
    @Scheduled(cron = "0 0 2 * * ?")
    @SchedulerLock(name = "synUnitCylinderFillingDataTask", lockAtMostFor = "PT6H")
    public void synUnitCylinderFillingDataJob() {
        this.synUnitCylinderFillingData();
    }

    @Override
    public void synUnitCylinderFillingData() {
        DecimalFormat df = new DecimalFormat("#0.00");
        cylinderFillingDataUnitServiceImpl.remove(new LambdaQueryWrapper<>());
        countByUnit(cylinderUnit -> {
            // 按照月份 获取数据 取一年数据
            Calendar calendar = Calendar.getInstance();
            // 按月份获取充装量
            // 当月与上月 对比获取数据
            for (int i = 0; i < 12; i++) {
                // 获取当月数据
                CylinderFillingDataUnitDto temp = new CylinderFillingDataUnitDto();
                String year = calendar.get(Calendar.YEAR) + "";
                int month = calendar.get(Calendar.MONTH) + 1;
                String monthStr = month < 10 ? "0" + month : month + "";
                temp.setFillingYear(year);
                temp.setFillingMonth(monthStr);
                temp.setFillingDate(year + "-" + monthStr);
                // 本月
                Double thisMonth = cylinderFillingRecordServiceImpl.getFillingSumByMonth(cylinderUnit.getAppId(),
                        calendar.getTime());
                temp.setTotalSum(Double.valueOf(df.format(thisMonth)));
                calendar.add(Calendar.MONTH, -1);
                // 上月
                Double lastMonth = cylinderFillingRecordServiceImpl.getFillingSumByMonth(cylinderUnit.getAppId(),
                        calendar.getTime());
                double percent = 0d;
                if (lastMonth != 0) {
                    percent = (thisMonth - lastMonth) / lastMonth;
                    BigDecimal bg = new BigDecimal(percent);
                    percent = bg.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
                }
                temp.setChangePercent((int) (percent * 100) + "");
                temp.setAppId(cylinderUnit.getAppId());
                temp.setChangeSum(thisMonth - lastMonth);
                temp.setChangeSum(Double.valueOf(df.format(temp.getChangeSum())));
                cylinderFillingDataUnitServiceImpl.createWithModel(temp);
            }
        });
    }

    /**
     * 按单位统计
     */
    @Transactional(rollbackFor = Exception.class)
    @Scheduled(cron = "0 0 2 * * ?")
    @SchedulerLock(name = "synUnitCylinderTagsDataTask", lockAtMostFor = "PT6H")
    public void synUnitCylinderTagsDataJob() {
        this.synUnitCylinderTagsData();
    }

    @Override
    public void synUnitCylinderTagsData() {
        cylinderTagsDataUnitServiceImpl.remove(new LambdaQueryWrapper<>());
        countByUnit(cylinderUnit -> {
            CylinderTagsDataUnitDto temp = new CylinderTagsDataUnitDto();
            temp.setAppId(cylinderUnit.getAppId());
            int cylinder = this
                    .count(new LambdaQueryWrapper<CylinderInfo>().eq(CylinderInfo::getAppId, cylinderUnit.getAppId()));
            int tags = cylinderTagsServiceImpl
                    .count(new LambdaQueryWrapper<CylinderTags>().eq(CylinderTags::getAppId, cylinderUnit.getAppId()));
            String tagPercent = "";
            String cylinderPercent = "";
            if (tags != 0) {
                double tagPercentDouble = (double) cylinder / (double) tags;
                DecimalFormat df = new DecimalFormat("##.00%");
                if (Math.abs(tagPercentDouble) < 0.0000000000001) {
                    tagPercent = "0.00%";
                } else {
                    tagPercent = df.format(tagPercentDouble);
                }
                double cylinderPercentDouble = (double) tags / (double) cylinder;
                if (Math.abs(cylinderPercentDouble) < 0.0000000000001) {
                    cylinderPercent = "0.00%";
                } else {
                    cylinderPercent =Math.abs(cylinderPercentDouble) > 1 ? "100%" : df.format(cylinderPercentDouble);
                }
            }
            temp.setCylinderSum((long) cylinder);
            temp.setTagsSum((long) tags);
            temp.setTagPercent(tagPercent);
            temp.setCylinderPercent(cylinderPercent);
            cylinderTagsDataUnitServiceImpl.createWithModel(temp);
        });
    }

    /**
     * 按单位统计
     */

    @Transactional(rollbackFor = Exception.class)
    @Scheduled(cron = "0 0 2 * * ?")
    @SchedulerLock(name = "synUnitIntegrityDataTask", lockAtMostFor = "PT6H")
    public void synUnitIntegrityDataJob() {
        this.synUnitIntegrityData();
    }

    @Override
    public void synUnitIntegrityData() {
        cylinderIntegrityDataUnitServiceImpl.remove(new LambdaQueryWrapper<>());
        countByUnit(cylinderUnit -> {
            // 企业信息
            CylinderIntegrityDataUnitDto uninInfo = new CylinderIntegrityDataUnitDto();
            uninInfo.setAppId(cylinderUnit.getAppId());
            uninInfo.setDataType("企业信息");
            Double unitIntegirty = cylinderUnit.getIntegrity();
            uninInfo.setIntegrity(unitIntegirty);
            cylinderIntegrityDataUnitServiceImpl.createWithModel(uninInfo);
            // 气瓶基本信息
            CylinderIntegrityDataUnitDto cylinderInfo = new CylinderIntegrityDataUnitDto();
            cylinderInfo.setAppId(cylinderUnit.getAppId());
            cylinderInfo.setDataType("气瓶基本信息");
            Double cylinderIntegirty = this.queryIntegirtyByAppId(cylinderUnit.getAppId());
            cylinderInfo.setIntegrity(cylinderIntegirty);
            cylinderIntegrityDataUnitServiceImpl.createWithModel(cylinderInfo);
            // 气瓶标签信息
            CylinderIntegrityDataUnitDto cylinderTag = new CylinderIntegrityDataUnitDto();
            cylinderTag.setAppId(cylinderUnit.getAppId());
            cylinderTag.setDataType("气瓶标签信息");
            Double tagIntegirty = cylinderTagsServiceImpl.queryIntegirtyByAppId(cylinderUnit.getAppId());
            cylinderTag.setIntegrity(tagIntegirty);
            cylinderIntegrityDataUnitServiceImpl.createWithModel(cylinderTag);
            // 气瓶检验信息
            CylinderIntegrityDataUnitDto cylinderInspection = new CylinderIntegrityDataUnitDto();
            cylinderInspection.setAppId(cylinderUnit.getAppId());
            cylinderInspection.setDataType("气瓶检验信息");
            cylinderInspection.setIntegrity(0d);
            cylinderIntegrityDataUnitServiceImpl.createWithModel(cylinderInspection);
            // 充装前检查
            CylinderIntegrityDataUnitDto cylinderFilling = new CylinderIntegrityDataUnitDto();
            cylinderFilling.setAppId(cylinderUnit.getAppId());
            cylinderFilling.setDataType("充装前检查");
            Double fillingIntegirty = cylinderFillingServiceImpl.queryIntegirtyByAppId(cylinderUnit.getAppId());
            cylinderFilling.setIntegrity(fillingIntegirty);
            cylinderIntegrityDataUnitServiceImpl.createWithModel(cylinderFilling);
            // 充装信息
            CylinderIntegrityDataUnitDto cylinderRecord = new CylinderIntegrityDataUnitDto();
            cylinderRecord.setAppId(cylinderUnit.getAppId());
            cylinderRecord.setDataType("充装信息");
            Double recordIntegirty = cylinderFillingRecordServiceImpl.queryIntegirtyByAppId(cylinderUnit.getAppId());
            cylinderRecord.setIntegrity(recordIntegirty);
            cylinderIntegrityDataUnitServiceImpl.createWithModel(cylinderRecord);
            // 充装后复查
            CylinderIntegrityDataUnitDto cylinderCheck = new CylinderIntegrityDataUnitDto();
            cylinderCheck.setAppId(cylinderUnit.getAppId());
            cylinderCheck.setDataType("充装后复查");
            Double checkIntegirty = cylinderFillingCheckServiceImpl.queryIntegirtyByAppId(cylinderUnit.getAppId());
            cylinderCheck.setIntegrity(checkIntegirty);
            cylinderIntegrityDataUnitServiceImpl.createWithModel(cylinderCheck);
        });
    }

    /**
     * 充装详情按单位统计
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    @Scheduled(cron = "0 0 2 * * ?")
    @SchedulerLock(name = "synUnitFillingCheckDataTask", lockAtMostFor = "PT6H")
    public void synUnitFillingCheckDataJob() {
        this.fillingCheckDataSynHandler();
    }

    @Override
    public void fillingCheckDataSynHandler() {
        countByUnit(cylinderUnit -> {
            List<CylinderFillingCheckDataUnitDto> allCylinderFillingCheckDataList =
                    cylinderFillingCheckDataUnitServiceImpl.queryCylinderFillingCheckListByParam(cylinderUnit.getAppId(), null);
            // 按照月份 获取数据 取一年数据
            Calendar c = Calendar.getInstance();
            // 第一次查询到该appId对应的统计数据为空，则计算过去一年12个月的数据统计
            if (ValidationUtil.isEmpty(allCylinderFillingCheckDataList)) {
                // 按月份获取充装量
                // 当月与上月 对比获取数据
                for (int i = 0; i < 12; i++) {
                    // 获取当月数据
                    CylinderFillingCheckDataUnitDto temp = new CylinderFillingCheckDataUnitDto();
                    calcCylinderFillingCheckDataUnitData(cylinderUnit, c, temp);
                    c.add(Calendar.MONTH, -1);
                    cylinderFillingCheckDataUnitServiceImpl.createWithModel(temp);
                }
            } else {
                // 如果已经有该appId对应数据，则直接更新当前月的数据即可
                Calendar current = Calendar.getInstance();
                String year = current.get(Calendar.YEAR) + "";
                int month = current.get(Calendar.MONTH) + 1;
                String monthStr = month < 10 ? "0" + month : month + "";
                List<CylinderFillingCheckDataUnitDto> existDataDtoList =
                        cylinderFillingCheckDataUnitServiceImpl.queryCylinderFillingCheckListByParam(cylinderUnit.getAppId(), year + "-" + monthStr);
                CylinderFillingCheckDataUnitDto tempDto = new CylinderFillingCheckDataUnitDto();
                if (!ValidationUtil.isEmpty(existDataDtoList) && !ValidationUtil.isEmpty(existDataDtoList.get(0))) {
                    tempDto = existDataDtoList.get(0);
                    calcCylinderFillingCheckDataUnitData(cylinderUnit, current, tempDto);
                    cylinderFillingCheckDataUnitServiceImpl.updateWithModel(tempDto);
                } else if (ValidationUtil.isEmpty(existDataDtoList)) {
                    // 处理当前月第一天还没数据情况
                    calcCylinderFillingCheckDataUnitData(cylinderUnit, current, tempDto);
                    cylinderFillingCheckDataUnitServiceImpl.createWithModel(tempDto);
                }
            }
        });
    }

    /**
     * 计算当前单位的充装检查数据统计
     *
     * @param cylinderUnit
     * @param calender
     * @param cylinderFillingCheckDataUnitDto
     */
    public void calcCylinderFillingCheckDataUnitData(CylinderUnit cylinderUnit, Calendar calender, CylinderFillingCheckDataUnitDto cylinderFillingCheckDataUnitDto) {
        DecimalFormat df = new DecimalFormat("#0.00");
        String year = calender.get(Calendar.YEAR) + "";
        int month = calender.get(Calendar.MONTH) + 1;
        String monthStr = month < 10 ? "0" + month : month + "";
        cylinderFillingCheckDataUnitDto.setFillingMonth(monthStr);
        cylinderFillingCheckDataUnitDto.setFillingYear(year);
        cylinderFillingCheckDataUnitDto.setFillingDate(year + "-" + monthStr);
        Integer countThisMonth = cylinderFillingRecordServiceImpl
                .getFillingCountByMonth(cylinderUnit.getAppId(), calender.getTime());
        cylinderFillingCheckDataUnitDto.setTotalSum((long) countThisMonth);
        // 获取本月数据
        Integer fillingCount = cylinderFillingServiceImpl.getFillingCountByMonth(cylinderUnit.getAppId(),
                calender.getTime());
        Integer fillingCheckCount = cylinderFillingCheckServiceImpl
                .getFillingCountByMonth(cylinderUnit.getAppId(), calender.getTime());
        // 充装前检查率：充装前检查次数/充装次数
        double before = 0d;
        if (countThisMonth != 0) {
            before = (double) (fillingCount) / (double) countThisMonth;
        }
        cylinderFillingCheckDataUnitDto.setFillingCount((long) fillingCount);
        cylinderFillingCheckDataUnitDto.setFillingPercent(Double.valueOf(df.format(before * 100)));
        // 充装后检查率：充装后检查次数/充装次数
        double after = 0d;
        if (countThisMonth != 0) {
            after = (double) (fillingCheckCount) / (double) countThisMonth;
        }
        cylinderFillingCheckDataUnitDto.setFillingCheckCount((long) fillingCheckCount);
        cylinderFillingCheckDataUnitDto.setFillingCheckPercent(Double.valueOf(df.format(after * 100)));
        // 充装合格率：充装前检查合格次数+充装后检查合格次数/2*充装次数
        double passed = 0d;
        // 充装前检查合格次数
        Integer fillingPassedCount = cylinderFillingServiceImpl
                .getFillingPassedCountByMonth(cylinderUnit.getAppId(), calender.getTime());
        // 充装后检查合格次数
        Integer fillingCheckPassedCount = cylinderFillingCheckServiceImpl
                .getFillingPassedCountByMonth(cylinderUnit.getAppId(), calender.getTime());
        if (countThisMonth != 0) {
            passed = ((double) (fillingPassedCount) + (double) fillingCheckPassedCount)
                    / (double) (2 * countThisMonth);
        }
        cylinderFillingCheckDataUnitDto.setFillingPassedCount((long) (fillingPassedCount + fillingCheckPassedCount));
        cylinderFillingCheckDataUnitDto.setTotalSumDouble((long) 2 * countThisMonth);
        cylinderFillingCheckDataUnitDto.setFillingPassedPercent(Double.valueOf(df.format(passed * 100)));
        cylinderFillingCheckDataUnitDto.setAppId(cylinderUnit.getAppId());
    }

    @Override
    public void oneDayEnterpriseCylinderDataRefreshTask(String day, String appId) {
        try {
            SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
            Date date = formatter.parse(day);
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(date);
            if (ValidationUtil.isEmpty(appId)) {// 刷入所有企业数据
                CompletableFuture.runAsync(() -> countByUnit(cylinderUnit -> {
                    CylinderFillingRecordStatisticsUnitDayDto unitDay = new CylinderFillingRecordStatisticsUnitDayDto();
                    calcCylinderFillingCheckDataUnitDataWithDay(cylinderUnit, calendar, unitDay);
                    checkFillingRecordStatisticsUnitDay(unitDay);
                    cylinderFillingRecordStatisticsUnitDayServiceImpl.createWithModel(unitDay);
                }));
            } else {// 刷入指定企业数据
                CylinderUnit targetUnit = cylinderUnitServiceImpl.list().stream()
                        .filter(unit -> unit.getAppId().equals(appId))
                        .findFirst()
                        .orElseThrow(() -> new NoSuchElementException("没有对应appid的企业"));
                CylinderFillingRecordStatisticsUnitDayDto unitDay = new CylinderFillingRecordStatisticsUnitDayDto();
                calcCylinderFillingCheckDataUnitDataWithDay(targetUnit, calendar, unitDay);
                checkFillingRecordStatisticsUnitDay(unitDay);
                cylinderFillingRecordStatisticsUnitDayServiceImpl.createWithModel(unitDay);
            }
        } catch (ParseException e) {
            log.error("日期解析错误", e);
        } catch (NoSuchElementException e) {
            log.error("未找到指定appid的企业", e);
        } catch (Exception e) {
            log.error("刷新企业气瓶数据任务出错", e);
        }
    }

    /**
     * 检查CylinderFillingRecordStatisticsUnitDayDto是否已经存在数据，若存在，则删除该条记录
     * 检查维度：appId + fillingDate
     */
    public void checkFillingRecordStatisticsUnitDay(CylinderFillingRecordStatisticsUnitDayDto unitDay) {
        LambdaQueryWrapper<CylinderFillingRecordStatisticsUnitDay> queryWrapper = new QueryWrapper<CylinderFillingRecordStatisticsUnitDay>().lambda()
                .eq(CylinderFillingRecordStatisticsUnitDay::getAppId, unitDay.getAppId())
                .eq(CylinderFillingRecordStatisticsUnitDay::getFillingDate, unitDay.getFillingDate());
        CylinderFillingRecordStatisticsUnitDay one = cylinderFillingRecordStatisticsUnitDayServiceImpl.getOne(queryWrapper);
        if (!ValidationUtil.isEmpty(one)){
            cylinderFillingRecordStatisticsUnitDayServiceImpl.deleteBySeq(one.getSequenceNbr());
        }
    }

    @Override
    public void fillingRecordDataSynHandler() {
        CompletableFuture.runAsync(() -> cylinderFillingRecordStatisticsUnitDayServiceImpl.remove(new LambdaQueryWrapper<>()))
                .thenRunAsync(() -> countByUnit(cylinderUnit -> {
                    Calendar calendar = Calendar.getInstance();
                    for (int i = 0; i <= 30; i++) {
                        CylinderFillingRecordStatisticsUnitDayDto unitDay = new CylinderFillingRecordStatisticsUnitDayDto();
                        calendar.add(Calendar.DAY_OF_MONTH, -1);
                        calcCylinderFillingCheckDataUnitDataWithDay(cylinderUnit, calendar, unitDay);
                        cylinderFillingRecordStatisticsUnitDayServiceImpl.createWithModel(unitDay);
                    }
                }));
    }

    /**
     * 计算当前单位的充装检查数据统计-以日为单位
     *
     * @param cylinderUnit
     * @param calender
     * @param unitDay
     */
    public void calcCylinderFillingCheckDataUnitDataWithDay(CylinderUnit cylinderUnit, Calendar calender, CylinderFillingRecordStatisticsUnitDayDto unitDay) {

        unitDay.setFillingDate(calender.getTime());
        unitDay.setAppId(cylinderUnit.getAppId());
        unitDay.setRegionCode(cylinderUnit.getRegionCode());
        
        // 当天充装次数
        Integer countThisDay = cylinderFillingRecordServiceImpl
                .getFillingCountByDay(cylinderUnit.getAppId(), calender.getTime());
        unitDay.setTotalSum(countThisDay);
        
        // 当前充装前检查数
        Integer fillingBeforeCount = cylinderFillingServiceImpl.getFillingCountByDay(cylinderUnit.getAppId(),
                calender.getTime());
        unitDay.setFillingBeforeSum(fillingBeforeCount);

        // 当天充装后检查数
        Integer fillingAfterCount = cylinderFillingCheckServiceImpl
                .getFillingCountByDay(cylinderUnit.getAppId(), calender.getTime());
        unitDay.setFillingAfterSum(fillingAfterCount);

        // 当天充装量
        Double fillingSumByDate= cylinderFillingRecordServiceImpl
                .getFillingSumByDate(cylinderUnit.getAppId(), calender.getTime());
        unitDay.setFillingQuantity(BigDecimal.valueOf(fillingSumByDate));

        // 充装检查不合格数
        Integer uniqueFillingBeforeSum =  cylinderFillingRecordServiceImpl
                .getFillingUnqualifiedCountByDay(cylinderUnit.getAppId(), calender.getTime());
        unitDay.setFillingNotPassedCount(uniqueFillingBeforeSum);

    }

    /**
     * 充装量、卸液量按单位统计
     */
    @Transactional(rollbackFor = Exception.class)
    @Scheduled(cron = "0 0 2 * * ?")
    @SchedulerLock(name = "synFillingUnloadUnitDataTask", lockAtMostFor = "PT6H")
    public void synFillingUnloadUnitDataJob() {
        this.synFillingUnloadUnitData();
    }

    @Override
    public void synFillingUnloadUnitData() {
        cylinderFillingUnloadDataUnitServiceImpl.remove(new LambdaQueryWrapper<>());
        countByUnit(cylinderUnit -> {
            Calendar now = Calendar.getInstance();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            for (int i = 0; i < 30; i++) {
                now.add(Calendar.DATE, -1);
                CylinderFillingUnloadDataUnitDto temp = new CylinderFillingUnloadDataUnitDto();
                Double fillingSum = cylinderFillingRecordServiceImpl.getFillingSumByDate(cylinderUnit.getAppId(),
                        now.getTime());
                Double unloadSum = 0d;
                temp.setFillingSum(fillingSum);
                temp.setAppId(cylinderUnit.getAppId());
                temp.setStatisDate(now.getTime());
                temp.setStatisDateStr(sdf.format(now.getTime()));
                temp.setUnloadSum(unloadSum);
                cylinderFillingUnloadDataUnitServiceImpl.createWithModel(temp);
            }
        });
    }

    @Scheduled(cron = "${tzs.cylinder.info.cron}")
    @SchedulerLock(name = "cylinderInfoToESTask", lockAtMostFor = "PT6H")
    public void setTimeSaveCylinderInfoToES() {
        Page<CylinderInfoDto> cylinderInfoPage = new Page<>();
        Integer count = getInfoTotal();
        Integer times = 0;
        if (count != 0) {
            times = count / 1000;
            int last = count % 1000;
            if (last > 0) {
                times++;
            }
        }
        for (int i = 0; i <= times; i++) {
            cylinderInfoPage.setCurrent(0);
            cylinderInfoPage.setSize(1000);
            cylinderInfoPage = cylinderInfoMapper.getCyinderInfoList(cylinderInfoPage);
            if (!ObjectUtils.isEmpty(cylinderInfoPage)) {
                saveCylinderInfo2ES(cylinderInfoPage.getRecords());
            }
//			for (CylinderInfoDto ci : cylinderInfoPage.getRecords()) {
//				saveCylinderInfoToES(ci);
//			}
        }
    }

    @Override
    public void saveCylinderInfo2ES(List<CylinderInfoDto> records) {
        List<ESCylinderInfoDto> esCylinderInfoDto = new ArrayList<>();
        List<String> ids = Lists.newArrayList();
        for (CylinderInfoDto record : records) {
            ESCylinderInfoDto esCylinderInfo = new ESCylinderInfoDto();
            BeanUtils.copyProperties(record, esCylinderInfo);
            esCylinderInfoDto.add(esCylinderInfo);
            try {
                esCylinderInfo.setInspectionDateMs(ObjectUtils.isEmpty(esCylinderInfo.getInspectionDate()) ? 0L : DateUtils.dateParse(esCylinderInfo.getInspectionDate(), DateUtils.DATE_TIME_PATTERN).getTime());
            } catch (ParseException e) {
                throw new RuntimeException(e);
            }
            ids.add(String.valueOf(record.getSequenceNbr()));
        }
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        esCylinderInfoRepository.saveAll(esCylinderInfoDto);
        stopWatch.stop();
        if (log.isInfoEnabled()) {
            log.info("批量存入es耗时：{} 秒", stopWatch.getTotalTimeSeconds());
        }
        StopWatch stopWatch1 = new StopWatch();
        stopWatch1.start();
        cylinderInfoMapper.updateEsCylinderInfoStatus(ids);
        stopWatch1.stop();
        if (log.isInfoEnabled()) {
            log.info("批量更新业务数据耗时：{} 秒", stopWatch1.getTotalTimeSeconds());
        }
    }

    public void createCylinderInfo2ES(CylinderInfoDto cylinderInfoDto) {
        List<ESCylinderInfoDto> esCylinderInfoDto = new ArrayList<>();
        ESCylinderInfoDto esCylinderInfo = new ESCylinderInfoDto();
        BeanUtils.copyProperties(cylinderInfoDto, esCylinderInfo);
        esCylinderInfoDto.add(esCylinderInfo);
        try {
            esCylinderInfo.setInspectionDateMs(ObjectUtils.isEmpty(esCylinderInfo.getInspectionDate()) ? 0L : DateUtils.dateParse(esCylinderInfo.getInspectionDate(), DateUtils.DATE_TIME_PATTERN).getTime());
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }

        List<String> ids = Lists.newArrayList();

        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        esCylinderInfoRepository.saveAll(esCylinderInfoDto);
        stopWatch.stop();
        if (log.isInfoEnabled()) {
            log.info("存入es耗时：{} 秒", stopWatch.getTotalTimeSeconds());
        }
        StopWatch stopWatch1 = new StopWatch();
        stopWatch1.start();
        cylinderInfoMapper.updateEsCylinderInfoStatus(ids);
        stopWatch1.stop();
        if (log.isInfoEnabled()) {
            log.info("更新业务数据耗时：{} 秒", stopWatch1.getTotalTimeSeconds());
        }
    }

    @Override
    public Integer getInfoTotal() {
        return cylinderInfoMapper.getInfoTotal();
    }

    @Override
    public ESCylinderInfoDto saveCylinderInfoToES(CylinderInfoDto ci) {
        ESCylinderInfoDto esCylinderInfoDto = new ESCylinderInfoDto();
        BeanUtils.copyProperties(ci, esCylinderInfoDto);
        ESCylinderInfoDto saveResult = esCylinderInfoRepository.save(esCylinderInfoDto);
        if (!ObjectUtils.isEmpty(saveResult)) {
            // 同步到es后修改
            CylinderInfo cylinderInfo = new CylinderInfo();
            cylinderInfo.setIsNotEs("1");
            cylinderInfoMapper.update(cylinderInfo, new QueryWrapper<CylinderInfo>().eq("sequence_nbr", ci.getSequenceNbr()));
        }
        return saveResult;
    }

    @Override
    public Page<ESCylinderInfoDto> queryByKeys(CylinderInfoDto cylinderInfoDto, int pageNum, int pageSize) {

        Page<ESCylinderInfoDto> result = new Page<ESCylinderInfoDto>(pageNum, pageSize);
        SearchRequest request = new SearchRequest();
        request.indices("cylinder_info");

        // 通用匹配规则，条件构建
        boolean flag = true;

        SearchSourceBuilder builder = new SearchSourceBuilder();
        BoolQueryBuilder boolMust = QueryBuilders.boolQuery();

        // 匹配统一信用代码
        if (!ObjectUtils.isEmpty(cylinderInfoDto.getCreditCode())) {
            flag = false;
            BoolQueryBuilder meBuilder = QueryBuilders.boolQuery();
            meBuilder.must(QueryBuilders.matchQuery("creditCode", cylinderInfoDto.getCreditCode()));
            boolMust.must(meBuilder);
        }

        // 匹配RegionCode
        if (!ObjectUtils.isEmpty(cylinderInfoDto.getRegionCode())) {
            flag = false;
            BoolQueryBuilder appIdBuilder = QueryBuilders.boolQuery();
            appIdBuilder.must(QueryBuilders.wildcardQuery("regionCode", "*" + cylinderInfoDto.getRegionCode() + "*"));
            boolMust.should(appIdBuilder);
        }

        // 应用id
        if (!ObjectUtils.isEmpty(cylinderInfoDto.getAppId())) {
            flag = false;
            BoolQueryBuilder query = QueryBuilders.boolQuery();
            query.must(QueryBuilders.termsQuery("appId", cylinderInfoDto.getAppId().toLowerCase()));
            boolMust.must(query);
        }


        // 匹配产权单位
        if (!ObjectUtils.isEmpty(cylinderInfoDto.getUnitName())) {
            flag = false;
            BoolQueryBuilder query = QueryBuilders.boolQuery();
            query.must(QueryBuilders.matchQuery("unitName", "*" + cylinderInfoDto.getUnitName() + "*"));
            boolMust.must(query);
        }

        // 匹配出厂编号
        if (!ObjectUtils.isEmpty(cylinderInfoDto.getFactoryNum())) {
            flag = false;
            BoolQueryBuilder query = QueryBuilders.boolQuery();
            query.must(QueryBuilders.wildcardQuery("factoryNum", "*" + cylinderInfoDto.getFactoryNum() + "*"));
            boolMust.must(query);
        }

        // 匹配气瓶品种
        if (!ObjectUtils.isEmpty(cylinderInfoDto.getCylinderVariety())) {
            flag = false;
            BoolQueryBuilder query = QueryBuilders.boolQuery();
            query.must(QueryBuilders.matchQuery("cylinderVariety", cylinderInfoDto.getCylinderVariety()));
            boolMust.must(query);
        }

        // 匹配二维码编码
        if (!ObjectUtils.isEmpty(cylinderInfoDto.getQrCode())) {
            flag = false;
            BoolQueryBuilder query = QueryBuilders.boolQuery();
            query.must(QueryBuilders.wildcardQuery("qrCode", "*" + cylinderInfoDto.getQrCode() + "*"));
            boolMust.must(query);
        }

        // 匹配电子标签
        if (!ObjectUtils.isEmpty(cylinderInfoDto.getElectronicLabelCode())) {
            flag = false;
            BoolQueryBuilder query = QueryBuilders.boolQuery();
            query.must(QueryBuilders.matchQuery("electronicLabelCode", "*" + cylinderInfoDto.getElectronicLabelCode() + "*"));
            boolMust.must(query);
        }

        // 匹配气瓶唯一标识
        if (!ObjectUtils.isEmpty(cylinderInfoDto.getSequenceCode())) {
            flag = false;
            BoolQueryBuilder sequenceCodeBuilder = QueryBuilders.boolQuery();
            sequenceCodeBuilder.must(QueryBuilders.matchQuery("sequenceCode", cylinderInfoDto.getSequenceCode()));
            boolMust.must(sequenceCodeBuilder);
        }

        // 匹配单位内部编号
        if (!ObjectUtils.isEmpty(cylinderInfoDto.getUnitInnerCode())) {
            flag = false;
            BoolQueryBuilder query = QueryBuilders.boolQuery();
            query.must(QueryBuilders.wildcardQuery("unitInnerCode", "*" + cylinderInfoDto.getUnitInnerCode() + "*"));
            boolMust.must(query);
        }

        // 气瓶状态
        if (!ObjectUtils.isEmpty(cylinderInfoDto.getCylinderStatus())) {
            flag = false;
            BoolQueryBuilder query = QueryBuilders.boolQuery();
            query.must(QueryBuilders.matchQuery("cylinderStatus", cylinderInfoDto.getCylinderStatus()));
            boolMust.must(query);
        }

        // 制造单位
        if (!ObjectUtils.isEmpty(cylinderInfoDto.getManufacturingUnit())) {
            flag = false;
            BoolQueryBuilder query = QueryBuilders.boolQuery();
            query.must(QueryBuilders.matchQuery("manufacturingUnit", "*" + cylinderInfoDto.getManufacturingUnit() + "*"));
            boolMust.must(query);
        }
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        // 检验日期
        if (!ObjectUtils.isEmpty(cylinderInfoDto.getInspectionDateStart()) && !ObjectUtils.isEmpty(cylinderInfoDto.getInspectionDateEnd())) {
            flag = false;
            BoolQueryBuilder query = QueryBuilders.boolQuery();
            try {
                String inspectionDateStart = cylinderInfoDto.getInspectionDateStart() + " 00:00:00";
                String inspectionDateEnd = cylinderInfoDto.getInspectionDateEnd() + " 23:59:59";
                query.must(QueryBuilders.rangeQuery("inspectionDateMs")
                        .gte(sdf.parse(inspectionDateStart).getTime())
                        .lte(sdf.parse(inspectionDateEnd).getTime()));
            } catch (ParseException e) {
                e.printStackTrace();
            }
            boolMust.must(query);
        } else if (!ObjectUtils.isEmpty(cylinderInfoDto.getInspectionDateStart()) && ObjectUtils.isEmpty(cylinderInfoDto.getInspectionDateEnd())) {
            flag = false;
            BoolQueryBuilder query = QueryBuilders.boolQuery();
            try {
                String inspectionDateStart = cylinderInfoDto.getInspectionDateStart() + " 00:00:00";
                query.must(QueryBuilders.rangeQuery("inspectionDateMs")
                        .gte(sdf.parse(inspectionDateStart).getTime()));
            } catch (ParseException e) {
                e.printStackTrace();
            }
            boolMust.must(query);
        } else if (ObjectUtils.isEmpty(cylinderInfoDto.getInspectionDateStart()) && !ObjectUtils.isEmpty(cylinderInfoDto.getInspectionDateEnd())) {
            flag = false;
            BoolQueryBuilder query = QueryBuilders.boolQuery();
            try {
                String inspectionDateEnd = cylinderInfoDto.getInspectionDateEnd() + " 23:59:59";
                query.must(QueryBuilders.rangeQuery("inspectionDateMs")
                        .lte(sdf.parse(inspectionDateEnd).getTime()));
            } catch (ParseException e) {
                e.printStackTrace();
            }
            boolMust.must(query);
        }

        if (flag) { // 搜索全部
            boolMust.must(QueryBuilders.matchAllQuery());
        }

        builder.query(boolMust);
        builder.sort(SEQUENCE_NBR, SortOrder.DESC);
        builder.from((pageNum - 1) * pageSize);
        builder.size(pageSize);
        builder.trackTotalHits(true);
        request.source(builder);
        List<ESCylinderInfoDto> list = new LinkedList<>();
        long totle = 0;
        try {
            SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
            for (SearchHit hit : response.getHits()) {
                JSONObject jsonObject = (JSONObject) JSONObject.toJSON(hit);
                ESCylinderInfoDto esCylinderInfoDto = JSONObject.toJavaObject(jsonObject.getJSONObject("sourceAsMap"), ESCylinderInfoDto.class);
                list.add(esCylinderInfoDto);
            }
            totle = response.getInternalResponse().hits().getTotalHits().value;
            result.setRecords(list);
            result.setTotal(totle);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        return result;
    }

    /**
     * 根据月份统计
     */
    private void countByMonth(IntConsumer consumer) {
        for (int i = 0; i < 12; i++) {
            consumer.accept(i);
        }
    }

    /**
     * 根据单位统计
     */
    private void countByUnit(Consumer<CylinderUnit> consumer) {
        List<CylinderUnit> units = cylinderUnitServiceImpl.list();
        units.forEach(consumer);
    }

    /**
     * 根据区域统计
     */
    private void countByRegion(Consumer<RegionModel> consumer) {
        List<RegionModel> regionList = new ArrayList<>();
        startPlatformTokenService.getToken();
        Collection<RegionModel> regions = Systemctl.regionClient.queryForTree(null).getResult();
        regions.forEach(regionModel -> convertTreeToList(regionList, regionModel));
        regionList.forEach(consumer);
    }

    /**
     * 将区域树转为区域List列表
     */
    private void convertTreeToList(List<RegionModel> regionList, RegionModel region) {
        regionList.add(region);
        if (region.getChildren() != null) {
            region.getChildren().forEach(c -> {
                convertTreeToList(regionList, c);
            });
        }
    }

    public Integer getOverDateStatisticsNumber(String earlyWarningLevel) {
        return this.baseMapper.countOverDateNumber(earlyWarningLevel);
    }

    public Page<CylinderInfoDto> earlyWarningLevelPageList(Page<CylinderInfoDto> page, String earlyWarningLevel) {
        Page<CylinderInfoDto> result = this.baseMapper.queryPageListByEarlyWarningLevel(page, earlyWarningLevel);
        result.getRecords().forEach(r -> {
//            r.setEarlyWarningLevelName(EarlyWarningLevelEnum.getEumByLevel(earlyWarningLevel).getName());
//            r.setInspectionStatusDesc(EarlyWarningLevelEnum.getEumByLevel(earlyWarningLevel).getStatus());
        });

        return result;
    }

    public void calEarlyWarningLevel() {
        if (log.isDebugEnabled()) {
            log.debug("气瓶超期预警时间调用规则开始");
        }
        // 1.批量分组大小
        int size = 500;
        int total = this.count();
        int groupNumber = total / size + 1;
        // 2.批量小分组处理数据，调用规则
        for (int i = 0; i < groupNumber; i++) {
            Page<CylinderInfo> page = new Page<>();
            page.setCurrent(i);
            page.setSize(size);
            LambdaQueryWrapper<CylinderInfo> wrapper = new LambdaQueryWrapper<>();
            wrapper.select(CylinderInfo::getSequenceCode, CylinderInfo::getSequenceNbr)
                    .orderByDesc(CylinderInfo::getSequenceNbr);
            IPage<CylinderInfo> result = this.page(page, wrapper);
            for (CylinderInfo r : result.getRecords()) {
                // 设置token
                tzsAuthService.setRequestContext();
                // 调用规则
                this.touchRuleToCalLevel(r);
            }
        }
        if (log.isDebugEnabled()) {
            log.debug("气瓶超期预警时间调用规则完成");
        }
    }

    private void touchRuleToCalLevel(CylinderInfo r) {
        Date now = new Date();
        String dateStr;
        try {
            dateStr = DateUtils.dateFormat(now, DateUtils.DATE_TIME_PATTERN);
        } catch (ParseException e) {
            throw new RuntimeException("日期个时候失败");
        }
        // 1.气瓶详情
        CylinderInfoDto cylinderInfoDto = this.getDetail(r.getSequenceCode());
        try {
            WarningMsgDto warningMsgDto = new WarningMsgDto();
            int interval = DateUtils.dateBetweenIncludeToday(now, cylinderInfoDto.getNextInspectionDate()) - 1;
            warningMsgDto.setNum(String.valueOf(interval));
            warningMsgDto.setFactoryNum(cylinderInfoDto.getFactoryNum());
//            warningMsgDto.setUserType(cylinderInfoDto.getCustomType());
//            warningMsgDto.setUserPeople(cylinderInfoDto.getCustomName());
//            warningMsgDto.setUserPeoplePhone(cylinderInfoDto.getContactPhone());
            warningMsgDto.setCode(cylinderInfoDto.getSequenceCode());
            warningMsgDto.setCompanyName(cylinderInfoDto.getUnitName());
            warningMsgDto.setPhone(cylinderInfoDto.getPersonMobilePhone());
            warningMsgDto.setPeople(cylinderInfoDto.getUnitPerson());
            warningMsgDto.setCurrentTime(dateStr);
            // 2.循环调用规则 触发计算等级及发送消息
            if (log.isInfoEnabled()) {
                log.info("调用规则对象！+：{}", JSON.toJSONString(warningMsgDto));
            }
            ruleTrigger.publish(warningMsgDto, packageId, null);
        } catch (Exception e) {
            log.error("调用规则失败！:{},{}", JSON.toJSONString(cylinderInfoDto), e);
        }
    }

    public CylinderInfoDto getDetail(String sequenceCode) {
        CylinderInfoDto dto = scheduleMapper.getCylDetail(sequenceCode);
        dto.setInspectionStatusDesc(StringUtils.isNotEmpty(dto.getEarlyWarningLevel())
                ? EarlyWarningLevelEnum.getEumByLevel(dto.getEarlyWarningLevel()).getStatus()
                : "");
        return dto;
    }

    public List<MsgLog> getMsgList(String sequenceCode, String terminalType) {
        LambdaQueryWrapper<MsgLog> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(MsgLog::getRelationCode, sequenceCode);
        wrapper.eq(StringUtils.isNotEmpty(terminalType), MsgLog::getTerminalType, terminalType);
        wrapper.orderByDesc(MsgLog::getSendTime);
        return msgLogService.list(wrapper);
    }

    /**
     * 更新气瓶等级
     *
     * @param sequenceCode 唯一表设
     * @param level        等级
     * @return CylinderInfo
     */
    public CylinderInfo updateEarlyWarningLevel(String sequenceCode, String level) {
        CylinderInfo cylinderInfo = this
                .getOne(new LambdaQueryWrapper<CylinderInfo>().eq(CylinderInfo::getSequenceCode, sequenceCode));
        cylinderInfo.setEarlyWarningLevel(level);
        cylinderInfo.setEarlyWarningLevelCalDate(new Date());
        this.updateById(cylinderInfo);
        return cylinderInfo;
    }

    public Boolean nextInspectionDateUpdate(List<CylinderInfoDto> cylinderInfoDtos) {
        // 1.更新下次检验日期
        List<CylinderInfo> cylinderInfos = new ArrayList<>();
        cylinderInfoDtos.forEach(c -> {
            CylinderInfo cylinderInfo = this.getOne(
                    new LambdaQueryWrapper<CylinderInfo>().eq(CylinderInfo::getSequenceCode, c.getSequenceCode()));
//            cylinderInfo.setNextInspectionDate(c.getNextInspectionDate());
            cylinderInfos.add(cylinderInfo);
        });
        if (!cylinderInfos.isEmpty()) {
            this.updateBatchById(cylinderInfos);
        }
        // 2.循环调用规则 触发计算等级及发送消息
        cylinderInfos.forEach(this::touchRuleToCalLevel);
        return Boolean.TRUE;
    }

    public Page<CylinderInfoDto> cyinderInfoList(Page<CylinderInfoDto> page, CylinderInfoDto cylinderInfoDto, String sort, List<String> appids) {
        return cylinderInfoMapper.cyinderInfoList(page, cylinderInfoDto, sort, appids);
    }

    public Page<CylinderInfoDto> getCyinderInfoList(Page<CylinderInfoDto> page) {
        return cylinderInfoMapper.getCyinderInfoList(page);
    }

    public Page<CylinderInfoDto> cyinderOutInfoList(Page<CylinderInfoDto> page, CylinderInfoDto cylinderInfoDto, String sort, List<String> appids) {
        return cylinderInfoMapper.cyinderOutInfoList(page, cylinderInfoDto, sort, appids);
    }

    public Map<String, Object> countFillingTimesAndQuantityByCity() {
        // 查询所有的市级城市
        List<RegionModel> regionModelList = Systemctl.regionClient.queryByLevel("2").getResult();
        // 并行处理每个城市的数据
        List<CompletableFuture<Map<String, Object>>> futures = regionModelList.stream()
                .map(regionModel -> CompletableFuture.supplyAsync(() -> esQuery(regionModel)))
                .collect(Collectors.toList());
        List<Map<String, Object>> collect = futures.stream()
                .map(CompletableFuture::join)
                .collect(Collectors.toList());
        return assemblingHistogramData(collect);
        // Map<String, Object> stringObjectMap = esQuery(regionModelList.get(3));
        // ArrayList<Map<String, Object>> ss = new ArrayList<>();
        // ss.add(stringObjectMap);
        // return assemblingHistogramData(ss);
    }

    private Map<String, Object> esQuery(RegionModel regionModel) {
        SearchRequest searchRequest = new SearchRequest(INDEX_NAME);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        // 将 regionModel.getRegionCode() 转换成匹配中间部分的格式
        String regionCodePattern = "*" + regionModel.getRegionCode() + "*";
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        boolQueryBuilder.must(QueryBuilders.wildcardQuery(REGION_CODE, regionCodePattern));
        searchSourceBuilder.query(boolQueryBuilder);
        // 添加新的条件：筛选fillingEndTime在当前时间往前推31天之内的数据
        ZonedDateTime thirtyOneDaysAgo = ZonedDateTime.now().minusDays(31);
        Date thirtyOneDaysAgoDate = Date.from(thirtyOneDaysAgo.toInstant());

        boolQueryBuilder.must(QueryBuilders.rangeQuery(FILLING_END_TIME).gte(thirtyOneDaysAgoDate));
        searchSourceBuilder.aggregation(
                AggregationBuilders.cardinality(TOTAL_FILLING_COUNT)
                        .field(SEQUENCE_NBR)
        );
        searchSourceBuilder.aggregation(
                AggregationBuilders.sum(TOTAL_FILLING_QUANTITY)
                        .field(FILLING_QUANTITY)
        );
        searchRequest.source(searchSourceBuilder);
        System.out.println("Search Request: " + searchRequest);

        try {
            // 执行搜索请求
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            // 获取所有聚合结果
            Aggregations aggregations = searchResponse.getAggregations();
            ParsedCardinality cardinalityAgg = aggregations.get(TOTAL_FILLING_COUNT);
            long uniqueCount = cardinalityAgg.getValue();
            Sum sumAgg = aggregations.get(TOTAL_FILLING_QUANTITY);
            double totalSum = sumAgg.getValue();
            System.out.println("填充数量的唯一值数量: " + uniqueCount);
            System.out.println("填充量的总和: " + totalSum);
            Map<String, Object> resultMap = new HashMap<>();
            resultMap.put(REGION_CODE, regionModel.getRegionCode());
            resultMap.put(REGION_NAME, regionModel.getRegionName());
            resultMap.put(FILLING_TIMES, uniqueCount);
            resultMap.put(FILLING_QUANTITY, Math.round(totalSum));
            return resultMap;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return Collections.emptyMap();
    }


    public List<CylinderFillingRecordStatisticsDto>  fillingTimesAndQuantity(String reginCode) {
        String orgCode = null;
        CompanyModel result = Privilege.companyClient.queryByCompanyCode(reginCode).getResult();
        orgCode = result.getOrgCode();

        List<String> times = new ArrayList<>();
        Date currentDate = new Date();
        for (int i =0; i < 30; i++) {
            times.add(LocalDate.now().minusDays(i).format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
        }
        List<CylinderFillingRecordStatisticsDto> fillingRecord = baseMapper.queryFillingRecordByOrgCode(orgCode, LocalDate.now().minusDays(29).format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
        List<CylinderFillingRecordStatisticsDto> offloading = baseMapper.queryoffloadingByOrgCode(orgCode, LocalDate.now().minusDays(29).format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
        if (fillingRecord == null) {
            fillingRecord = new ArrayList<>(0);
        }
        if (offloading == null) {
            offloading = new ArrayList<>(0);
        }
        Map<String, String> fillingRecordMap = fillingRecord.stream().collect(Collectors.toMap(CylinderFillingRecordStatisticsDto::getTime, f -> f.getFillingQuantity()));
        Map<String, String> offloadingMap = offloading.stream().collect(Collectors.toMap(CylinderFillingRecordStatisticsDto::getTime, f -> f.getOffloadingVolume()));
        List<CylinderFillingRecordStatisticsDto> data = new ArrayList<>();
        for (String key : times) {
            CylinderFillingRecordStatisticsDto dayData = new CylinderFillingRecordStatisticsDto();
            dayData.setTime(key);
            dayData.setFillingQuantity(fillingRecordMap.getOrDefault(key, "0"));
            dayData.setOffloadingVolume(offloadingMap.getOrDefault(key, "0"));
            data.add(dayData);
        }
        return data;
    }
}