package com.qmth.themis.business.constant;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.aventrix.jnanoid.jnanoid.NanoIdUtils;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.common.reflect.TypeToken;
import com.qmth.themis.business.bean.admin.OpenRecordAnswerBean;
import com.qmth.themis.business.bean.admin.OpenRecordAnswerStructBean;
import com.qmth.themis.business.bean.admin.OpenRecordAnswerTempBean;
import com.qmth.themis.business.bean.admin.OpenRecordNeedMarkBean;
import com.qmth.themis.business.cache.ExamRecordCacheUtil;
import com.qmth.themis.business.cache.RedisKeyHelper;
import com.qmth.themis.business.dto.ExpireTimeDTO;
import com.qmth.themis.business.dto.cache.TEStudentCacheDto;
import com.qmth.themis.business.entity.TOeExamRecord;
import com.qmth.themis.business.enums.*;
import com.qmth.themis.business.service.TEExamPaperService;
import com.qmth.themis.business.service.TOeExamRecordService;
import com.qmth.themis.business.service.ThemisCacheService;
import com.qmth.themis.business.util.OssUtil;
import com.qmth.themis.business.util.RedisUtil;
import com.qmth.themis.business.util.SessionUtil;
import com.qmth.themis.common.contanst.Constants;
import com.qmth.themis.common.enums.ExceptionResultEnum;
import com.qmth.themis.common.enums.Platform;
import com.qmth.themis.common.enums.Source;
import com.qmth.themis.common.exception.BusinessException;
import com.qmth.themis.common.util.Base64Util;
import com.qmth.themis.common.util.GsonUtil;
import com.qmth.themis.common.util.IpUtil;
import org.apache.commons.io.IOUtils;
import org.lionsoul.ip2region.xdb.Searcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * @Description: 系统常量
 * @Param:
 * @return:
 * @Author: wangliang
 * @Date: 2019/10/11
 */
public class SystemConstant {
    private final static Logger log = LoggerFactory.getLogger(SystemConstant.class);

    /**
     * 云阅卷api
     */
    public static final String CLOUD_MARK_EXAM_STUDENT_SCORE_API = "/api/exam/student/score";
    public static final String CLOUD_MARK_EXAM_SAVE_API = "/api/exam/save";
    public static final String CLOUD_MARK_SUBJECT_SAVE_API = "/api/exam/subject/save";
    public static final String CLOUD_MARK_STUDENT_SAVE_API = "/api/exam/student/save";
    public static final String CLOUD_MARK_FILE_TYPE_UPLOAD_API = "/api/file/{type}/upload";
    public static final String CLOUD_MARK_STUDENT_OBJECTIVE_SAVE_API = "/api/exam/student/objective/save";

    /**
     * api前缀
     */
    public static final String PREFIX_URL_ADMIN = "/api/admin";
    public static final String PREFIX_URL_OPEN = "/api/open";
    public static final String PREFIX_URL_NOTIFY = "/api/notify";
    public static final String PREFIX_URL_OE = "/api/oe";
    public static final String PREFIX_URL_MOBILE = "/api/mobile";

    public static final String ATTACHMENT_TYPE = "attachment.type";
    public static final String ADMIN_LOGO_URL = "admin.logo.url";
    public static final String ADMIN_FILE_HOST = "admin.file.host";
    public static final String ADMIN_SERVER_HOST = "admin.server.host";
    public static final String EXAM_FILE_HOST = "exam.file.host";
    public static final String EXAM_SERVER_HOST = "exam.server.host";
    public static final String QR_EXPIRE_TIME = "qr.expire.time";
    public static final String CLOUD_MARK_URL = "cloud.mark.url";
    public static final String MONITOR_CONFIG_PREFIX = "monitor.config.prefix";
    public static final String WXAPP_UPLOAD_URL = "wxapp.upload.url";
    public static final String MQ_CONFIG_TOPIC = "mq.config.topic";
    public static final String MQ_CONFIG_BODY = "mq.config.body";
    public static final String ADMIN_NO_AUTH_URLS = "admin.no.auth.urls";
    public static final String ADMIN_COMMON_SYSTEM_URLS = "admin.common.system.urls";
    public static final String EXAM_NO_AUTH_URLS = "exam.no.auth.urls";
    public static final String EXAM_COMMON_SYSTEM_URLS = "exam.common.system.urls";

    public static final String TENCENT_CLOUD_SDK_APPID = "tencentcloud.sdk.appId";
    public static final String TENCENT_CLOUD_SDK_KEY = "tencentcloud.sdk.key";
    public static final String TENCENT_CLOUD_SDK_URLS = "tencentcloud.sdk.urls";
    public static final String TENCENT_CLOUD_SDK_SERVICE = "tencentcloud.sdk.service";
    public static final String TENCENT_CLOUD_SDK_QUERY_URL = "tencentcloud.sdk.queryUrl";
    public static final String TENCENT_CLOUD_SDK_SECRET_ID = "tencentcloud.sdk.secretId";
    public static final String TENCENT_CLOUD_SDK_SECRET_KEY = "tencentcloud.sdk.secretKey";
    public static final String TENCENT_CLOUD_SDK_VOD_APP_ID = "tencentcloud.sdk.vodAppId";
    public static final String TENCENT_CLOUD_SDK_CALLBACK_PWD = "tencentcloud.sdk.callbackPwd";
    public static final String TENCENT_CLOUD_SDK_CALLBACK_TIME = "tencentcloud.sdk.callbackTime";
    public static final String TENCENT_CLOUD_SDK_TRTC_QUERY_URL = "tencentcloud.sdk.trtcQueryUrl";
    public static final String TENCENT_CLOUD_SDK_TRTC_REGION = "tencentcloud.sdk.trtcRegion";
    public static final String CLIENT_DB_UPLOAD_LOCATION = "client.db.upload.location";
    public static final String CDN_URL = "cdn.url";
    public static final String WAITING_EXAM_DAY = "waiting.exam.day";

    public static final String DENY_LIST_FILE_NAME = "denyList.json";
    public static final String EXAM_CONFIG_FILE_NAME = "examConfig.json";
    public static final String NOTIFY_LIST = "notifyList";
    public static final String SYS_CONFIG_LIST = "sysconfigList";

    public static final String DATE_TIME_FORMAT = "%02d";

    public static final String TEMP = "temp";
    public static final String TMP_DIR = "java.io.tmpdir";

    public static final String PATTERN = "^[A-Za-z0-9]*";

    public static final String PATTERN_NUMBER = "^[0-9]*$";

    public static final String VIDEO_TIME_FORMAT = "yyyy-MM-dd-HH-mm-ss";

    public static final Integer DEFAULT_OPENING_SECONDS = 40;

    public static final String DEFAULT_DATE_PATTERN = "yyyy-MM-dd HH:mm:ss";

    /**
     * 鉴权
     */
    public static final String ACCESS_TOKEN = "accessToken";

    public static final String SESSION_ID = "sessionId";

    public static final String ORG_INFO = "orgInfo";

    public static final String CHARSET_NAME = "UTF-8";

    public static final String SIGN = "Sign";

    /**
     * 阿里云oss
     */
    public static final String END_POINT = "endpoint";

    public static final String ACCESS_KEY_ID = "accessKeyId";

    public static final String ACCESS_KEY_SECRET = "accessKeySecret";

    public static final String BUCKET = "bucket";

    public static final String URL = "url";

    /**
     * 腾讯云
     */
    //    public static final String TENCENT_APPID = "appId";

    //    public static final String TENCENT_KEY = "key";

    public static final long TENCENT_EXPIRE_TIME = 24 * 3600 * 30;//30天过期

    public static final String HEADER_AUTHORIZATION = "Authorization";

    public static final String HEADER_TIME = "time";

    public static final String VIDEO_SOURCE = "videoSource";

    public static final String VIDEO_URL = "videoUrl";

    /**
     * 系统相关
     */
    public static final String BREACH_LOG_STATUS = "breachLogStatus";
    public static final String OBJ_ID = "objId";
    public static final String TITLE = "title";
    public static final String BODY = "body";
    public static final String SUB_QUESTIONS = "subQuestions";
    public static final String ANSWER = "answer";
    public static final String STRUCT_TYPE = "structType";
    public static final String NUMBER = "number";
    public static final String DETAILS = "details";
    public static final String QUESTIONS = "questions";
    public static final String SCORE = "score";
    public static final String OPTIONS = "options";
    public static final String JSON_PREFIX = ".json";
    public static final String JOINT_MARK = "_";
    public static final String VALUE_OF_BLANK_REQUIRED_FIELD = "#";
    public static final String MULTI_MEDIA = "MULTI_MEDIA";
    public static final String FILE_UPLOAD_VARIABLE_URL = "\\{type\\}";
    public static final String CONTENT = "content";
    public static final String FORM_USER_ID = "formUserId";
    public static final String OPER = "oper";
    public static final String EXAM = "exam";
    public static final String WARNING_COUNT_UPDATE = "warning_count_update";
    public static final String WARNING_UNREAD_UPDATE = "warning_unread_update";
    //    public static final String EXEC_TIME = "execTime";
//    public static final String REAL_EXEC_TIME = "realExecTime";
    public static final String INSERT = "insert";
    public static final String DELETE = "delete";
    public static final String IP_ADDRESS = "ipAddress";
    //    public static final Long EXAM_AUDIO_BEFORE_TIME = 3000L;
    public static final Long BEFORE_AUDIO_ATTACHMENT_ID = 1L;
    public static final Long AFTER_AUDIO_ATTACHMENT_ID = 2L;
    public static final Integer BEFORE_AUDIO_SECOND = 60;
    public static final Integer AFTER_AUDIO_SECOND = 900;
    public static final long FINISH_DELAY_TIME = 1000 * 60 * 5;
    //    public static final String AUTH_INFO_CACHE = "auth:info:cache";
    public static final String EXAM_AUDIO_CACHE = "exam:audio:cache";
    public static final String ATTACHMENT_CACHE = "attachment:cache";
    public static final String BLACK_LIST_CACHE = "blacklist:cache";
    public static final String SYS_NOTIFY_CACHE = "sys:notify:cache";
    public static final String PROBLEM_CACHE = "problem:cache";
    public static final String CURRENT_EXAM_LIST_MAP_CACHE = "current:exam:list:map:cache";
    public static final String ORG_EXAM_LIST_MAP_CACHE = "org:exam:list:map:cache:";

    public volatile static Searcher SEARCHER = null;
    public static final String ONLINE_WARN_INTERVAL = "online.warn.interval";
    public static final String ONLINE_WARN_SCALESIZE = "online.warn.scaleSize";
    public static final String METHOD = "post";
    public static final int PAGE_SIZE_MIN = 10;
    public static final int PAGE_SIZE_MAX = 1000;
    public static final int SELECT_PAGE_SIZE_MAX = 1000000;
    public static final int PAGE_NUMBER_MIN = 0;
    public static final String STATUS = "status";
    public static final String WEBSOCKET_OE_PREFIX = "/ws/oe";
    public static final String WEBSOCKET_MOBILE_PREFIX = "/ws/mobile";
    public static final String LOGIN_LIMIT = "login limit";

    public static final String OBJECTIVE_SCORE_PUSH = "objectiveScorePush";
    public static final String EXAM_STUDENT_BREACH_PUSH = "examStudentBreachPush";
    public static final String EXAM_ID = "examId";
    public static final String EXAM_CODE = "examCode";
    public static final String ORG_ID = "orgId";
    public static final String REMARK = "remark";
    public static final String USER_ID = "userId";
    public static final String CREATE_ID = "createId";
    public static final String EXAM_RECORD_ID = "examRecordId";
    public static final String EXAM_STUDENT_ID = "examStudentId";
    public static final String COURSE_CODE = "courseCode";
    public static final String COURSE_NAME = "courseName";
    public static final String ROOM_CODE = "roomCode";
    public static final String ACTIVITY_ID = "activityId";
    public static final String CREATE_TIME = "createTime";
    public static final String UPDATE_TIME = "updateTime";
    public static final String WEB_SOCKET_OE_SERVER = "webSocketOeServer";
    public static final String WEBSOCKET_CLOSE = "服务器端关闭";
    public static final String LOG_ERROR = "请求出错:{}";
    public static final String EXTEND_COLUMN = "extendColumn";
    public static final String REMOVE_WEBSOCKET = "removeWebsocket";
    public static final String IMPORT_INIT = "准备开始处理导入数据";
    public static final String EXPORT_INIT = "准备开始处理导出数据";
    public static final String SCORE_PUSH_INIT = "准备同步云阅卷成绩数据";
    public static final String DATA_PUSH_INIT = "准备推送云阅卷数据";
    public static final String RECORD_ID = "recordId";
    public static final String STUDENT_ID = "studentId";
    public static final String MESSAGE = "message";
    public static final String ADMIN = "ADMIN";
    public static final String BREACH_STATUS = "breachStatus";
    public static final String STUDENT = "student:";
    public static final String SESSION = "session:";
    public static final String ORG = "org:cache:";
    public static final String LINK = "LINK";
    public static final String SYSADMIN = "系统管理员";
    public static final String ALL_PATH = "/**";
    public static final String ADMIN_ALL_PATH = "/api/admin/**";
//    public static final String THIRD_ALL_PATH = "/api/open/**";

    public static final String SCORE_PUSH_PATH = "/api/open/score/sync";

    public static final List<String> THIRD_ALL_PATH_LIST = Arrays.asList("/api/open/exam/course/query",
            "/api/open/exam/paper/detail",
            "/api/open/exam/record/need_mark",
            "/api/open/exam/query");

    public static final String ACCOUNT = "account";

    public static final String STUDENT_ACCOUNT = "student";

    public static final String COUNT = "count";

    public static final String ERROR = "/error";

    public static final String MQ_TOPIC_BUFFER_LIST = "mq:topic:buffer:list";

    public static final String MQ_DELAY_TOPIC_BUFFER_LIST = "mq:delay:topic:buffer:list";

    public static final String MQ_BROADCAST_TOPIC_BUFFER_LIST = "mq:broadcast:topic:buffer:list";

    public static final String MQ_ORDER_TOPIC_BUFFER_LIST = "mq:order:topic:buffer:list";

    public static final String TYPE = "type";

    public static final String LOCAL = "local";

    public static final String OSS = "oss";

//    public static final String FILE_HOST = "fileHost";

    public static final String PATH = "path";

    public static final String ID = "id";

    public static final String NAME = "name";

    public static final String TASK_ID = "taskId";

    public static final String MD5 = "md5";

    public static final String UPLOAD_TYPE = "uploadType";

    public static final String DEFAULT_PASSWORD = "MTIzNDU2";

    public static final String USER_DIR = "user.dir";

    public static final int MAX_IMPORT_SIZE = 500;

    public static final int MAX_EXPORT_SIZE = 500;

    public static final int MIN_QUERY_SIZE = 1;

    public static final int MAX_QUERY_SIZE = 1000;

    public static final int MAX_EXAM_STATUS_COUNT = 20;

    public static final int MAX_TENCENT_VIDEO_STREAM_ID_COUNT = 20;

    public static final String EXCEL_PREFIX = ".xlsx";

    public static final String TXT_PREFIX = ".txt";

    public static final String JPG_PREFIX = ".jpg";

    public static final String GLOBAL = "global";

    public static final String OK = "ok";

    public static String FILES_DIR;

    public static String TEMP_FILES_DIR;

    //    public static final String MONITOR_LIVE_URL_ = "monitorLiveUrl_";

    //    public static final String STREAM_NAME = "streamName_";

    public static final String MONITOR_STATUS_ = "monitorStatus_";

    public static final String MONITOR_CALL_STATUS_ = "monitorCallStatus_";

    public final static String REDIS_ACTIVITY_CODE_SEQUENCE = "redis:seq:activity:code:";

    public static final String VERSION = "version";
    public static final String VERSION_VALUE = "1.2.7.1";

    /**
     * session过期时间
     */
    public static final int ADMIN_WEB_SESSION_EXPIRE = 1;//过期时间1天

    public static final int ADMIN_CLIENT_SESSION_EXPIRE = 1;//过期时间1天

    public static final int OE_CLIENT_SESSION_EXPIRE = 1;//过期时间1天

    public static final int MOBILE_SESSION_EXPIRE = 30;//过期时间30天

    //二维码过期时间（秒）
    //    public final static Long QR_EXPIRE_TIME = 30L;

    /**
     * redis分布式锁
     */
    public static final String REDIS_LOCK_MQ_PREFIX = "lock:mq:";

    public static final String REDIS_LOCK_WEBSOCKET_PREFIX = "lock:websocket:";

    public static final String REDIS_LOCK_REEXAM_AUDITING_PREFIX = "lock:reexam:";

    public static final String REDIS_LOCK_EXAM_STATUS_PREFIX = "lock:exam_status:record:";

    public static final String REDIS_LOCK_EXAM_WARN_COUNT_PREFIX = "lock:exam_warn_count:record:";

    public static final String REDIS_LOCK_EXAM_WARN_UNREAD_PREFIX = "lock:exam_warn_unread:record:";

    public static final long REDIS_LOCK_MQ_TIME_OUT = 60L;

    public static final long REDIS_LOCK_WEBSOCKET_TIME_OUT = 30L;

    public static final long REDIS_LOCK_REEXAM_TIME_OUT = 30L;

    public static final long REDIS_LOCK_REEXAM_EXAM_STUDENT_TIME_OUT = 30L;

    public static final long REDIS_LOCK_EXAM_ACTIVITY_CODE_TIME_OUT = 30L;

    public static final long REDIS_CACHE_TIME_OUT = 30L;

    public static final long REDIS_LOCK_EXAM_STATUS_TIME_OUT = 60L;

    public static final long REDIS_LOCK_PROBLEM_TIME_OUT = 30L;

    public static final long REDIS_LOCK_EXAM_WARN_COUNT_TIME_OUT = 60L;

    public static final long REDIS_LOCK_EXAM_WARN_UNREAD_TIME_OUT = 60L;

    //学生锁
    public static final String REDIS_LOCK_STUDENT_PREFIX = "lock:student:student_id_";

    public static final long REDIS_LOCK_STUDENT_TIME_OUT = 30L;

    //考生锁
    public static final String REDIS_LOCK_EXAM_STUDENT_PREFIX = "lock:student:student_id_";

    public static final String REDIS_LOCK_EXAM_STUDENT_NOT_AUDITING_PREFIX = "lock:student:not_auditing_student_id_";

    //考试批次code锁
    public static final String REDIS_LOCK_EXAM_ACTIVITY_CODE_PREFIX = "lock:exam:activity:code:exam_id_";

    //计算客观分总分锁
    public static final String REDIS_LOCK_TOTAL_OBJECTIVE_SCORE_PREFIX = "lock:total_objective_score:record_id_";

    //考试记录数据持久化锁
    public static final String REDIS_LOCK_EXAM_RECORD_PERSISTED_PREFIX = "lock:exam_record_persisted:record_id_";

    //重新算分锁
    public static final String REDIS_LOCK_CALCULATE_SCORE_PREFIX = "lock:calculate_score:exam_id_";

    //试卷导入锁
    public static final String REDIS_LOCK_PAPER_IMPORT_PREFIX = "lock:paper_import:exam_id_";

    //重新算分和试卷导入互斥锁
    public static final String REDIS_LOCK_CALCULATE_SCORE_PAPER_IMPORT_PREFIX = "lock:calculate_score_paper_import:exam_id_";

    //交卷锁
    public static final String REDIS_LOCK_FINISH_EXAM_PREFIX = "lock:finish_exam:record_id_";

    //手机锁
    public static final String REDIS_LOCK_MOBILE_AUTHORIZATION_PREFIX = "lock:mobile:authorization_";

    public static final String REDIS_LOCK_TENCENT_VIDEO_PREFIX = "lock:tencent:video:";//腾讯云视频锁

    public static final String REDIS_LOCK_TENCENT_VIDEO_DELETE_PREFIX = "lock:tencent:video:delete:";//腾讯云视频锁

    public static final String REDIS_LOCK_TENCENT_VIDEO_STREAM_ID_PREFIX = "lock:tencent:video:stream:id:";//腾讯云视频锁

    //考生锁
//    public static final String REDIS_LOCK_PROBLEM_PREFIX = "lock:problem:id_";

    /**
     * redis过期时间
     */
    public static final long REDIS_EXPIRE_TIME = 60L * 1440L;//过期时间24小时

    public static final long REDIS_CLIENT_EXPIRE_TIME = 60L * 43200L;//过期时间30天

    public static final long REDIS_PHONE_EXPIRE_TIME = 60L * 43200L;//过期时间30天

    public static final long REDIS_DEFAULT_EXPIRE_TIME = 24 * 60L * 60L;//过期时间24小时

    public static final long REDIS_LOCK_TENCENT_VIDEO_TIME_OUT = 60L * 1;

    public static final long REDIS_LOCK_TENCENT_VIDEO_STREAM_ID_TIME_OUT = 60L * 20;

    public static final long REDIS_LOCK_TENCENT_VIDEO_DELETE_TIME_OUT = 60L * 60;

    /**
     * rocket mq
     */
    //    public static final String MQDTO_OBJ = "mqDtoObj";

    public static final int CONSUME_MESSAGE_BATCH_MAX_SIZE = 10;

    public static final int MAXRECONSUMETIMES = 3;

    //    public static final String MQDTO_OBJ = "mqDtoObj";
    public static final long MESSAGE_TIMEOUT = 3000L;

    public static List<String> mqDelayLevelList = null;

    public static final String delayLevel = "1s,5s,10s,30s,1m,2m,3m,4m,5m,6m,7m,8m,9m,10m,20m,30m,1h,2h";

    public static Map<String, Integer> mqDelayLevel = null;

    public static final int DELIVERED_ACK_TYPE = 0;//消息"已发出"，但尚未处理结束

    public static final int POSION_ACK_TYPE = 1;//消息"错误",通常表示"抛弃"此消息，比如消息重发多次后，都无法正确处理时，消息将会被删除或者DLQ(死信队列)

    public static final int STANDARD_ACK_TYPE = 2;//"标准"类型,通常表示为消息"处理成功"，broker端可以删除消息了

    public static final int REDELIVERED_ACK_TYPE = 3;//消息需"重发"，比如consumer处理消息时抛出了异常，broker稍后会重新发送此消息

    public static final int INDIVIDUAL_ACK_TYPE = 4;//表示只确认"单条消息",无论在任何ACK_MODE下

    public static final int UNMATCHED_ACK_TYPE = 5;//BROKER间转发消息时,接收端"拒绝"消息

    public static final int UNSEND_ACK_TYPE = 6;//消息未发出

    /**
     * 线程池配置
     */
//    public static final String THREAD_POOL_NAME = "taskThreadPool";
//    public static final int THREAD_POOL_CORE_POOL_SIZE = 5;
//    public static final int THREAD_POOL_MAX_POOL_SIZE = 100;
//    public static final int THREAD_POOL_KEEP_ALIVE_SECONDS = 10;
//    public static final int THREAD_POOL_QUEUE_CAPACITY = 500;
    /**
     * websocket
     */
    public static final String WEBSOCKET_OE_ONLINE_COUNT = "websocket:oe:online:count";

    public static final String WEBSOCKET_MOBILE_ONLINE_COUNT = "websocket:mobile:online:count";

//    public static final String WEBSOCKET_ADMIN_ONLINE_COUNT = "websocket:admin:online:count";

    public static final String GET = "get";

    public static final long WEBSOCKET_MAX_TIME_OUT = 1 * 60 * 1000;

    public static final String ACK_MESSAGE = "ackMessage";

    /**
     * 缓存配置
     */
    public static final String userOauth = "user:oauth:cache";

    public static final String studentOauth = "student:oauth:cache";

    public static final String userAccount = "user:account:cache";

    public static final String studentAccount = "student:account:cache";

    public static final String orgCache = "org:cache";

    public static final String orgCodeCache = "org:code:cache";

    public static final String roleCache = "role:cache";

    public static final String configCache = "config:cache";

    public static final String sysConfigCache = "sys:config:cache";

    public static final String examSummaryCache = "exam:summary:cache";

    public static final String orgSummaryCache = "org:summary:cache";

    public static final String regionSummaryCache = "region:summary:cache";

    //    /**
    //     * ehcache配置
    //     */
    //    public static final String orgCodeCache = "org_code_cache";
    //    public static final String roleCache = "role_cache";

    /**
     * http设置
     */
    public static final int CONNECT_TIME_OUT = 1000 * 60 * 2;//请求超时
    public static final int SOCKET_CONNECT_TIME_OUT = 1000 * 60 * 30;//读取数据超时

    static {
        String[] strs = delayLevel.split(",");
        mqDelayLevel = new LinkedHashMap();
        for (int i = 0; i < strs.length; i++) {
            mqDelayLevel.put(strs[i], i + 1);
        }
        mqDelayLevelList = Arrays.asList(strs);
    }

    /**
     * 获取过期时间
     *
     * @param platform
     * @return
     */
    public static ExpireTimeDTO getExpireTime(Source source, Platform platform) {
        ExpireTimeDTO dto = new ExpireTimeDTO();
        Date now = new Date();
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(now);
        long redisExpire = REDIS_EXPIRE_TIME;
        switch (source) {
            case ADMIN_WEB:
                calendar.add(Calendar.DAY_OF_YEAR, SystemConstant.ADMIN_WEB_SESSION_EXPIRE);
                break;
            case ADMIN_CLIENT:
                calendar.add(Calendar.DAY_OF_YEAR, SystemConstant.ADMIN_CLIENT_SESSION_EXPIRE);
                break;
            case OE_CLIENT:
                calendar.add(Calendar.DAY_OF_YEAR, SystemConstant.OE_CLIENT_SESSION_EXPIRE);
                redisExpire = REDIS_CLIENT_EXPIRE_TIME;
                break;
            case OE_ANSWER:
            case MOBILE_MONITOR_FIRST:
            case MOBILE_MONITOR_SECOND:
                calendar.add(Calendar.DAY_OF_YEAR, SystemConstant.MOBILE_SESSION_EXPIRE);
                redisExpire = REDIS_PHONE_EXPIRE_TIME;
                break;
            default:
                break;
        }
        dto.setDate(calendar.getTime());
        dto.setExpireSeconds(redisExpire);
        return dto;
    }

    /**
     * 比较过期
     *
     * @param timestamp
     * @return
     */
    public static boolean expire(long timestamp) {
        long diff = (System.currentTimeMillis() - timestamp) / 1000;
        return diff < -1 * Constants.SIGNATURE_AHEAD_SECONDS || diff > Constants.SIGNATURE_EXPIRE_SECONDS;
    }

    /**
     * 获取searcher
     *
     * @param path
     */
    public static void getSearcher(String path) throws IOException {
        SEARCHER = Objects.isNull(SEARCHER) ? IpUtil.getSearcher(path) : SEARCHER;
    }

    /**
     * 初始化附件文件路径
     */
    public static void initTempFiles() {
        StringJoiner localPath = new StringJoiner("").add(System.getProperty(SystemConstant.USER_DIR));
        String mkdir = localPath.toString().substring(0, localPath.toString().lastIndexOf(File.separator));
        File dir = new File(mkdir + File.separator + "themis-files");
        if (!dir.exists()) {
            dir.mkdirs();
        }
        File tempdir = new File(mkdir + File.separator + "themis-temp");
        if (!tempdir.exists()) {
            tempdir.mkdirs();
        }
        FILES_DIR = dir.getPath();
        TEMP_FILES_DIR = tempdir.getPath();
    }

    /**
     * 获取剩余时间
     *
     * @param diff
     * @return
     */
    public static String getRemainTime(Long diff) {
        long day = 0;
        long hour = 0;
        long min = 0;
        long sec = 0;
        day = diff / (24 * 60 * 60 * 1000);
        hour = (diff / (60 * 60 * 1000) - day * 24);
        min = ((diff / (60 * 1000)) - day * 24 * 60 - hour * 60);
        sec = (diff / 1000 - day * 24 * 60 * 60 - hour * 60 * 60 - min * 60);
        return String.format("%02d", hour) + ":" + String.format("%02d", min) + ":" + String.format("%02d", sec);
    }

    /**
     * 设置streamId
     *
     * @param prefix
     * @param recordId
     * @param monitorVideoSource
     * @return
     */
    public static String setStreamId(String prefix, Long recordId, MonitorVideoSourceEnum monitorVideoSource) {
        return prefix + "_" + recordId + "_" + monitorVideoSource.name().toLowerCase();
    }

    /**
     * 检查考试状态
     *
     * @param recordId
     */
    public static void checkExamStatus(Long recordId) {
        if (Objects.isNull(recordId)) {
            throw new BusinessException(ExceptionResultEnum.RECORD_ID_IS_NULL);
        }
        ExamRecordStatusEnum examRecordStatusEnum = ExamRecordCacheUtil.getStatus(recordId);
        if (Objects.isNull(examRecordStatusEnum)) {
            throw new BusinessException(ExceptionResultEnum.EXAM_FINISH);
        }
        if (!Objects.equals(ExamRecordStatusEnum.FIRST_PREPARE, examRecordStatusEnum) && !Objects
                .equals(ExamRecordStatusEnum.RESUME_PREPARE, examRecordStatusEnum) && !Objects
                .equals(ExamRecordStatusEnum.ANSWERING, examRecordStatusEnum)) {
            throw new BusinessException(ExceptionResultEnum.EXAM_STATUS_ERROR);
        }
    }

    /**
     * 获取监控源
     *
     * @param videoSource
     * @return
     */
    public static String getMonitorRecordStreamId(String videoSource) {
        MonitorVideoSourceEnum monitorVideoSourceEnum = getMonitorRecordVideoSource(videoSource);
        return videoSource.substring(0,
                videoSource.indexOf(monitorVideoSourceEnum.name().toLowerCase()) + monitorVideoSourceEnum.name()
                        .length());
    }

    /**
     * 获取监控源
     *
     * @param videoSource
     * @return
     */
    public static MonitorVideoSourceEnum getMonitorRecordVideoSource(String videoSource) {
        MonitorVideoSourceEnum monitorVideoSourceEnum = null;
        if (videoSource.contains(MonitorVideoSourceEnum.CLIENT_CAMERA.name().toLowerCase())) {
            monitorVideoSourceEnum = MonitorVideoSourceEnum.CLIENT_CAMERA;
        } else if (videoSource.contains(MonitorVideoSourceEnum.MOBILE_FIRST.name().toLowerCase())) {
            monitorVideoSourceEnum = MonitorVideoSourceEnum.MOBILE_FIRST;
        } else if (videoSource.contains(MonitorVideoSourceEnum.MOBILE_SECOND.name().toLowerCase())) {
            monitorVideoSourceEnum = MonitorVideoSourceEnum.MOBILE_SECOND;
        }
        return monitorVideoSourceEnum;
    }

    /**
     * 客户端状态强制断掉
     *
     * @param recordId
     */
    public static void clientMonitorStatusStop(Long recordId) {
        //更新客户端摄像头推流状态为stop
        MonitorStatusSourceEnum cameraStatusSourceEnum = ExamRecordCacheUtil
                .getMonitorStatus(recordId, MonitorVideoSourceEnum.CLIENT_CAMERA);
        Long timestamp = System.currentTimeMillis();
        if (Objects.nonNull(cameraStatusSourceEnum)) {
            ExamRecordCacheUtil
                    .setMonitorStatus(recordId, MonitorVideoSourceEnum.CLIENT_CAMERA, MonitorStatusSourceEnum.STOP,
                            timestamp);
        }
        //更新客户端屏幕推流状态为stop
        MonitorStatusSourceEnum screenStatusSourceEnum = ExamRecordCacheUtil
                .getMonitorStatus(recordId, MonitorVideoSourceEnum.CLIENT_SCREEN);
        if (Objects.nonNull(screenStatusSourceEnum)) {
            ExamRecordCacheUtil
                    .setMonitorStatus(recordId, MonitorVideoSourceEnum.CLIENT_SCREEN, MonitorStatusSourceEnum.STOP,
                            timestamp);
        }
    }

    /**
     * 移动端状态强制断掉
     *
     * @param studentId
     * @param recordId
     * @param deleteSession
     * @throws NoSuchAlgorithmException
     */
    public static void mobileMonitorStatusStop(Long studentId, Long recordId, boolean deleteSession)
            throws NoSuchAlgorithmException {
        ThemisCacheService themisCacheService = SpringContextHolder.getBean(ThemisCacheService.class);
        RedisUtil redisUtil = SpringContextHolder.getBean(RedisUtil.class);
        Long timestamp = System.currentTimeMillis();

        TEStudentCacheDto teStudent = themisCacheService.addStudentAccountCache(studentId);
        Optional.ofNullable(teStudent).orElseThrow(() -> new BusinessException("学生数据为空"));

        //更新移动端第一机位推流状态为stop
        MonitorStatusSourceEnum mobileFirstStatusSourceEnum = ExamRecordCacheUtil
                .getMonitorStatus(recordId, MonitorVideoSourceEnum.MOBILE_FIRST);
        if (Objects.nonNull(mobileFirstStatusSourceEnum)) {
            ExamRecordCacheUtil
                    .setMonitorStatus(recordId, MonitorVideoSourceEnum.MOBILE_FIRST, MonitorStatusSourceEnum.STOP,
                            timestamp);
            ExamRecordCacheUtil.setMobileFirstWebsocketStatus(recordId, WebsocketStatusEnum.OFF_LINE, timestamp);
            //是否删除session
            if (deleteSession) {
                String sessionId = SessionUtil.digest(teStudent.getId(),
                        Math.abs(Sets.newHashSet(RoleEnum.STUDENT.name()).toString().hashCode()),
                        Source.MOBILE_MONITOR_FIRST.name());
                redisUtil.deleteUserSession(sessionId);
            }
        }

        //更新移动端第二机位推流状态为stop
        MonitorStatusSourceEnum mobileSecondStatusSourceEnum = ExamRecordCacheUtil
                .getMonitorStatus(recordId, MonitorVideoSourceEnum.MOBILE_SECOND);
        if (Objects.nonNull(mobileSecondStatusSourceEnum)) {
            ExamRecordCacheUtil
                    .setMonitorStatus(recordId, MonitorVideoSourceEnum.MOBILE_SECOND, MonitorStatusSourceEnum.STOP,
                            timestamp);
            ExamRecordCacheUtil.setMobileSecondWebsocketStatus(recordId, WebsocketStatusEnum.OFF_LINE, timestamp);
            //是否删除session
            if (deleteSession) {
                String sessionId = SessionUtil.digest(teStudent.getId(),
                        Math.abs(Sets.newHashSet(RoleEnum.STUDENT.name()).toString().hashCode()),
                        Source.MOBILE_MONITOR_SECOND.name());
                redisUtil.deleteUserSession(sessionId);
            }
        }
    }

    /**
     * 获取全局uuid
     *
     * @return
     */
    public static String getNanoId() {
        return NanoIdUtils.randomNanoId();
    }

    /**
     * 获取考试状态
     *
     * @param recordId
     * @return
     */
    public static ExamRecordStatusEnum getExamStatus(Long recordId) {
        TOeExamRecord tOeExamRecord = getExamRecord(recordId);
        return tOeExamRecord.getStatus();
    }

    /**
     * 获取考试状态
     *
     * @param recordId
     * @return
     */
    public static TOeExamRecord getExamRecord(Long recordId) {
        TOeExamRecordService tOeExamRecordService = SpringContextHolder.getBean(TOeExamRecordService.class);
        TOeExamRecord tOeExamRecord = tOeExamRecordService.getById(recordId);
        Optional.ofNullable(tOeExamRecord).orElseThrow(() -> new BusinessException(ExceptionResultEnum.NOT_FOUND_EXAM_RECORD));
        return tOeExamRecord;
    }

    /**
     * 获取monitorRecordMap
     *
     * @param monitorRecord
     * @return
     */
    public static Map<String, String> getMonitorRecordMap(String monitorRecord) {
        if (Objects.nonNull(monitorRecord) && !Objects.equals(monitorRecord.trim().replaceAll(" ", ""), "")) {
            List<String> monitorRecordList = Arrays.asList(monitorRecord.trim().toUpperCase().split(","));
            Map<String, String> monitorRecordMap = monitorRecordList.stream()
                    .collect(Collectors.toMap(Function.identity(), s -> s));
            return Objects.nonNull(monitorRecordMap) && monitorRecordMap.size() > 0 ? monitorRecordMap : null;
        } else {
            return null;
        }
    }

    /**
     * 校验是否是数字
     *
     * @param str
     * @return
     */
    public static boolean checkNumber(String str) {
        Pattern pattern = Pattern.compile(PATTERN_NUMBER);
        Matcher matcher = pattern.matcher(str.trim());
        return matcher.matches();
    }

    /**
     * 图片转换
     *
     * @param imgBase64
     * @param filePath
     * @throws IOException
     */
    public static void base64ToImage(String imgBase64, String filePath) throws IOException {
        OutputStream out = null;
        try {
            byte[] b = Base64Util.decode(imgBase64);
            out = new FileOutputStream(filePath);
            IOUtils.write(b, out);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (Objects.nonNull(out)) {
                out.flush();
                out.close();
            }
        }
    }

    /**
     * 获取监控主机位
     *
     * @param monitorVideoSourceList
     * @return
     */
    public static MonitorVideoSourceEnum getMainMonitor(List<String> monitorVideoSourceList) {
        if (Objects.nonNull(monitorVideoSourceList) && (monitorVideoSourceList.contains(MonitorVideoSourceEnum.CLIENT_CAMERA.name())
                && monitorVideoSourceList.contains(MonitorVideoSourceEnum.MOBILE_FIRST.name())
                && monitorVideoSourceList.contains(MonitorVideoSourceEnum.MOBILE_SECOND.name()))) {
            return MonitorVideoSourceEnum.CLIENT_CAMERA;
        } else if (Objects.nonNull(monitorVideoSourceList) && (monitorVideoSourceList.contains(MonitorVideoSourceEnum.CLIENT_CAMERA.name())
                && monitorVideoSourceList.contains(MonitorVideoSourceEnum.MOBILE_FIRST.name()))) {
            return MonitorVideoSourceEnum.CLIENT_CAMERA;
        } else if (Objects.nonNull(monitorVideoSourceList) && (monitorVideoSourceList.contains(MonitorVideoSourceEnum.CLIENT_CAMERA.name())
                && monitorVideoSourceList.contains(MonitorVideoSourceEnum.MOBILE_SECOND.name()))) {
            return MonitorVideoSourceEnum.CLIENT_CAMERA;
        } else if (Objects.nonNull(monitorVideoSourceList) && (monitorVideoSourceList.contains(MonitorVideoSourceEnum.MOBILE_FIRST.name())
                && monitorVideoSourceList.contains(MonitorVideoSourceEnum.MOBILE_SECOND.name()))) {
            return MonitorVideoSourceEnum.MOBILE_FIRST;
        } else if (Objects.nonNull(monitorVideoSourceList) && monitorVideoSourceList.contains(MonitorVideoSourceEnum.CLIENT_CAMERA.name())) {
            return MonitorVideoSourceEnum.CLIENT_CAMERA;
        } else if (Objects.nonNull(monitorVideoSourceList) && monitorVideoSourceList.contains(MonitorVideoSourceEnum.MOBILE_FIRST.name())) {
            return MonitorVideoSourceEnum.MOBILE_FIRST;
        } else if (Objects.nonNull(monitorVideoSourceList) && monitorVideoSourceList.contains(MonitorVideoSourceEnum.MOBILE_SECOND.name())) {
            return MonitorVideoSourceEnum.MOBILE_SECOND;
        }
        return null;
    }

    /**
     * 过滤题目
     *
     * @param questions
     * @param answers
     * @param acceptObjective
     * @param acceptSubjective
     * @return
     */
    public static JSONArray filterQuestions(JSONArray questions, JSONArray answers, boolean acceptObjective,
                                            boolean acceptSubjective) {
        JSONArray collection = new JSONArray();
        for (int i = 0; i < questions.size(); i++) {
            JSONObject question = questions.getJSONObject(i);
            //根据题号查找答案
            JSONObject answer = findJsonObject(answers, question.getIntValue(SystemConstant.NUMBER));
            boolean accept = true;
            //判断结构类型
            switch (question.getIntValue(SystemConstant.STRUCT_TYPE)) {
                case 1:
                    //单选
                case 2:
                    //多选
                case 3:
                    //判断
                    accept = acceptObjective;
                    break;
                case 4:
                    //填空
                case 5:
                    //问答
                    accept = acceptSubjective;
                    break;
                case 6:
                    //套题
                    //按条件过滤套题下所有子题
                    JSONArray subAnswers = answer != null ? answer.getJSONArray(SystemConstant.SUB_QUESTIONS) : null;
                    JSONArray subCollection = filterQuestions(question.getJSONArray(SystemConstant.SUB_QUESTIONS), subAnswers,
                            acceptObjective, acceptSubjective);
                    //没有子题，则本题也被过滤掉
                    if (subCollection.size() > 0) {
                        question.put(SystemConstant.SUB_QUESTIONS, subCollection);
                    } else {
                        accept = false;
                    }
                    break;
                default:
                    accept = false;
            }
            if (accept) {
                if (answer != null) {
                    question.put(SystemConstant.ANSWER, answer.get(SystemConstant.ANSWER));
                }
                collection.add(question);
            }
        }
        return collection;
    }

    /**
     * 过滤答案
     *
     * @param array
     * @param number
     * @return
     */
    public static JSONObject findJsonObject(JSONArray array, int number) {
        if (array != null) {
            for (int i = 0; i < array.size(); i++) {
                JSONObject item = array.getJSONObject(i);
                if (item.getIntValue(SystemConstant.NUMBER) == number) {
                    return item;
                }
            }
        }
        return null;
    }

    /**
     * 过滤答案
     *
     * @param openRecordNeedMarkBean
     * @param answersTemp
     * @param paperStructMap
     * @return
     */
    public static OpenRecordNeedMarkBean filterAnswer(OpenRecordNeedMarkBean openRecordNeedMarkBean,
                                                      List<OpenRecordAnswerTempBean> answersTemp,
                                                      Map<Long, Map<String, Integer>> paperStructMap) {
        TEExamPaperService teExamPaperService = SpringContextHolder.getBean(TEExamPaperService.class);
        if (!CollectionUtils.isEmpty(answersTemp)) {
            List<OpenRecordAnswerBean> answers = new ArrayList<>(answersTemp.size());
            answersTemp.forEach(s -> {
                Map<String, Integer> struct = paperStructMap.get(openRecordNeedMarkBean.getPaperId());
                if (struct == null) {
                    struct = teExamPaperService.getPaperStructCacheBean(openRecordNeedMarkBean.getPaperId());
                    if (struct != null) {
                        paperStructMap.put(openRecordNeedMarkBean.getPaperId(), struct);
                    } else {
                        log.error("找不到对应的试卷结构，paperId=" + openRecordNeedMarkBean.getPaperId());
                        return;
                    }
                }
                Integer type = struct.get(RedisKeyHelper
                        .examAnswerHashKey(s.getMainNumber(), s.getSubNumber(),
                                s.getSubIndex()));
                if (Objects.nonNull(type) && type.intValue() != 1 && type.intValue() != 2 && type.intValue() != 3) {
                    OpenRecordAnswerBean openRecordAnswerBean = new OpenRecordAnswerBean(s.getMainNumber(), s.getSubNumber(), s.getSubIndex());
                    try {
                        openRecordAnswerBean.setAnswer(GsonUtil.fromJson(s.getAnswer(), new TypeToken<List<OpenRecordAnswerStructBean>>() {
                        }.getType()));
                    } catch (Exception e) {
                        log.error("错误答案:{}", s.getAnswer(), e);
                    }
                    answers.add(openRecordAnswerBean);
                }
            });
            openRecordNeedMarkBean.setAnswers(answers);
        }
        //考生未答题或未答主观题
        if (CollectionUtils.isEmpty(openRecordNeedMarkBean.getAnswers()) || CollectionUtils.isEmpty(answersTemp)) {
            Map<String, Integer> struct = paperStructMap.get(openRecordNeedMarkBean.getPaperId());
            if (struct == null) {
                struct = teExamPaperService.getPaperStructCacheBean(openRecordNeedMarkBean.getPaperId());
                if (struct != null) {
                    paperStructMap.put(openRecordNeedMarkBean.getPaperId(), struct);
                } else {
                    log.error("找不到对应的试卷结构，paperId=" + openRecordNeedMarkBean.getPaperId());
                    return openRecordNeedMarkBean;
                }
            }
            for (Map.Entry<String, Integer> entry : struct.entrySet()) {
                if (entry.getValue() >= 4 && entry.getValue() <= 8) {
                    String[] strs = entry.getKey().split("_");
                    OpenRecordAnswerBean openRecordAnswerBean = null;
                    if (strs.length == 3) {
                        openRecordAnswerBean = new OpenRecordAnswerBean(Integer.parseInt(strs[0]), Integer.parseInt(strs[1]), Integer.parseInt(strs[2]));
                    } else {
                        openRecordAnswerBean = new OpenRecordAnswerBean(Integer.parseInt(strs[0]), Integer.parseInt(strs[1]));
                    }
                    String answer = "[{\"index\":1,\"sections\":[{\"blocks\":[{\"type\":\"text\",\"value\":\"\"}]}]}]";
                    openRecordAnswerBean.setAnswer(GsonUtil.fromJson(answer, new TypeToken<List<OpenRecordAnswerStructBean>>() {
                    }.getType()));
                    openRecordNeedMarkBean.setAnswers(Lists.newArrayList(openRecordAnswerBean));
                    break;
                }
            }
        }
        return openRecordNeedMarkBean;
    }

    /**
     * 获取临时文件
     *
     * @param suffix
     * @return
     */
    public static File getFileTempVar(String suffix) throws IOException {
        File fileTmpDir = new File(System.getProperty(SystemConstant.TMP_DIR));
        if (!fileTmpDir.exists()) {
            fileTmpDir.mkdirs();
        }
        File file = File.createTempFile(SystemConstant.TEMP, suffix);
        log.info("getFileTempVar_absolutePath:{}", file.getAbsolutePath());
        return file;
    }

    /**
     * 创建txt文件
     *
     * @param path
     * @param type
     * @param message
     * @return
     * @throws Exception
     */
    public static JSONObject createTxt(String path, String type, String message) throws Exception {
        File txtFileTemp = null;
        try {
            OssUtil ossUtil = SpringContextHolder.getBean(OssUtil.class);
            txtFileTemp = SystemConstant.getFileTempVar(SystemConstant.TXT_PREFIX);
            IOUtils.write(message.getBytes(StandardCharsets.UTF_8), new FileOutputStream(txtFileTemp));
            JSONObject json = new JSONObject();
            StringJoiner stringJoiner = new StringJoiner("");
            if (Objects.equals(type, SystemConstant.OSS)) {//上传至oss
                stringJoiner.add(path).add(File.separator).add(SystemConstant.getNanoId())
                        .add(SystemConstant.TXT_PREFIX).toString();
                ossUtil.upload(false, stringJoiner.toString(), txtFileTemp);
            }
            json.put(SystemConstant.PATH, stringJoiner.toString());
            json.put(SystemConstant.TYPE, type);
            json.put(SystemConstant.UPLOAD_TYPE, UploadFileEnum.file.ordinal());
            return json;
        } finally {
            if (Objects.nonNull(txtFileTemp)) {
                txtFileTemp.delete();
            }
        }
    }

    /**
     * 获取dirname
     *
     * @param stringJoiner
     * @param uploadFileEnum
     * @param fileSpearator
     * @return
     */
    public static StringJoiner getDirName(StringJoiner stringJoiner, UploadFileEnum uploadFileEnum, boolean fileSpearator) {
        LocalDateTime nowTime = LocalDateTime.now();
        stringJoiner.add(uploadFileEnum.name()).add(File.separator)
                .add(String.valueOf(nowTime.getYear())).add(File.separator)
                .add(String.format(SystemConstant.DATE_TIME_FORMAT, nowTime.getMonthValue())).add(File.separator)
                .add(String.format(SystemConstant.DATE_TIME_FORMAT, nowTime.getDayOfMonth()));
        if (fileSpearator) {
            stringJoiner.add(File.separator);
        }
        return stringJoiner;
    }


    /**
     * 获取dirname
     *
     * @param stringJoiner
     * @param fileSpearator
     * @return
     */
    public static StringJoiner getDirName(StringJoiner stringJoiner, boolean fileSpearator) {
        LocalDateTime nowTime = LocalDateTime.now();
        stringJoiner.add(String.valueOf(nowTime.getYear())).add(File.separator)
                .add(String.format(SystemConstant.DATE_TIME_FORMAT, nowTime.getMonthValue())).add(File.separator)
                .add(String.format(SystemConstant.DATE_TIME_FORMAT, nowTime.getDayOfMonth()));
        if (fileSpearator) {
            stringJoiner.add(File.separator);
        }
        return stringJoiner;
    }

    /**
     * 获取dirname
     *
     * @param uploadFileEnum
     * @param fileSpearator
     * @return
     */
    public static StringJoiner getDirName(UploadFileEnum uploadFileEnum, boolean fileSpearator) {
        LocalDateTime nowTime = LocalDateTime.now();
        StringJoiner stringJoiner = new StringJoiner("");
        stringJoiner.add(uploadFileEnum.name()).add(File.separator)
                .add(String.valueOf(nowTime.getYear())).add(File.separator)
                .add(String.format(SystemConstant.DATE_TIME_FORMAT, nowTime.getMonthValue())).add(File.separator)
                .add(String.format(SystemConstant.DATE_TIME_FORMAT, nowTime.getDayOfMonth()));
        if (fileSpearator) {
            stringJoiner.add(File.separator);
        }
        return stringJoiner;
    }

    /**
     * 获取ip信息
     *
     * @param ip
     * @return
     */
    public static String[] getIpInfo(String ip) {
        String[] strs = null;
        if (Objects.nonNull(ip) && !Objects.equals(ip.trim(), "0:0:0:0:0:0:0:1") && !Objects.equals(ip.trim(), "127.0.0.1")
                && !Objects.equals(ip.trim(), "localhost")) {
            String region = IpUtil.getIpInfo(SystemConstant.SEARCHER, ip);
            if (Objects.nonNull(region)) {
                strs = region.split("\\|");
                if (strs.length != 5) {
                    log.error("ip region error:{}", region);
                    throw new BusinessException("ip信息格式错误");
                }
            }
        }
        return strs;
    }
}