package com.lutao.cloud.collect.service.impl;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.lutao.cloud.collect.dto.DataAddDTO;
import com.lutao.cloud.collect.service.CollectDataService;
import com.lutao.cloud.collect.service.CollectLogService;
import com.lutao.cloud.collect.service.StorageService;
import com.lutao.cloud.collect.utils.CheckUtil;
import com.lutao.cloud.collect.utils.EntityConvert;
import com.lutao.cloud.collect.utils.IpUtil;
import com.lutao.cloud.collect.utils.JsonUtilConvert;
import com.lutao.cloud.collect.vo.DataAddVO;
import com.lutao.cloud.common.core.kafka.MessageCommandEnum;
import com.lutao.cloud.common.core.kafka.MessageTopicEnum;
import com.lutao.cloud.common.core.util.JsonUtil;
import com.lutao.cloud.common.core.util.MessageInfo;
import com.lutao.cloud.consume.feign.RemoteConsumeService;
import com.lutao.entity.*;
import com.lutao.utils.Validator;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.time.Instant;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.*;

@Service
@Slf4j
@AllArgsConstructor
public class CollectDataServiceImpl implements CollectDataService {

    @Resource
    private KafkaTemplate<String, Object> kafkaTemplate;

    @Resource
    private RemoteConsumeService remoteConsumeService;

    @Resource
    private CollectLogService collectLogService;

    private static Map<String, Instant> processedRequestIds = new ConcurrentHashMap<>();

    private static final long THREE_SECONDS_IN_MILLS = 3 * 1000;

    private static final long THREE_MINUTES_IN_MILLIS = 3 * 60 * 1000;

    private RedisTemplate redisTemplate;

    private StorageService storageService;

    private static ScheduledExecutorService executor;

    private static HashMap<String, Object> objectHashMap;

    static {
        objectHashMap = new HashMap<>();
        objectHashMap.put("change_info", ChangeInfo.class);
        objectHashMap.put("platform_info", PlatformInfo.class);
        objectHashMap.put("person_info", PersonInfo.class);
        objectHashMap.put("person_certificate", PersonCertificate.class);
        objectHashMap.put("process_parameters_tag", ProcessParameterTag.class);
        objectHashMap.put("fire_parameters_tag", FireParametersTag.class);
        objectHashMap.put("hazardous_gas_parameters_tag", HazardousGasParametersTag.class);
        objectHashMap.put("gas_parameters_tag", GasParametersTag.class);
        objectHashMap.put("work_status_clm", WorkStatusClm.class);
        objectHashMap.put("valve_status_clm", ValveStatusClm.class);
        objectHashMap.put("tag_alarm_clm", TagAlarmClm.class);
        objectHashMap.put("prof_equipment_info", ProfEquipmentInfo.class);
        objectHashMap.put("platform_person_info", PlatformPersonInfo.class);
        objectHashMap.put("video_equipment_info", VideoEquipmentInfo.class);
        objectHashMap.put("dangerous_act_record", DangerousActRecord.class);
        objectHashMap.put("person_in_info", PersonInInfo.class);
        objectHashMap.put("person_out_info", PersonOutInfo.class);
        objectHashMap.put("contractor_manage", ContractorManage.class);
        objectHashMap.put("high_risk_operation", HighRiskOperation.class);
        objectHashMap.put("emergency_record", EmergencyRecord.class);
        objectHashMap.put("change_info_review", ChangeInfoReview.class);
        objectHashMap.put("record_manage", RecordManage.class);
        objectHashMap.put("matters_manager", MattersManager.class);
        objectHashMap.put("giveup_mine_report", GiveupMineReport.class);
        objectHashMap.put("security_risk_assessment", SecurityRiskAssessment.class);
        objectHashMap.put("problem_rectification_detail", ProblemRectificationDetail.class);
        objectHashMap.put("platform_person_time_info", PlatformPersonTimeInfo.class);
        objectHashMap.put("division_inspection_detail", DivisionInspectionDetail.class);
        objectHashMap.put("ministerial_supervision_detail", MinisteriaSupervisionDetail.class);
        // 动态数据
        objectHashMap.put("perception_monitor_info", PerceptionMonitorInfo.class);
        objectHashMap.put("perception_monitor_fire_info", PerceptionMonitorFireInfo.class);
        objectHashMap.put("perception_monitor_gas_info", PerceptionMonitorGasInfo.class);
        objectHashMap.put("perception_monitor_hazardous_gas_info", PerceptionMonitorHazardousGasInfo.class);
        objectHashMap.put("atmosphere_monitor_info", AtmosphereMonitorInfo.class);
        objectHashMap.put("work_status_monitor", WorkStatusMonitor.class);
        objectHashMap.put("valve_status_monitor", ValveStatusMonitor.class);
    }


    /**
     * 感知监测信息
     */
    private static final List<String> PERCEPTION_MONITOR_INFO_KEY_LIST =
            Arrays.asList("platform_id", "platform_code", "facility_id", "facility_code", "facility_name", "equip_id", "equip_name", "equip_code", "tag_id", "tag_code", "tag_name", "rt_value", "collect_time", "last_update_time", "alarm_state", "alarm_type");

    /**
     * 火灾感知监测信息
     */
    private static final List<String> PERCEPTION_MONITOR_FIRE_INFO_KEY_LIST =
            Arrays.asList("platform_id", "platform_code", "facility_id", "facility_code", "facility_name", "equip_id", "equip_name", "equip_code", "tag_id", "tag_code", "tag_name", "rt_value", "collect_time", "last_update_time", "alarm_state", "alarm_type");

    /**
     * 可燃气体感知监测信息
     */
    private static final List<String> PERCEPTION_MONITOR_GAS_INFO_KEY_LIST =
            Arrays.asList("platform_id", "platform_code", "facility_id", "facility_code", "facility_name", "equip_id", "equip_name", "equip_code", "tag_id", "tag_code", "tag_name", "rt_value", "collect_time", "last_update_time", "alarm_state", "alarm_type");

    /**
     * 有毒有害气体感知监测信息
     */
    private static final List<String> PERCEPTION_MONITOR_HAZARDOUS_GAS_INFO_KEY_LIST =
            Arrays.asList("platform_id", "platform_code", "facility_id", "facility_code", "facility_name", "equip_id", "equip_name", "equip_code", "tag_id", "tag_code", "tag_name", "rt_value", "collect_time", "last_update_time", "alarm_state", "alarm_type");

    /**
     * 气象监测信息
     */
    private static final List<String> ATMOSPHERE_MONITOR_INFO_KEY_LIST =
            Arrays.asList("platform_id", "platform_code", "name_waters", "forecast_efficiency", "wind_speed", "wind_direction", "gustiness", "pressure", "wave_height", "max_wave_height", "wave_direction", "period", "temp", "low_temp", "high_temp", "sd", "wse", "flow_velocity", "flow_direction", "visibility", "weather_type", "extreme_weather_type", "detection_time");

    /**
     * 阀状态监测信息 valve_status_monitor
     */
    private static final List<String> VALUE_STAUS_MONITOR =
            Arrays.asList("platform_id", "platform_code", "facility_id", "facility_code", "facility_name", "equip_id", "equip_name", "equip_code", "valve_state", "collect_time");

    /**
     * 工况状态监测信息表 work_status_monitor
     */
    private static final List<String> work_status_monitor =
            Arrays.asList("platform_id", "platform_code", "facility_id", "facility_code", "facility_name", "equip_id", "equip_name", "equip_code", "work_status", "collect_time");

    private static final HashMap<String, HashSet> updateFileMap = new HashMap<String, HashSet>() {
        {
            // 人员资质 扫描件
            put("person_certificate", new HashSet<>(Collections.singletonList("certificate_file")));
            // 危险行为识别信息记录 报警图片地址
            put("dangerous_act_record", new HashSet<>(Collections.singletonList("alarm_picture_url")));
            // 应急预案备案信息数据 应急预案附件
            put("emergency_record", new HashSet<>(Collections.singletonList("emergencyplan_file")));
            // 备案管理数据  备案申请资料
            put("record_manage", new HashSet<>(Collections.singletonList("filing_file")));
            // 重大事项报告信息数据 报告附件
            put("matters_manager", new HashSet<>(Collections.singletonList("report_file")));
            // 专业设备信息数据 检测报告附件
            put("prof_equipment_info", new HashSet<>(Collections.singletonList("file_url")));
            // 变更信息数据 变更附件
            put("change_info", new HashSet<>(Collections.singletonList("change_file")));
            // 弃井报告信息数据 弃井前报告 弃井后报告
            put("giveup_mine_report", new HashSet<>(Collections.singletonList(new String[]{"submit_report", "complete_reprot"})));
            // 高风险作业信息数据 报告附件
            put("high_risk_operation", new HashSet<>(Collections.singletonList("file_url")));
        }
    };

    /**
     * （1）批量同步业务数据接口(根据指令进行数据的增删改操作)
     * 数据量限制，新增和更新限制data数组最大50条记录；删除限制data数组最大200条记录。
     * 原始数据样例
     * 加密前数据样例
     * {
     * "ts": 1688552964000,
     * "requestId": "request-123",
     * "command": "add",
     * "appKey": "abc",
     * "resource": "employee_out_sea_info",
     * "sign": "fc2c193d1960bb09122fa6261fce7ac6a0877c1b65277252779ca2acec9bd1a4",
     * "data": [
     * {
     * "userId": "bc7954df1101055a199fa3671b79c8a1",
     * "name": "张三"
     * }
     * ]
     * }
     * 同步：ip:port/sync
     *
     * @param dataAdd
     * @return
     */
    @Override
    //@CollectInfoLog
    public MessageInfo collectStaticDataSync(DataAddDTO dataAdd, HttpServletRequest request) {

//        if (true){
//            return MessageInfo.ok("success");
//        }
//        log.info("dataAdd:{}", dataAdd);

        String ip = IpUtil.getIP(request);
        //校验解密后的data是否是json格式
        AppInfo appInfo = getAppInfo(dataAdd.getAppKey());
        String data = CheckUtil.decrypData(appInfo, dataAdd.getData());
        log.info("data:{}", data);
        if (!JSONUtil.isJsonArray(data)) {
            return MessageInfo.failed("data json 格式错误");
        }
        dataAdd.setData(data);
        //校验sign
        if (!CheckUtil.checkSign(appInfo, dataAdd)) {
            log.error("ip:{} sign error", ip);
            return MessageInfo.failed("签名错误");
        }
        // 验证后替换key:id 为key:origin_id

        String newdata = data.replaceAll("\"id\"", "\"origin_id\"");
        log.info("newData:{}", newdata);
        dataAdd.setData(newdata);
        String requestId = dataAdd.getRequestId();
        //检查是否已处理相同的requestId，并且时间间隔大于3秒

        if (!checkRequest(requestId)) {
            log.error("ip:{} RequestId error ", ip);
            return MessageInfo.failed("相同的RequestId在3秒内被处理过了，该id为:" + requestId);
        }

        if (false) {
            log.error("ip:{} ts error", ip);
            return MessageInfo.failed("ts时间和服务器时间相差3分钟以上，数据丢弃不处理");
        } else {
            DataAddVO dataAddVO = EntityConvert.dataAddDtoToVo(dataAdd);
            dataAddVO.setAppId(appInfo.getId());

            //实体字段校验
            String resource = dataAdd.getResource();
            Class obj = (Class) objectHashMap.get(resource);
            if (obj == null) {
                return MessageInfo.failed("静态数据未知{table_name}----数据丢弃");
            }
            String command = dataAdd.getCommand();
            int i = 1;
            //add  upd  进行 实体校验
            if ("add".equals(command) || "upd".equals(command)) {
                List objList = JSONUtil.toList(dataAdd.getData(), obj);
                for (Object object : objList) {
                    try {
                        checkObject(object);
                        i++;
                    } catch (Exception e) {
                        log.info(LocalDateTime.now() + "===" + dataAdd.getAppId() + "===" + dataAdd.getRequestId() + "===第" + i + "条数据存在字段为空");
                        return MessageInfo.failed(e.getMessage());
                    }
                }
            }

            //发送消息
            if ("perception_monitor_info".equals(resource) || "perception_monitor_fire_info".equals(resource)
                    || "perception_monitor_gas_info".equals(resource) || "perception_monitor_hazardous_gas_info".equals(resource)
                    || "atmosphere_monitor_info".equals(resource) || "work_status_monitor".equals(resource) || "valve_status_monitor".equals(resource)) {
                sendMessage(MessageTopicEnum.DYNAMIC_DATA_UPDATE_TOPIC.getTopic(), dataAddVO, dataAdd);

            } else {
                sendMessage(MessageTopicEnum.STATIC_DATA_ADD_TOPIC.getTopic(), dataAddVO, dataAdd);
            }
            //更新requestId的处理时间
            processedRequestIds.put(requestId, Instant.now());
            log.info("ip:{}  sync success", ip);
            return MessageInfo.ok("success");
        }
    }

    /**
     * （1）批量同步业务数据接口(根据指令进行数据的增删改操作)
     * 数据量限制，新增和更新限制data数组最大50条记录；删除限制data数组最大200条记录。
     * {
     * "ts": 1688552964000,
     * "requestId": "request-123",
     * "command": "add",
     * "appKey": "abc",
     * "resource": "perception_monitor_info",
     * "sign": "867d8e38117e9a6c84f786f95b5c10243b65df2152462c4d12c56ab4a1ff5923",
     * "data": [[
     * "bc7954df1101055a199fa3671b79c8a1",
     * "张三"
     * ]]
     * }
     *
     * @param dataAdd
     * @return
     */
    public MessageInfo collectDynamicBatch(DataAddDTO dataAdd, HttpServletRequest request) {
        log.info("dataAdd:{}", dataAdd);
        String ip = IpUtil.getIP(request);
        AppInfo appInfo = getAppInfo(dataAdd.getAppKey());
        String data = CheckUtil.decrypData(appInfo, dataAdd.getData());
        log.info("data:{}", data);
        if (!JSONUtil.isJsonArray(data)) {
            return MessageInfo.failed("data json 格式错误");
        }
        dataAdd.setData(data);
        if (!CheckUtil.checkSign(appInfo, dataAdd)) {
            return MessageInfo.failed("签名错误");
        }

        String requestId = dataAdd.getRequestId();
        //检查是否已处理相同的requestId，并且时间间隔大于3秒
        if (!checkRequest(requestId)) {
            return MessageInfo.failed("相同的RequestId在3秒内被处理过了，该id为:" + requestId);
        }

        if (!checkTs(dataAdd.getTs())) {
            return MessageInfo.failed("ts时间和服务器时间相差3分钟以上，数据丢弃不处理");
        } else {
            String tableName = dataAdd.getResource();
            Object tableObj = objectHashMap.get(tableName);
            if (tableObj == null) {
                return MessageInfo.failed("动态数据未知{table_name}----数据丢弃");
            }
            JSONArray jsonArray = JSONUtil.parseArray(data);
            JSONArray insertJsonArray = new JSONArray();
            jsonArray.forEach(datainfo -> {
                if (JSONUtil.isJsonArray(datainfo.toString())) {
                    JSONArray array = JSONUtil.parseArray(datainfo);
                    JSONObject jsonObject = formatBatchData(array, dataAdd.getResource());
                    if (jsonObject != null) {
                        insertJsonArray.put(jsonObject);
                    }
                }
            });
            dataAdd.setData(JSONUtil.toJsonStr(insertJsonArray));
            DataAddVO dataAddVO = EntityConvert.dataAddDtoToVo(dataAdd);
            dataAddVO.setAppId(appInfo.getId());
            String resource = dataAdd.getResource();
            String command = dataAdd.getCommand();

            Class obj = (Class) objectHashMap.get(resource);
            List objList = JSONUtil.toList(dataAdd.getData(), obj);
            for (Object object : objList) {
                try {
                    checkObject(object);
                } catch (Exception e) {
                    return MessageInfo.failed(e.getMessage());
                }

            }

            log.info("dataAdd:{}", JSONUtil.toJsonStr(dataAdd));
            //发送消息
            sendMessage(MessageTopicEnum.DYNAMIC_DATA_UPDATE_TOPIC.getTopic(), dataAddVO, dataAdd);
            //更新requestId的处理时间
            processedRequestIds.put(requestId, Instant.now());
            log.info("ip:{}  batch success", ip);
            return MessageInfo.ok("success");
        }
    }

    private JSONObject formatBatchData(JSONArray array, String resource) {
        List<String> keysList = new ArrayList<>();
        switch (resource) {
            case "perception_monitor_info":
                keysList = PERCEPTION_MONITOR_INFO_KEY_LIST;
                break;

            case "perception_monitor_fire_info":
                keysList = PERCEPTION_MONITOR_FIRE_INFO_KEY_LIST;
                break;

            case "perception_monitor_gas_info":
                keysList = PERCEPTION_MONITOR_GAS_INFO_KEY_LIST;
                break;

            case "perception_monitor_hazardous_gas_info":
                keysList = PERCEPTION_MONITOR_HAZARDOUS_GAS_INFO_KEY_LIST;
                break;

            case "atmosphere_monitor_info":
                keysList = ATMOSPHERE_MONITOR_INFO_KEY_LIST;
                break;

            case "valve_status_monitor":
                keysList = VALUE_STAUS_MONITOR;
                break;

            case "work_status_monitor":
                keysList = work_status_monitor;
                break;

        }
        if (array.size() != keysList.size()) {
            log.error("collectDynamicBatch arrayData error");
            return null;
        }
        JSONObject retJson = new JSONObject();
        for (int i = 0; i < keysList.size(); i++) {
            retJson.putOnce(keysList.get(i), array.get(i));
        }
        return retJson;
    }

    /**
     * （4）文件上传，采用base64上传，并附带上传文件的作用和对应关系信息。资质证书、附件。
     * {
     * "ts": 1688552964000,
     * "requestId": "request-123",
     * "command": "file_id",
     * "appKey": "abc",
     * "resource": "perception_monitor_info",
     * "sign": "b47e924535f9af48739eee383cc0b8d340844e1ef87f0a86726a6f55b885583b",
     * "data": "data:image/jpeg;base64,iVBORw0KGgoAAAANSUhEUgAAAykAAABYCAYAAADx"
     * }
     *
     * @param dataAdd
     * @return
     */
    public MessageInfo collectBase64Upload(DataAddDTO dataAdd, HttpServletRequest request) {
        log.info("dataAdd:{}", dataAdd);
        String ip = IpUtil.getIP(request);
        AppInfo appInfo = getAppInfo(dataAdd.getAppKey());
        String data = CheckUtil.decrypData(appInfo, dataAdd.getData());
        log.info("data:{}", data);
        dataAdd.setData(data);
        if (!CheckUtil.checkSign(appInfo, dataAdd)) {
            return MessageInfo.failed("签名错误");
        }
        try {

            HashSet field = updateFileMap.get(dataAdd.getResource());
            if (field == null) {
                return MessageInfo.failed("不支持的表更新");
            }
            if (!field.contains(dataAdd.getCommand())) {
                return MessageInfo.failed("不支持的字段更新");
            }
            //检查是否已处理相同的requestId，并且时间间隔大于3秒
            String requestId = dataAdd.getRequestId();
            if (!checkRequest(requestId)) {
                return MessageInfo.failed("相同的RequestId在3秒内被处理过了，该id为:" + requestId);
            }

            if (!checkTs(dataAdd.getTs())) {
                return MessageInfo.failed("ts时间和服务器时间相差3分钟以上，数据丢弃不处理");
            }
            String fileUrl = storageService.uploadFileBase64(data);
            // 发送消息到kafka 进行更新
            // 需要文件的表和字段处理
            JSONObject jsonObject = new JSONObject();
            jsonObject.putOnce(dataAdd.getCommand(), fileUrl);
            dataAdd.setCommand(MessageCommandEnum.MESSAGE_upd.getCommand());
            dataAdd.setData(JSONUtil.toJsonStr(jsonObject));
            DataAddVO dataAddVO = EntityConvert.dataAddDtoToVo(dataAdd);
            dataAddVO.setAppId(appInfo.getId());
            log.info("dataAdd:{}", JSONUtil.toJsonStr(dataAdd));
            //发送消息
            sendMessage(MessageTopicEnum.STATIC_DATA_ADD_TOPIC.getTopic(), dataAddVO, dataAdd);
            //更新requestId的处理时间
            processedRequestIds.put(requestId, Instant.now());
        } catch (Exception e) {
            return MessageInfo.failed(e.getMessage());
        }
        log.info("ip:{}  upload success", ip);
        return MessageInfo.ok("success");
    }

    /**
     * （3）数据查询，用于验证推送结果。
     * 查询：ip:port/list
     * {
     * "ts": 1688552964000,
     * "requestId": "request-123",
     * "command": "find",
     * "appKey": "abc",
     * "resource": "employee_out_sea_info",
     * "sign": "b47e924535f9af48739eee383cc0b8d340844e1ef87f0a86726a6f55b885583b",
     * "data": [
     * {
     * "id": "bc7954df1101055a199fa3671b79c8a1"
     * }
     * ]
     * }
     *
     * @param dataAdd
     * @return
     */
    public MessageInfo collectDataFindList(DataAddDTO dataAdd, HttpServletRequest request) {
        log.info("dataAdd:{}", dataAdd);
        String ip = IpUtil.getIP(request);
        AppInfo appInfo = getAppInfo(dataAdd.getAppKey());
        if (checkAppId(appInfo)) {

        }
        String data = CheckUtil.decrypData(appInfo, dataAdd.getData());
        log.info("data:{}", data);
        if (!JSONUtil.isJsonArray(data)) {
            return MessageInfo.failed("data json 格式错误");
        }
        dataAdd.setData(data);
        if (!CheckUtil.checkSign(appInfo, dataAdd)) {
            return MessageInfo.failed("签名错误");
        }
        log.info("ip:{}  find success", ip);
        dataAdd.setAppId(appInfo.getId());
        return remoteConsumeService.getListByIds(dataAdd);
    }

    private boolean checkAppId(AppInfo appInfo) {
        return appInfo == null || StrUtil.isBlank(appInfo.getId());
    }


    /**
     * 发送静态信息
     *
     * @param topic      话题
     * @param dataAddVO  数据添加vo
     * @param dataAddDTO 数据添加dto
     */
    public void sendMessage(String topic, DataAddVO dataAddVO, DataAddDTO dataAddDTO) {
        //创建线程池
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                10,
                15,
                180,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(500),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.CallerRunsPolicy()
        );

        JsonNode jsonNode = JsonUtilConvert.convertJsonToArray(dataAddDTO.getData());
        String appId = dataAddVO.getAppId();
        String resource = dataAddVO.getResource();
        String requestId = dataAddVO.getRequestId();
        String command = dataAddVO.getCommand();

        for (JsonNode node : jsonNode) {
            threadPoolExecutor.submit(() -> {
                synchronized (dataAddVO) {
                    dataAddVO.setJsonDate(node);
                    if ("person_certificate".equals(resource) || "platform_person_time_info".equals(resource)) {
                        kafkaTemplate.send(topic, resource, JsonUtil.toJson(dataAddVO));
                    } else {
                        kafkaTemplate.send(topic, JsonUtil.toJson(dataAddVO));
                    }
                }
            });
        }

        try {
            collectLogService.saveCollectLog(requestId, appId, command, jsonNode.size(), resource, dataAddDTO.getData());
        } catch (Exception e) {
            log.error("生产日志入库-{}-失败", resource);
        }
        log.info("生产日志入库-{}-已完成", resource);
        threadPoolExecutor.shutdown();
    }

    /**
     * 检查请求ID是否在3秒内重复
     *
     * @param requestId 请求id
     * @return {@link Boolean}
     */
    public Boolean checkRequest(String requestId) {
        Instant lastProcessedTime = processedRequestIds.get(requestId);
        return lastProcessedTime == null || lastProcessedTime.plusSeconds(3).isBefore(Instant.now());
    }

    /**
     * 检查ts 是否和服务器时间相差3分钟以上
     *
     * @param ts ts
     * @return {@link Boolean}
     */
    public Boolean checkTs(Long ts) {
        //ts
        Instant instant = Instant.ofEpochMilli(ts);
        //系统时间
        Instant currentInstant = Instant.now();
        long differenceInMillis = Math.abs(instant.toEpochMilli() - currentInstant.toEpochMilli());
        return differenceInMillis <= THREE_MINUTES_IN_MILLIS;
        //return true;
    }

    private AppInfo getAppInfo(String appKey) {
        AppInfo appInfo = new AppInfo();
        BoundHashOperations appinfoCache = redisTemplate.boundHashOps("appinfo");
        Object appInfoStr = appinfoCache.get(appKey);
        if (appInfo != null) {
            appInfo = (AppInfo) appInfoStr;
        } else {
            return null;
        }
        return appInfo;
    }

    /**
     * 将消息转换为实体
     *
     * @param message 消息
     */
    public DataAddVO convertMessageToEntity(String message) {
        try {
            //创建ObjectMapper对象
            ObjectMapper objectMapper = new ObjectMapper();
            //将字符串转换为JSON对象
            JsonNode jsonNode = objectMapper.readTree(message);
            //将JSON对象转换为Java对象
            return objectMapper.treeToValue(jsonNode, DataAddVO.class);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 校验对象
     *
     * @param obj obj
     */
    private static void checkObject(Object obj) {
        try {
            Validator.validate(obj);

        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        }
    }


    static {
        executor = Executors.newSingleThreadScheduledExecutor();
        executor.scheduleAtFixedRate(() -> {
            Iterator<Map.Entry<String, Instant>> iterator = processedRequestIds.entrySet().iterator();
            while (iterator.hasNext()) {
                Map.Entry<String, Instant> entry = iterator.next();
                Instant processTime = entry.getValue();
                Instant currentTime = Instant.now();
                //清理3s前的数据
                if (processTime.isBefore(currentTime.minusMillis(THREE_SECONDS_IN_MILLS))) {
                    iterator.remove();
                }
                log.info("Map CleanUp completed.Current size :{}", processedRequestIds.size());
            }
        }, 5, 5, TimeUnit.SECONDS);
    }


}
