package com.xhkjedu.utils;

import com.xhkjedu.exception.MissingParametersException;
import com.xhkjedu.exception.ServiceException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.util.CollectionUtils;

import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.URL;
import java.net.URLConnection;
import java.text.*;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.IntStream;


/**
 * @author WN
 * @className N_Utils
 * @description 工具类
 * @date 2019/4/3 14:13
 **/
@Slf4j
public class N_Utils {

    /**
     * 字符串是否为空
     *
     * @param: @param obj
     * @param: @return
     * @author WN
     */
    public static boolean isNotEmpty(Object obj) {
        if (null == obj || "".equals(obj.toString().trim()) || "null".equals(obj.toString().trim())) {
            return false;
        } else {
            return true;
        }
    }

    /**
     * 字符串是否为空
     *
     * @param: @param obj
     * @param: @return
     * @author zj
     */
    public static boolean isEmpty(Object obj) {
        if (null == obj || "".equals(obj.toString().trim()) || "null".equals(obj.toString().trim())) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * json字符串是否不为空
     * @Param [jsonStr]
     * @Author ywx
     * @Date 2020/5/13 16:06
     * @return boolean
     **/
    public static boolean isJsonStrNotEmpty(String jsonStr) {
        if (isNotEmpty(jsonStr) && !jsonStr.equals("[]")){
            return true;
        } else {
            return false;
        }
    }


    /**
     * 功能描述  integer 是否是有效的类型
     *
     * @param * @param obj
     * @return boolean
     * @author WN
     * @date 2019/7/15
     */
    public static boolean isTrueInteger(Integer obj) {
        if (null != obj && obj > 0) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 获取当前时间精确到秒的时间戳
     *
     * @param @return 设定文件
     * @return int    返回类型
     * @Title: getSecondTimestamp
     * @author WN
     * @date 2019年3月6日 下午10:13:50
     */
    public static int getSecondTimestamp(){
        String timestamp = String.valueOf(SystemClock.millisClock().now()/1000);
        return Integer.valueOf(timestamp);
    }

    public static String getStrtimeTimestamp(Integer seconds,String format){
        if(seconds == null || seconds==0){
            return "";
        }
        if(format == null || format.isEmpty()){
            format = "yyyy-MM-dd HH:mm:ss";
        }
        SimpleDateFormat sdf = new SimpleDateFormat(format);
        return sdf.format(new Date(Long.valueOf(seconds+"000")));
    }

    //计算两个时间戳相差的天数
    public static Integer getTimestampDay(Integer begintime,Integer endtime){
        if(isEmptyInteger(begintime)) return 365;//默认时间差为一年
        /*Integer day = 0;

        Integer cha = endtime - begintime;

        day = cha / (3600 * 24);*/

        BigDecimal b1 = new BigDecimal(String.valueOf(endtime - begintime));
        BigDecimal b2 = new BigDecimal(String.valueOf(3600 * 24));

        BigDecimal count = b1.divide(b2,2,BigDecimal.ROUND_UP);
        return count.intValue();
    }

    /**
     * 字符串转数字
     * @Param [str]
     * @Author ywx
     * @Date 2020/6/3 17:01
     * @return java.lang.Integer
     **/
    public static Integer str2Int(String str){
        if (isNotEmpty(str)){
            return Integer.parseInt(str);
        } else {
            return 0;
        }
    }

    /**
     * @Description 对象转数字
     * @Date 2024/10/21 16:49
     * @Author YWX
     * @Param [str]
     * @Return java.lang.Integer
     **/
    public static Integer obj2Int(Object str) {
        if (isNotEmpty(str)) {
            String num = str.toString();
            if (num.contains(".")) {
                return Integer.parseInt(num.split("\\.")[0]);
            } else {
                return Integer.parseInt(num);
            }
        } else {
            return 0;
        }
    }

    /**
     * 日期转时间戳(秒)
     * @Param [data, strType]
     * @Author ywx
     * @Date 2020/6/4 17:47
     * @return int
     **/
    public static int getDataToTimestamp(String data,String strType) {
        try {
            SimpleDateFormat format =  new SimpleDateFormat(strType);
            Date date = format.parse(data);
            //日期转时间戳（毫秒）
            return Integer.valueOf((date.getTime()/1000)+"");
        } catch (ParseException e) {
            log.error("日期转时间戳失败",e.getMessage());
            return 0;
        }
    }

    /**
     * 判断集合中数据同一个位置是否相同
     *
     * @param @param  oldlst
     * @param @param  newlst
     * @param @return 设定文件
     * @return boolean    返回类型
     * @Title: listNeworder
     * @author WN
     * @date 2018年8月2日 下午6:02:59
     */
    public static boolean listNeworder(List<Integer> oldlst, List<Integer> newlst) {
        for (int i = 0; i < oldlst.size(); i++) {
            int oldid = oldlst.get(i);
            int newid = newlst.get(i);
            if (oldid == newid) {
                return true;
            }
        }
        return false;
    }

    /**
     * 将一个list均分成n个list,主要通过偏移量来实现的
     * @param source
     * @return
     */
    public static List<List<Integer>> averageAssignList(List<Integer> source,int n){
        List<List<Integer>> result=new ArrayList<List<Integer>>();
        int remaider=source.size()%n;  //(先计算出余数)
        int number=source.size()/n;  //然后是商
        int offset=0;//偏移量
        for(int i=0;i<n;i++){
            List<Integer> value=null;
            if(remaider>0){
                value=source.subList(i*number+offset, (i+1)*number+offset+1);
                remaider--;
                offset++;
            }else{
                value=source.subList(i*number+offset, (i+1)*number+offset);
            }
            result.add(value);
        }
        return result;
    }

    /**
     * 判断集合是否不为空
     * @Param [list]
     * @Author ywx
     * @Date 2020/6/15 10:50
     * @return boolean
     **/
    public static boolean isListNotEmpty(List list) {
        if (CollectionUtils.isEmpty(list)){
            return false;
        } else {
            return true;
        }
    }

    /**
     * 判断集合是否为空
     * @Param [list]
     * @Author ywx
     * @Date 2020/7/14 8:48
     * @return boolean
     **/
    public static boolean isListEmpty(List list) {
        if (CollectionUtils.isEmpty(list)){
            return true;
        } else {
            return false;
        }
    }

    //获取当前日期的节点时间（年，月，日，时，分，秒）
    public static Integer getNodeDateTime(String node) {
        LocalDateTime today = LocalDateTime.now();
        Integer resultNode = null;
        switch (node) {
            case "year":
                resultNode = today.getYear();
                break;
            case "month":
                resultNode = today.getMonthValue();
                break;
            case "day":
                resultNode = today.getDayOfMonth();
                break;
            case "hour":
                resultNode = today.getHour();
                break;
            case "minute":
                resultNode = today.getMinute();
                break;
            case "second":
                resultNode = today.getSecond();
                break;
            default:
                // 当前日期是当前年的第几天。例如：2019/1/3是2019年的第三天
                resultNode = today.getDayOfYear();
                break;
        }
        return resultNode;
    }

    /**
     * 数字转字母 1-26 ： A-Z
     * @param num
     * @return
     */
    public static String numberToLetter(int num) {
        if (num <= 0) {
            return null;
        }
        String letter = "";
        num--;
        do {
            if (letter.length() > 0) {
                num--;
            }
            letter = ((char) (num % 26 + (int) 'A')) + letter;
            num = (int) ((num - num % 26) / 26);
        } while (num > 0);

        return letter;
    }

    /**
     * 获取指定个数的英文字母数组
     * @Param [num]
     * @Author ywx
     * @Date 2019/11/25 17:06
     * @return java.lang.String[]
     **/
    public static String[] getLetter(Integer num){
        String[] sections = new String[num];
        if(num!=null && num>0){
            for(int i=1;i<=num;i++){
                sections[i-1] = numberToLetter(i);
            }
        }
        return sections;
    }

    /**
     * 集合1不包含在集合2中的数字集合
     * @Param [nums1, nums2]
     * @Author ywx
     * @Date 2020/7/8 13:01
     * @return java.util.List<java.lang.Integer>
     **/
    public static String listNotInList2(List<String> nums1,List<String> nums2){
        List<String> nums = new ArrayList<>();
        for (String num : nums1) {
            if (!nums2.contains(num)){
                nums.add(num);
            }
        }

        return String.join(",",nums);
    }

    /**
     * 集合1不包含在集合2中的数字集合
     * @Param [nums1, nums2]
     * @Author ywx
     * @Date 2021/3/24 15:42
     * @return java.util.List<java.lang.String>
     **/
    public static List<String> listNotInList3(List<String> nums1,List<String> nums2){
        List<String> nums = new ArrayList<>();
        for (String num : nums1) {
            if (!nums2.contains(num)){
                nums.add(num);
            }
        }
        return nums;
    }

    /**
     * 数字是否包含在集合中的数字集合
     * @Param [num, nums]
     * @Author ywx
     * @Date 2020/7/14 11:18
     * @return boolean
     **/
    public static boolean numInList(Integer num,List<Integer> nums){
        if (isListEmpty(nums)) return false;
        if (nums.contains(new Integer(num))){
            return true;
        } else {
            return false;
        }
    }

    /**
     * 集合去重
     * @Param [list]
     * @Author ywx
     * @Date 2020/7/11 9:59
     * @return java.util.List
     **/
    public static List listDistinct(List<?> list){
        return list.stream().distinct().collect(Collectors.toList());
    }

    /**
     * 功能描述  判断integer类型不为null和0
     *
     * @param * @param strint
     * @return boolean
     * @author WN
     * @date 2019/10/15
     */
    public static boolean isEmptyInteger(Integer strint) {
        if (null != strint && strint != 0) {
            return false;
        } else {
            return true;
        }
    }

    /**
     * 去除字符串中前后的全、半角空格
     * @Param [str]
     * @Author ywx
     * @Date 2020/4/1 9:57
     * @return java.lang.String
     **/
    public static String strTrim(String str) {
        str = str.trim();//去除字符串前后的半角空格
        while (str.startsWith("　")) {//去除字符串前的全角空格
            str = str.substring(1, str.length()).trim();
        }
        while (str.endsWith("　")) {//去除字符串后的全角空格
            str = str.substring(0, str.length() - 1).trim();
        }
        return str;
    }



    /**
     * 验证手机号
     * @param phone
     * @return
     */
    public static boolean isPhone(String phone) {
        //String regex = "^((13[0-9])|(14[5,6,7,9])|(15([0-3]|[5-9]))|(166)|(17[0,1,3,5,6,7,8])|(18[0-9])|(19[8|9]))\\d{8}$";
        String regex = "^1\\d{10}$";
        if (phone == null) {
            return false;
        } else {
            Pattern p = Pattern.compile(regex);
            Matcher m = p.matcher(phone);
            boolean isMatch = m.matches();
            return isMatch;
        }

    }

    /**
     * 参数校验，为空的话提示定义的提示信息
     * @Param [strs] strs={"filename","msg","type","filename2","msg2","type2"}
     * @Author ywx
     * @Date 2020/4/26 15:08
     * @return void
     **/
    public static void validation(Object[] strs) {
        for (int i = 0; i < strs.length; i += 3) {
            Object filename = strs[i];
            String msg = strs[i + 1].toString() + "不能为空";
            Integer type = Integer.parseInt(strs[i + 2].toString());
            if (type == 1) {//Integer类型参数
                if (!N_Utils.isNotEmpty(filename) || filename.equals(0)) {
                    throw new MissingParametersException(msg);
                }
            } else if (type == 2) {//String类型参数
                if (!N_Utils.isNotEmpty(filename)) {
                    throw new MissingParametersException(msg);
                }
            } else if (type == 3) {// List类型参数
                List list = (List) filename;
                if (!isListNotEmpty(list)) {
                    throw new ServiceException(msg);
                }
            }
        }
    }


    /**
     *功能描述  获取当前年月日的时间戳
     * @author WN
     * @date 2020/8/9
     * @param  * @param
     * @return java.lang.Long
     */
    public static Long getNowtimestamp(){
        LocalDate localDate = LocalDate.now();

        ZoneId zone = ZoneId.systemDefault();
        LocalDateTime ldt = localDate.atTime(23,59,59);
        Instant instant = ldt.atZone(zone).toInstant();
        Long stamp =instant.toEpochMilli()/1000;
        return stamp;
    }

    public static Long getNowtimestamp2(){
        LocalDate localDate = LocalDate.now();

        ZoneId zone = ZoneId.systemDefault();
        LocalDateTime ldt = localDate.atTime(00,00,00);
        Instant instant = ldt.atZone(zone).toInstant();
        Long stamp =instant.toEpochMilli()/1000;
        return stamp;
    }

    /**
     *功能描述  根据当前时间获取指定日期的时间戳
     * @author WN
     * @date 2020/8/9
     * @param  * @param type
     * @return java.lang.String
     */
    public static Long getLongGoTime(Integer type){
        LocalDate localDate = LocalDate.now();
        ZoneId zone = ZoneId.systemDefault();
        Long stamp = null;

        LocalDate godate = null;

        if(type==1){
            //过去一周
            godate = localDate.minusWeeks(1);
        }else if(type==2){
            //过去一月
            godate = localDate.minusMonths(1);
        }else if(type==3){
            //过去三个月
            godate = localDate.minusMonths(3);
        }else if(type==4){
            //过去六个月
            godate = localDate.minusMonths(6);

        }else{
            //过去一年
            godate = localDate.minusYears(1);
        }
        stamp = godate.atStartOfDay(zone).toInstant().toEpochMilli()/1000;


        return stamp;
    }

    /**
     * @author ywx
     * @description 去除a标签
     * @date 2020/8/26 20:30
     **/
    public static String delATag(String str){
        if (isEmpty(str)){
            return null;
        } else {
            return str.replaceAll("<a[^>]*>|</a>","");
        }
    }


    /**
     *功能描述 两个数相除并乘以100.保留2位小数
     * @author WN
     * @date 2020/8/8
     * @param  * @param linecount
     * @param studentnum
     * @return java.math.BigDecimal
     */
    public static double getIntegerDivideAndMulitiply(Integer linecount, Integer studentnum){
        if (isEmptyInteger(studentnum) || isEmptyInteger(linecount)) {
            return 0;
        }
        BigDecimal b1 = new BigDecimal(linecount.toString());
        BigDecimal b2 = new BigDecimal(studentnum.toString());
        BigDecimal b = new BigDecimal(100);

        BigDecimal count = b1.divide(b2,4,BigDecimal.ROUND_DOWN).multiply(b);
        return count.doubleValue();
    }

    //两个数相除并乘以100.保留2位小数
    public static double getDoubleDivideAndMulitiply(Double linecount, Double studentnum){
        if (studentnum.compareTo(new Double(0)) == 0 || linecount == null) {
            return 0;
        }
        BigDecimal b1 = new BigDecimal(linecount.toString());
        BigDecimal b2 = new BigDecimal(studentnum.toString());
        BigDecimal b = new BigDecimal(100);

        BigDecimal count = b1.divide(b2,4,BigDecimal.ROUND_DOWN).multiply(b);
        return count.doubleValue();
    }

    //两个数相除并乘以100.保留2位小数
    public static double getDoubleDivideAndMulitiply(Double linecount, Integer studentnum){
        if (isEmptyInteger(studentnum) || linecount == null) {
            return 0;
        }
        BigDecimal b1 = new BigDecimal(linecount.toString());
        BigDecimal b2 = new BigDecimal(studentnum.toString());
        BigDecimal b = new BigDecimal(100);

        BigDecimal count = b1.divide(b2,4,BigDecimal.ROUND_DOWN).multiply(b);
        return count.doubleValue();
    }

    //两个数相除，保留两位小数（平均分）
    public static double getDoubleDivideForAvg(Double linecount, Integer studentnum){
        if (isEmptyInteger(studentnum) || linecount == null) {
            return 0;
        }
        BigDecimal b1 = new BigDecimal(linecount.toString());
        BigDecimal b2 = new BigDecimal(studentnum.toString());

        BigDecimal count = b1.divide(b2,2,BigDecimal.ROUND_DOWN);
        return count.doubleValue();
    }

    //两个数相除，保留两位小数（平均分）
    public static double getDoubleDivideForAvg(Integer linecount, Integer studentnum){
        if (isEmptyInteger(studentnum) || linecount == null) {
            return 0;
        }
        BigDecimal b1 = new BigDecimal(linecount.toString());
        BigDecimal b2 = new BigDecimal(studentnum.toString());

        BigDecimal count = b1.divide(b2,2,BigDecimal.ROUND_DOWN);
        return count.doubleValue();
    }

    //两个double 类型相加
    public static Double getDoubleSum(Double value1,Double value2){
        BigDecimal b1 = new BigDecimal(Double.toString(value1));
        BigDecimal b2 = new BigDecimal(Double.toString(value2));

        return b1.add(b2).doubleValue();
    }
    //两个double 类型相减
    public static Double getDoubleReduce(Double value1,Double value2){
        BigDecimal b1 = new BigDecimal(Double.toString(value1));
        BigDecimal b2 = new BigDecimal(Double.toString(value2));

        return b1.subtract(b2).doubleValue();
    }

    //计算比例向下取整
    public static Integer floorDiv(Integer num,Integer totalNum) {
        if (isEmptyInteger(totalNum) || isEmptyInteger(num)) return 0;
        return Math.floorDiv(num * 100, totalNum);
    }

    /**
     * 格式化百分比
     * @Param [num, totalNum]
     * @Author ywx
     * @Date 2020/9/22 10:26
     * @return java.lang.Integer
     **/
    public static Integer formatProportion(Integer num,Integer totalNum){
        if (isEmptyInteger(totalNum) || isEmptyInteger(num)) return 0;
        BigDecimal countBigDecimal = new BigDecimal(num);
        BigDecimal totalCountBigDecimal = new BigDecimal(totalNum);
        BigDecimal divide = countBigDecimal.divide(totalCountBigDecimal, 20, BigDecimal.ROUND_HALF_UP);
        divide=divide.setScale(2,BigDecimal.ROUND_HALF_UP);
        return divide.multiply(new BigDecimal("100")).intValue();
    }

    public static Integer formatAvg(Integer num,Integer totalNum){
        if (isEmptyInteger(totalNum) || isEmptyInteger(num)) return 0;
        BigDecimal countBigDecimal = new BigDecimal(num);
        BigDecimal totalCountBigDecimal = new BigDecimal(totalNum);
        BigDecimal divide = countBigDecimal.divide(totalCountBigDecimal, 0, BigDecimal.ROUND_UP);
        return divide.intValue();
    }

    /**
     * 格式化数值
     * @Param [num数值, decimalNum保留小数位数]
     * @Author ywx
     * @Date 2021/5/28 9:57
     * @return java.lang.Double
     **/
    public static Double formatDouble(Double num,Integer decimalNum) {
        if (num == null || num == Double.NEGATIVE_INFINITY || num == Double.POSITIVE_INFINITY) return 0.0;
        if (isEmptyInteger(decimalNum)) return num;
        BigDecimal bg = new BigDecimal(num);
        double num2 = bg.setScale(decimalNum, BigDecimal.ROUND_DOWN).doubleValue();
        return num2;
    }

    /**
     * @Description DoubleSummaryStatistics获取最大值有可能为(-Infinity)、最小值有可能为(Infinity);
     * 检查Double类型的值是否为-Infinity或Infinity，并在是的情况下返回0
     * @Date 2023/12/28 16:30
     * @Author YWX
     * @Param [value]
     * @Return double
     **/
    public static double handleInfinity(double value) {
        if (value == Double.NEGATIVE_INFINITY || value == Double.POSITIVE_INFINITY) {
            return 0;
        } else {
            return value;
        }
    }

    /**
     * @Description  格式化double类型，如果未小数点后为0反正整式，
     * @Param [score]
     * @Return java.lang.String
     * @Author wn
     * @Date 2022/9/2 16:34
     **/
    public static String formatDouble(Double score){
        BigDecimal bg = new BigDecimal(score).setScale(1, RoundingMode.HALF_DOWN);
        double num = bg.doubleValue();
        if (Math.round(num) - num == 0) {
            return String.valueOf((long) num);
        }
        return String.valueOf(num);
    }

    /**
     * @Description 格式化数值集合百分比（总和为100）
     * @Date 2023/10/25 17:36
     * @Author YWX
     * @Param [nums]
     * @Return java.util.Map<java.lang.Integer,java.lang.Double>
     **/
    public static Map<Integer, Double> parsePercent(List<Integer> nums) {
        Map<Integer, Double> map = new HashMap<>();
        int sum = 0;//定义一个整数变量用于记录列表中所有数字的总和
        for (int num : nums) {
            sum += num;
        }

        DecimalFormat df = new DecimalFormat("#.##"); // 用于格式化百分比，保留两位小数
        for (int num : nums) {
            double percentage = (double) num / sum * 100;//计算数字在总和中的百分比
            map.put(num, Double.valueOf(df.format(percentage)));//将数字作为键，格式化后的百分比作为值，存入哈希映射中
        }
        return map;//返回包含数字及其对应百分比的哈希映射
    }

    /**
     * 格式化百分比
     * @Param [arr]
     * @Author ywx
     * @Date 2020/10/12 11:58
     * @return double[]
     **/
    public static double[] getPercentValue(int[] arr){
        //求和
        double sum = 0;
        if(sum <= 0){
            for (int i = 0; i < arr.length; i++) {
                sum += arr[i];
            }
        }
        //10的2次幂是100，用于计算精度。
        double digits = 1;
        //扩大比例100
        double[] votesPerQuota = new double[arr.length];
        for(int i = 0; i < arr.length; i++){
            double val = arr[i] / sum * digits * 100;
            votesPerQuota[i] = val;
        }
        //总数,扩大比例意味的总数要扩大
        double targetSeats = digits * 100;
        //再向下取值，组成数组
        double[] seats = new double[arr.length];
        for(int i = 0; i < votesPerQuota.length; i++){
            seats[i] = Math.floor(votesPerQuota[i]);
        }
        //再新计算合计，用于判断与总数量是否相同,相同则占比会100%
        double currentSum = 0;
        for (int i = 0; i < seats.length; i++) {
            currentSum += seats[i];
        }
        //余数部分的数组:原先数组减去向下取值的数组,得到余数部分的数组
        double[] remainder = new double[arr.length];
        for(int i = 0; i < seats.length; i++){
            remainder[i] = votesPerQuota[i] - seats[i];
        }
        while(currentSum < targetSeats){
            double max = 0;
            int maxId = 0;
            for(int i = 0;i < remainder.length;++i){
                if(remainder[i] > max){
                    max = remainder[i];
                    maxId = i;
                }
            }
            //对最大项余额加1
            ++seats[maxId];
            //已经增加最大余数加1,则下次判断就可以不需要再判断这个余额数。
            remainder[maxId] = 0;
            //总的也要加1,为了判断是否总数是否相同,跳出循环。
            ++currentSum;
        }
        // 这时候的seats就会总数占比会100%
        return seats;
    }

    /**
     *功能描述 字符串是否包含中文
     * @author WN
     * @date 2020/11/21
     * @param  * @param str 待校验字符串
     * @return true 包含中文字符 false 不包含中文字符
     */
    public static boolean isContainChinese(String str){
        Pattern p = Pattern.compile("[\u4E00-\u9FA5|\\！|\\，|\\。|\\（|\\）|\\《|\\》|\\“|\\”|\\？|\\：|\\；|\\【|\\】]");
        Matcher m = p.matcher(str);
        if(m.find()){
            return true;
        }
        return false;

    }


    /**
     * 随机生成4位随机码
     * @Param []
     * @Author ywx
     * @Date 2020/5/8 9:29
     * @return java.lang.Integer
     **/
    public static Integer messageCode(){
        return (int) ((Math.random()*9+1)*1000);
    }

    /**
     * 向指定 URL 发送POST方法的请求
     * @param url   发送请求的 URL
     * @param param 请求参数，请求参数应该是 name1=value1&name2=value2 的形式。
     * @return 所代表远程资源的响应结果
     */
    public static String sendPost(String url, String param) {
        OutputStreamWriter out = null;
        BufferedReader in = null;
        String result = "";
        try {
            URL realUrl = new URL(url);
            // 打开和URL之间的连接
            URLConnection conn = realUrl.openConnection();

            // 设置通用的请求属性
            conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            conn.setRequestProperty("connection", "Keep-Alive");
            conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            // 发送POST请求必须设置如下两行
            conn.setDoOutput(true);
            conn.setDoInput(true);
            //设置请求编码格式
            conn.setRequestProperty("Accept-Charset", "UTF-8");
            // 文件流编码设置
            out = new OutputStreamWriter(conn.getOutputStream(), "UTF-8");
            out.write(param);
            // flush输出流的缓冲
            out.flush();
            // 定义BufferedReader输入流来读取URL的响应
            in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));
            String line;
            while ((line = in.readLine()) != null) {
                result += line;
            }
        } catch (Exception e) {
            log.error("发送 POST 请求出现异常！" + e.getMessage());
        }
        // 使用finally块来关闭输出流、输入流
        finally {
            try {
                if (out != null) {
                    out.close();
                }
                if (in != null) {
                    in.close();
                }
            } catch (IOException ex) {
                log.error("关闭输入输出流异常", ex.getMessage());
            }
        }
        return result;
    }

    /**
     * 向指定 URL 发送POST方法的请求
     * @Param [url, param]
     * @Author ywx
     * @Date 2021/5/6 16:01
     * @return java.lang.String
     **/
    public static String sendJsonPost(String url, String param) {
        OutputStreamWriter out = null;
        BufferedReader in = null;
        String result = "";
        try {
            URL realUrl = new URL(url);
            // 打开和URL之间的连接
            URLConnection conn = realUrl.openConnection();
            // 设置通用的请求属性
            conn.setRequestProperty("accept", "application/json");
            conn.setRequestProperty("Content-Type","application/json;charset=UTF-8");
            conn.setRequestProperty("connection", "Keep-Alive");
            conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            // 发送POST请求必须设置如下两行
            conn.setDoOutput(true);
            conn.setDoInput(true);
            //设置请求编码格式
            conn.setRequestProperty("Accept-Charset", "UTF-8");
            // 文件流编码设置
            out = new OutputStreamWriter(conn.getOutputStream(), "UTF-8");
            out.write(param);
            // flush输出流的缓冲
            out.flush();
            // 定义BufferedReader输入流来读取URL的响应
            in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));
            String line;
            while ((line = in.readLine()) != null) {
                result += line;
            }
        } catch (Exception e) {
            System.out.println("发送 POST 请求出现异常！" + e.getMessage());
        }
        // 使用finally块来关闭输出流、输入流
        finally {
            try {
                if (out != null) {
                    out.close();
                }
                if (in != null) {
                    in.close();
                }
            } catch (IOException ex) {
                System.out.println("关闭输入输出流异常" + ex.getMessage());
            }
        }
        return result;
    }

    /**
     * 发送form-data请求返回json结果
     * @Param [apiUrl, param]
     * @Author ywx
     * @Date 2021/5/14 17:41
     * @return java.lang.String
     **/
    /*public static String sendFormData(String apiUrl, MultiValueMap<String, String> param) {
        try {
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
            List<MediaType> acceptableMediaTypes = new ArrayList<>();
            acceptableMediaTypes.add(MediaType.APPLICATION_FORM_URLENCODED);
            acceptableMediaTypes.add(MediaType.APPLICATION_PROBLEM_JSON_UTF8);
            HttpEntity<MultiValueMap<String, String>> requestParams = new HttpEntity<>(param, headers);
            ResponseEntity<String> response = new RestTemplate().postForEntity(apiUrl, requestParams, String.class);
            String result = response.getBody();
            return result;
        } catch (RestClientException e) {
            log.error("发送form-data请求失败",e.getMessage());
            return null;
        }
    }*/

    static String CHN_NUMBER[] = { "零", "一", "二", "三", "四", "五", "六", "七", "八", "九" };
    static String CHN_UNIT[] = { "", "十", "百", "千" }; // 权位
    static String CHN_UNIT_SECTION[] = { "", "万", "亿", "万亿" }; // 节权位

    /**
     * 阿拉伯数字转换为中文数字的核心算法实现。
     * @return
     */
    public static String NumberToChn(int num) {
        StringBuffer returnStr = new StringBuffer();
        Boolean needZero = false;
        int pos = 0; // 节权位的位置
        if (num == 0) {
            // 如果num为0，进行特殊处理。
            returnStr.insert(0, CHN_NUMBER[0]);
        }
        while (num > 0) {
            int section = num % 10000;
            if (needZero) {
                returnStr.insert(0, CHN_NUMBER[0]);
            }
            String sectionToChn = SectionNumToChn(section);
            // 判断是否需要节权位
            sectionToChn += (section != 0) ? CHN_UNIT_SECTION[pos] : CHN_UNIT_SECTION[0];
            returnStr.insert(0, sectionToChn);
            needZero = ((section < 1000 && section > 0) ? true : false); // 判断section中的千位上是不是为零，若为零应该添加一个零。
            pos++;
            num = num / 10000;
        }
        return returnStr.toString().replace("一十", "十");
    }

    /**
     * 将四位的section转换为中文数字
     *
     * @param section
     * @return
     */
    public static String SectionNumToChn(int section) {
        StringBuffer returnStr = new StringBuffer();
        int unitPos = 0; // 节权位的位置编号，0-3依次为个十百千;

        Boolean zero = true;
        while (section > 0) {

            int v = (section % 10);
            if (v == 0) {
                if ((section == 0) || !zero) {
                    zero = true; /* 需要补0，zero的作用是确保对连续的多个0，只补一个中文零 */
                    // chnStr.insert(0, chnNumChar[v]);
                    returnStr.insert(0, CHN_NUMBER[v]);
                }
            } else {
                zero = false; // 至少有一个数字不是0
                StringBuffer tempStr = new StringBuffer(CHN_NUMBER[v]);// 数字v所对应的中文数字
                tempStr.append(CHN_UNIT[unitPos]); // 数字v所对应的中文权位
                returnStr.insert(0, tempStr);
            }
            unitPos++; // 移位
            section = section / 10;
        }
        return returnStr.toString();
    }

    /*
     * @Description 获取登录IP
     * @Date 2023/11/15 11:30:26
     * @Author WN
     * @Param [request]
     * @Return java.lang.String
     **/
    public static String getIpAddress(HttpServletRequest request){
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }

        if(StringUtils.isNotEmpty(ip)){
            String[] ipArr = ip.split(",");
            if(ipArr.length > 0){
                ip = ipArr[0];
            }
        }
        return ip;
    }

    /**
     * 生成id
     * @Param []
     * @Author ywx
     * @Date 2020/12/24 15:02
     * @return java.lang.String
     **/
    public static String getId(Integer id,Integer nummin,Integer nummax,Integer numadd){
        if (id == 0 || id.equals(nummax)) {
            id = nummin;
        } else {
            id ++;
        }
        String _num = (getSecondTimestamp() + numadd) +"" + id;
        long num = Long.parseLong(_num);
        return Long.toString(num,36).toUpperCase();
    }

    /**
     * 字符串是否包含在集合中的字符串集合
     * @Param [num, nums]
     * @Author ywx
     * @Date 2020/12/24 14:10
     * @return boolean
     **/
    public static boolean strInList(String num,List<String> nums){
        if (nums.contains(num)){
            return true;
        } else {
            return false;
        }
    }

    //计算比例
    public static String getFs(int a, int b){  // 设置分子和分母
        int numerator;  // 分子
        int denominator; // 分母
        if(a == 0 || b == 0){
            numerator = a;
            denominator = b;
        }else{
            int c = f(Math.abs(a),Math.abs(b));         // 计算最大公约数
            numerator = a / c;
            denominator = b / c;
            if(numerator<0 && denominator<0){
                numerator = - numerator;
                denominator = - denominator;
            }
        }
        return numerator + ":" + denominator;
    }

    static int f(int a,int b){  // 求a和b的最大公约数
        if(a < b){
            int c = a;
            a = b;
            b = c;
        }
        int r = a % b;
        while(r != 0){
            a = b;
            b = r;
            r = a % b;
        }
        return b;
    }

    //求一个数组的最大公约数
    public static int getArrMaxGys(int nums[]){
        int a=nums[0];int b=nums[1];int i=0;
        int temp;
        if(a<b){
            temp = a;
            a = b;
            b = temp;
            temp = a % b;
            while (temp != 0) {
                a = b;
                b = temp;
                temp = a % b;
            }
            nums[i + 1] = b;
        }
        return nums[nums.length - 1];
    }

    /**
     * 密码加密
     *
     * @param [pwd]
     * @return java.lang.String
     * @author ywx
     * @date 2022/5/20 10:18
     */
    public static String getMD5Pwd(String pwd) {
        if (isEmpty(pwd)) return null;
        MD5 md5 = new MD5();
        return md5.getMD5ofStr(md5.getMD5ofStr(md5.getMD5ofStr(pwd)));
    }

    //处理题型中客观题1选择题2多选题456判断7完型填空全选择（弃用）8阅读理解全选择（弃用）10任务行阅读券选择（弃用）16完型填空复合17任务型阅读复合
    private static int[] octypelist = new int[]{1,2,4,5,6,7,8,10,16,17};
    /**
     * @Description 判断ctype是否是客观题
     * @Param [ctype]
     * @Return boolean 如果是客观题返回true,否则返回false
     * @Author wn
     * @Date 2022/7/20 15:20
     **/
    public static boolean isObjectiveQuestion(Integer ctype){
        if(ctype != null && ctype != 0){
            return IntStream.of(octypelist).anyMatch(x -> x == ctype);
        }else{
            return false;
        }
    }

    //主观题处理题型
    public static int[] subjectiveList = new int[]{3,9,13,14,15};
    //客观题题型处理
    public static int[] objectiveList = new int[]{1,2,4,5,6,7,8,10,16,17};

    //简单题进行分项展示
    private static int[] simplelist = new int[]{1,2,4,5,6,7,8,10,16,17};
    public static boolean isSimpleCheckQuestion(Integer ctype){
        if(ctype != null && ctype != 0){
            return IntStream.of(simplelist).anyMatch(x -> x == ctype);
        }else{
            return false;
        }
    }

    //处理题型中复合题
    private static int[] cctypelist = new int[]{11,16,17};
    /*
     * 判断ctype是否是复合题
     * @Param [ctype]
     * @Author ywx
     * @Date 2022/10/18 19:11
     * @return boolean
     **/
    public static boolean isComplexQuestion(Integer ctype){
        if(ctype != null && ctype != 0){
            return IntStream.of(cctypelist).anyMatch(x -> x == ctype);
        }else{
            return false;
        }
    }

    /*
    * @Description 年级集合(导入时根据名次获取对应数字)
    * @Author WN
    * @Date 2023/4/11 10:07
    */
    public static Map<String, Integer> listGrade() {
        Map m = new HashMap();
        m.put("一年级", 1);
        m.put("二年级", 2);
        m.put("三年级", 3);
        m.put("四年级", 4);
        m.put("五年级", 5);
        m.put("六年级", 6);
        m.put("七年级", 7);
        m.put("八年级", 8);
        m.put("九年级", 9);
        m.put("高一", 10);
        m.put("高二", 11);
        m.put("高三", 12);
        return m;
    }

    /*
    * @Description 根据年级id获取年级名次
    * @Author WN
    * @Date 2023/4/11 10:07
    */
    public static String getGradeName(Integer gradeid){
        Map gradeMap = new HashMap();
        gradeMap.put(1, "一年级");
        gradeMap.put(2, "二年级");
        gradeMap.put(3, "三年级");
        gradeMap.put(4, "四年级");
        gradeMap.put(5, "五年级");
        gradeMap.put(6, "六年级");
        gradeMap.put(7, "七年级");
        gradeMap.put(8, "八年级");
        gradeMap.put(9, "九年级");
        gradeMap.put(10, "高一");
        gradeMap.put(11, "高二");
        gradeMap.put(12, "高三");
        String gradename = gradeMap.get(gradeid).toString();
        return gradename;
    }

    /**
     * 分数百分比
     *
     * @param v1 分数
     * @param v2 占比如80%写作0.8
     * @return
     */
    public static double getScoreRateScore(double v1, double v2) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));

        return b1.multiply(b2).setScale(0, BigDecimal.ROUND_HALF_UP).doubleValue();
    }

    /**
     * @Description 分值平均后得到的值，如果整除返回数组元素1，未整除返回元素数量2
     * @Param [num, score] 数量，分值
     * @Return java.lang.Double[] 第一个未平均分值，第二个值为最后一个数的分值
     * @Author wn
     * @Date 2022/8/18 11:07
     **/
    public static Double[] getPointAvgScore(int num, Double score) {
        BigDecimal bScore = new BigDecimal(Double.toString(score));
        BigDecimal bNum = new BigDecimal(Integer.toString(num));

        //2023-07-04通过四舍五入进行计算,
        BigDecimal avg = bScore.divide(bNum, 1, BigDecimal.ROUND_HALF_UP);
        BigDecimal avgAllScore = avg.multiply(bNum);//平均分乘以数量得到的分值
        if (bScore.equals(avgAllScore)) {
            //说明整除
            return new Double[]{avg.doubleValue()};
        } else {
            //未整除，计算最后剩余分值，subtract总分减去平均分和数量的积
            // 平均分加上剩余得分得到最后一个分值
            BigDecimal lastScore = avg.add(bScore.subtract(avgAllScore));
            return new Double[]{avg.doubleValue(), lastScore.doubleValue()};
        }
    }

    /**
     * @Description 是否是白名单接口
     * @Date 2023/11/3 9:26
     * @Author YWX
     * @Param [requestURI, whitelistAPI]
     * @Return boolean
     **/
    public static boolean whiteAPI(String requestURI, String whitelistAPI) {
        boolean rtn = false;
        if (isEmpty(whitelistAPI)) return rtn;

        String[] apis = whitelistAPI.split(",");
        for (String api : apis) {
            if (requestURI.contains(api)) {
                rtn = true;
                break;
            }
        }
        return rtn;
    }
}
