Java secret - 02

文章目录

  • Java secret - 02
    • HTML过滤器
    • Http客户端工具类
    • IP工具类
    • Jwt工具类 | 会员端
    • Jwt工具类 | 平台端
    • Jwt工具类
    • 分页工具类
    • Excel数据格式处理适配器
    • Excel相关处理
    • 客户端工具类
    • sql操作工具类
    • 时间工具类
    • 错误信息处理类
    • 树结构组装工具类
    • Base 基类
    • Basis 基类
    • Tree 基类
    • Base 混合基类
    • Basis 混合基类
    • Tree 混合基类
    • Base 基类 对象映射器
    • Tree 基类 对象映射器
    • Base 基类 对象映射器
    • Tree 基类 对象映射器

Java secret - 02

HTML过滤器

package com.xueyi.common.core.utils.html;

import lombok.Getter;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * HTML过滤器,用于去除XSS漏洞隐患。
 *
 * @author xueyi
 */
public final class HTMLFilter {

    /**
     * regex flag union representing /si modifiers in php
     **/
    private static final int REGEX_FLAGS_SI = Pattern.CASE_INSENSITIVE | Pattern.DOTALL;
    private static final Pattern P_COMMENTS = Pattern.compile("", Pattern.DOTALL);
    private static final Pattern P_COMMENT = Pattern.compile("^!--(.*)--$", REGEX_FLAGS_SI);
    private static final Pattern P_TAGS = Pattern.compile("<(.*?)>", Pattern.DOTALL);
    private static final Pattern P_END_TAG = Pattern.compile("^/([a-z0-9]+)", REGEX_FLAGS_SI);
    private static final Pattern P_START_TAG = Pattern.compile("^([a-z0-9]+)(.*?)(/?)$", REGEX_FLAGS_SI);
    private static final Pattern P_QUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)=([\"'])(.*?)\\2", REGEX_FLAGS_SI);
    private static final Pattern P_UNQUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)(=)([^\"\\s']+)", REGEX_FLAGS_SI);
    private static final Pattern P_PROTOCOL = Pattern.compile("^([^:]+):", REGEX_FLAGS_SI);
    private static final Pattern P_ENTITY = Pattern.compile("&#(\\d+);?");
    private static final Pattern P_ENTITY_UNICODE = Pattern.compile("&#x([0-9a-f]+);?");
    private static final Pattern P_ENCODE = Pattern.compile("%([0-9a-f]{2});?");
    private static final Pattern P_VALID_ENTITIES = Pattern.compile("&([^&;]*)(?=(;|&|$))");
    private static final Pattern P_VALID_QUOTES = Pattern.compile("(>|^)([^<]+?)(<|$)", Pattern.DOTALL);
    private static final Pattern P_END_ARROW = Pattern.compile("^>");
    private static final Pattern P_BODY_TO_END = Pattern.compile("<([^>]*?)(?=<|$)");
    private static final Pattern P_XML_CONTENT = Pattern.compile("(^|>)([^<]*?)(?=>)");
    private static final Pattern P_STRAY_LEFT_ARROW = Pattern.compile("<([^>]*?)(?=<|$)");
    private static final Pattern P_STRAY_RIGHT_ARROW = Pattern.compile("(^|>)([^<]*?)(?=>)");
    private static final Pattern P_AMP = Pattern.compile("&");
    private static final Pattern P_QUOTE = Pattern.compile("\"");
    private static final Pattern P_LEFT_ARROW = Pattern.compile("<");
    private static final Pattern P_RIGHT_ARROW = Pattern.compile(">");
    private static final Pattern P_BOTH_ARROWS = Pattern.compile("<>");

    // @xxx could grow large... maybe use sesat's ReferenceMap
    private static final ConcurrentMap<String, Pattern> P_REMOVE_PAIR_BLANKS = new ConcurrentHashMap<>();
    private static final ConcurrentMap<String, Pattern> P_REMOVE_SELF_BLANKS = new ConcurrentHashMap<>();

    /**
     * set of allowed html elements, along with allowed attributes for each element
     **/
    private final Map<String, List<String>> vAllowed;
    /**
     * counts of open tags for each (allowable) html element
     **/
    private final Map<String, Integer> vTagCounts = new HashMap<>();

    /**
     * html elements which must always be self-closing (e.g. "")
     **/
    private final String[] vSelfClosingTags;
    /**
     * html elements which must always have separate opening and closing tags (e.g. "")
     **/
    private final String[] vNeedClosingTags;
    /**
     * set of disallowed html elements
     **/
    private final String[] vDisallowed;
    /**
     * attributes which should be checked for valid protocols
     **/
    private final String[] vProtocolAtts;
    /**
     * allowed protocols
     **/
    private final String[] vAllowedProtocols;
    /**
     * tags which should be removed if they contain no content (e.g. "" or "")
     **/
    private final String[] vRemoveBlanks;
    /**
     * entities allowed within html markup
     **/
    private final String[] vAllowedEntities;
    /**
     * flag determining whether comments are allowed in input String.
     */
    private final boolean stripComment;
    private final boolean encodeQuotes;
    /**
     * flag determining whether to try to make tags when presented with "unbalanced" angle brackets (e.g. ""
     * becomes " text "). If set to false, unbalanced angle brackets will be html escaped.
     */
    @Getter
    private final boolean alwaysMakeTags;

    /**
     * Default constructor.
     */
    public HTMLFilter() {
        vAllowed = new HashMap<>();

        final ArrayList<String> a_atts = new ArrayList<>();
        a_atts.add("href");
        a_atts.add("target");
        vAllowed.put("a", a_atts);

        final ArrayList<String> img_atts = new ArrayList<>();
        img_atts.add("src");
        img_atts.add("width");
        img_atts.add("height");
        img_atts.add("alt");
        vAllowed.put("img", img_atts);

        final ArrayList<String> no_atts = new ArrayList<>();
        vAllowed.put("b", no_atts);
        vAllowed.put("strong", no_atts);
        vAllowed.put("i", no_atts);
        vAllowed.put("em", no_atts);

        vSelfClosingTags = new String[]{"img"};
        vNeedClosingTags = new String[]{"a", "b", "strong", "i", "em"};
        vDisallowed = new String[]{};
        vAllowedProtocols = new String[]{"http", "mailto", "https"}; // no ftp.
        vProtocolAtts = new String[]{"src", "href"};
        vRemoveBlanks = new String[]{"a", "b", "strong", "i", "em"};
        vAllowedEntities = new String[]{"amp", "gt", "lt", "quot"};
        stripComment = true;
        encodeQuotes = true;
        alwaysMakeTags = false;
    }

    /**
     * Map-parameter configurable constructor.
     *
     * @param conf map containing configuration. keys match field names.
     */
    @SuppressWarnings("unchecked")
    public HTMLFilter(final Map<String, Object> conf) {

        assert conf.containsKey("vAllowed") : "configuration requires vAllowed";
        assert conf.containsKey("vSelfClosingTags") : "configuration requires vSelfClosingTags";
        assert conf.containsKey("vNeedClosingTags") : "configuration requires vNeedClosingTags";
        assert conf.containsKey("vDisallowed") : "configuration requires vDisallowed";
        assert conf.containsKey("vAllowedProtocols") : "configuration requires vAllowedProtocols";
        assert conf.containsKey("vProtocolAtts") : "configuration requires vProtocolAtts";
        assert conf.containsKey("vRemoveBlanks") : "configuration requires vRemoveBlanks";
        assert conf.containsKey("vAllowedEntities") : "configuration requires vAllowedEntities";

        vAllowed = Collections.unmodifiableMap((HashMap<String, List<String>>) conf.get("vAllowed"));
        vSelfClosingTags = (String[]) conf.get("vSelfClosingTags");
        vNeedClosingTags = (String[]) conf.get("vNeedClosingTags");
        vDisallowed = (String[]) conf.get("vDisallowed");
        vAllowedProtocols = (String[]) conf.get("vAllowedProtocols");
        vProtocolAtts = (String[]) conf.get("vProtocolAtts");
        vRemoveBlanks = (String[]) conf.get("vRemoveBlanks");
        vAllowedEntities = (String[]) conf.get("vAllowedEntities");
        stripComment = conf.containsKey("stripComment") ? (Boolean) conf.get("stripComment") : true;
        encodeQuotes = conf.containsKey("encodeQuotes") ? (Boolean) conf.get("encodeQuotes") : true;
        alwaysMakeTags = conf.containsKey("alwaysMakeTags") ? (Boolean) conf.get("alwaysMakeTags") : true;
    }

    private void reset() {
        vTagCounts.clear();
    }

    // ---------------------------------------------------------------
    // my versions of some PHP library functions
    public static String chr(final int decimal) {
        return String.valueOf((char) decimal);
    }

    public static String htmlSpecialChars(final String s) {
        String result = s;
        result = regexReplace(P_AMP, "&", result);
        result = regexReplace(P_QUOTE, """, result);
        result = regexReplace(P_LEFT_ARROW, "<", result);
        result = regexReplace(P_RIGHT_ARROW, ">", result);
        return result;
    }

    // ---------------------------------------------------------------

    /**
     * given a user submitted input String, filter out any invalid or restricted html.
     *
     * @param input text (i.e. submitted by a user) than may contain html
     * @return "clean" version of input, with only valid, whitelisted html elements allowed
     */
    public String filter(final String input) {
        reset();
        String s = input;

        s = escapeComments(s);

        s = balanceHTML(s);

        s = checkTags(s);

        s = processRemoveBlanks(s);

        // s = validateEntities(s);

        return s;
    }

    public boolean isStripComments() {
        return stripComment;
    }

    private String escapeComments(final String s) {
        final Matcher m = P_COMMENTS.matcher(s);
        final StringBuffer buf = new StringBuffer();
        if (m.find()) {
            final String match = m.group(1); // (.*?)
            m.appendReplacement(buf, Matcher.quoteReplacement(""));
        }
        m.appendTail(buf);

        return buf.toString();
    }

    private String balanceHTML(String s) {
        if (alwaysMakeTags) {
            //
            // try and form html
            //
            s = regexReplace(P_END_ARROW, "", s);
            // 不追加结束标签
            s = regexReplace(P_BODY_TO_END, "<$1>", s);
            s = regexReplace(P_XML_CONTENT, "$1<$2", s);

        } else {
            //
            // escape stray brackets
            //
            s = regexReplace(P_STRAY_LEFT_ARROW, "<$1", s);
            s = regexReplace(P_STRAY_RIGHT_ARROW, "$1$2><", s);

            //
            // the last regexp causes '<>' entities to appear
            // (we need to do a lookahead assertion so that the last bracket can
            // be used in the next pass of the regexp)
            //
            s = regexReplace(P_BOTH_ARROWS, "", s);
        }

        return s;
    }

    private String checkTags(String s) {
        Matcher m = P_TAGS.matcher(s);

        final StringBuffer buf = new StringBuffer();
        while (m.find()) {
            String replaceStr = m.group(1);
            replaceStr = processTag(replaceStr);
            m.appendReplacement(buf, Matcher.quoteReplacement(replaceStr));
        }
        m.appendTail(buf);

        // these get tallied in processTag
        // (remember to reset before subsequent calls to filter method)
        final StringBuilder sBuilder = new StringBuilder(buf.toString());
        for (String key : vTagCounts.keySet()) {
            for (int ii = 0; ii < vTagCounts.get(key); ii++) {
                sBuilder.append(").append(key).append(">");
            }
        }
        s = sBuilder.toString();

        return s;
    }

    private String processRemoveBlanks(final String s) {
        String result = s;
        for (String tag : vRemoveBlanks) {
            if (!P_REMOVE_PAIR_BLANKS.containsKey(tag)) {
                P_REMOVE_PAIR_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?> + tag + ">"));
            }
            result = regexReplace(P_REMOVE_PAIR_BLANKS.get(tag), "", result);
            if (!P_REMOVE_SELF_BLANKS.containsKey(tag)) {
                P_REMOVE_SELF_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?/>"));
            }
            result = regexReplace(P_REMOVE_SELF_BLANKS.get(tag), "", result);
        }

        return result;
    }

    private static String regexReplace(final Pattern regex_pattern, final String replacement, final String s) {
        Matcher m = regex_pattern.matcher(s);
        return m.replaceAll(replacement);
    }

    private String processTag(final String s) {
        // ending tags
        Matcher m = P_END_TAG.matcher(s);
        if (m.find()) {
            final String name = m.group(1).toLowerCase();
            if (allowed(name)) {
                if (!inArray(name, vSelfClosingTags)) {
                    if (vTagCounts.containsKey(name)) {
                        vTagCounts.put(name, vTagCounts.get(name) - 1);
                        return " + name + ">";
                    }
                }
            }
        }

        // starting tags
        m = P_START_TAG.matcher(s);
        if (m.find()) {
            final String name = m.group(1).toLowerCase();
            final String body = m.group(2);
            String ending = m.group(3);

            // debug( "in a starting tag, name='" + name + "'; body='" + body + "'; ending='" + ending + "'" );
            if (allowed(name)) {
                final StringBuilder params = new StringBuilder();

                final Matcher m2 = P_QUOTED_ATTRIBUTES.matcher(body);
                final Matcher m3 = P_UNQUOTED_ATTRIBUTES.matcher(body);
                final List<String> paramNames = new ArrayList<>();
                final List<String> paramValues = new ArrayList<>();
                while (m2.find()) {
                    paramNames.add(m2.group(1)); // ([a-z0-9]+)
                    paramValues.add(m2.group(3)); // (.*?)
                }
                while (m3.find()) {
                    paramNames.add(m3.group(1)); // ([a-z0-9]+)
                    paramValues.add(m3.group(3)); // ([^\"\\s']+)
                }

                String paramName, paramValue;
                for (int ii = 0; ii < paramNames.size(); ii++) {
                    paramName = paramNames.get(ii).toLowerCase();
                    paramValue = paramValues.get(ii);

                    // debug( "paramName='" + paramName + "'" );
                    // debug( "paramValue='" + paramValue + "'" );
                    // debug( "allowed? " + vAllowed.get( name ).contains( paramName ) );

                    if (allowedAttribute(name, paramName)) {
                        if (inArray(paramName, vProtocolAtts)) {
                            paramValue = processParamProtocol(paramValue);
                        }
                        params.append(' ').append(paramName).append("=\\\"").append(paramValue).append("\\\"");
                    }
                }

                if (inArray(name, vSelfClosingTags)) {
                    ending = " /";
                }

                if (inArray(name, vNeedClosingTags)) {
                    ending = "";
                }

                if (ending == null || ending.length() < 1) {
                    if (vTagCounts.containsKey(name)) {
                        vTagCounts.put(name, vTagCounts.get(name) + 1);
                    } else {
                        vTagCounts.put(name, 1);
                    }
                } else {
                    ending = " /";
                }
                return "<" + name + params + ending + ">";
            } else {
                return "";
            }
        }

        // comments
        m = P_COMMENT.matcher(s);
        if (!stripComment && m.find()) {
            return "<" + m.group() + ">";
        }

        return "";
    }

    private String processParamProtocol(String s) {
        s = decodeEntities(s);
        final Matcher m = P_PROTOCOL.matcher(s);
        if (m.find()) {
            final String protocol = m.group(1);
            if (!inArray(protocol, vAllowedProtocols)) {
                // bad protocol, turn into local anchor link instead
                s = "#" + s.substring(protocol.length() + 1);
                if (s.startsWith("#//")) {
                    s = "#" + s.substring(3);
                }
            }
        }

        return s;
    }

    private String decodeEntities(String s) {
        StringBuffer buf = new StringBuffer();

        Matcher m = P_ENTITY.matcher(s);
        while (m.find()) {
            final String match = m.group(1);
            final int decimal = Integer.decode(match);
            m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
        }
        m.appendTail(buf);
        s = buf.toString();

        buf = new StringBuffer();
        m = P_ENTITY_UNICODE.matcher(s);
        while (m.find()) {
            final String match = m.group(1);
            final int decimal = Integer.valueOf(match, 16);
            m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
        }
        m.appendTail(buf);
        s = buf.toString();

        buf = new StringBuffer();
        m = P_ENCODE.matcher(s);
        while (m.find()) {
            final String match = m.group(1);
            final int decimal = Integer.valueOf(match, 16);
            m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
        }
        m.appendTail(buf);
        s = buf.toString();

        s = validateEntities(s);
        return s;
    }

    private String validateEntities(final String s) {
        StringBuffer buf = new StringBuffer();

        // validate entities throughout the string
        Matcher m = P_VALID_ENTITIES.matcher(s);
        while (m.find()) {
            final String one = m.group(1); // ([^&;]*)
            final String two = m.group(2); // (?=(;|&|$))
            m.appendReplacement(buf, Matcher.quoteReplacement(checkEntity(one, two)));
        }
        m.appendTail(buf);

        return encodeQuotes(buf.toString());
    }

    private String encodeQuotes(final String s) {
        if (encodeQuotes) {
            StringBuffer buf = new StringBuffer();
            Matcher m = P_VALID_QUOTES.matcher(s);
            while (m.find()) {
                final String one = m.group(1); // (>|^)
                final String two = m.group(2); // ([^<]+?)
                final String three = m.group(3); // (<|$)
                // 不替换双引号为",防止json格式无效 regexReplace(P_QUOTE, """, two)
                m.appendReplacement(buf, Matcher.quoteReplacement(one + two + three));
            }
            m.appendTail(buf);
            return buf.toString();
        } else {
            return s;
        }
    }

    private String checkEntity(final String preamble, final String term) {

        return ";".equals(term) && isValidEntity(preamble) ? '&' + preamble : "&" + preamble;
    }

    private boolean isValidEntity(final String entity) {
        return inArray(entity, vAllowedEntities);
    }

    private static boolean inArray(final String s, final String[] array) {
        for (String item : array) {
            if (item != null && item.equals(s)) {
                return true;
            }
        }
        return false;
    }

    private boolean allowed(final String name) {
        return (vAllowed.isEmpty() || vAllowed.containsKey(name)) && !inArray(name, vDisallowed);
    }

    private boolean allowedAttribute(final String name, final String paramName) {
        return allowed(name) && (vAllowed.isEmpty() || vAllowed.get(name).contains(paramName));
    }
}

Http客户端工具类

package com.xueyi.common.core.utils.http;

/**
 * Http客户端工具类
 *
 * @author xueyi
 */
public class HttpUtil extends cn.hutool.http.HttpUtil {
}

IP工具类

package com.xueyi.common.core.utils.ip;

import com.xueyi.common.core.utils.core.ArrayUtil;
import com.xueyi.common.core.utils.core.StrUtil;
import com.xueyi.common.core.utils.servlet.ServletUtil;
import jakarta.servlet.http.HttpServletRequest;

import java.net.InetAddress;
import java.net.UnknownHostException;

/**
 * IP工具类
 *
 * @author xueyi
 */
public class IpUtil {

    public final static String REGX_0_255 = "(25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]\\d|\\d)";
    // 匹配 ip
    public final static String REGX_IP = "((" + REGX_0_255 + "\\.){3}" + REGX_0_255 + ")";
    public final static String REGX_IP_WILDCARD = "(((\\*\\.){3}\\*)|(" + REGX_0_255 + "(\\.\\*){3})|(" + REGX_0_255 + "\\." + REGX_0_255 + ")(\\.\\*){2}" + "|((" + REGX_0_255 + "\\.){3}\\*))";
    // 匹配网段
    public final static String REGX_IP_SEG = "(" + REGX_IP + "\\-" + REGX_IP + ")";

    /**
     * 获取客户端IP
     *
     * @return IP地址
     */
    public static String getIpAddr() {
        return getIpAddr(ServletUtil.getRequest());
    }

    /**
     * 获取客户端IP
     *
     * @param request 请求对象
     * @return IP地址
     */
    public static String getIpAddr(HttpServletRequest request) {
        if (request == null) {
            return "unknown";
        }
        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("X-Forwarded-For");
        }
        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("X-Real-IP");
        }

        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }

        return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : getMultistageReverseProxyIp(ip);
    }

    /**
     * 检查是否为内部IP地址
     *
     * @param ip IP地址
     * @return 结果
     */
    public static boolean internalIp(String ip) {
        byte[] addr = textToNumericFormatV4(ip);
        return internalIp(addr) || "127.0.0.1".equals(ip);
    }

    /**
     * 检查是否为内部IP地址
     *
     * @param addr byte地址
     * @return 结果
     */
    private static boolean internalIp(byte[] addr) {
        if (ArrayUtil.isEmpty(addr) || addr.length < 2) {
            return true;
        }
        final byte b0 = addr[0];
        final byte b1 = addr[1];
        // 10.x.x.x/8
        final byte SECTION_1 = 0x0A;
        // 172.16.x.x/12
        final byte SECTION_2 = (byte) 0xAC;
        final byte SECTION_3 = (byte) 0x10;
        final byte SECTION_4 = (byte) 0x1F;
        // 192.168.x.x/16
        final byte SECTION_5 = (byte) 0xC0;
        final byte SECTION_6 = (byte) 0xA8;
        switch (b0) {
            case SECTION_1:
                return true;
            case SECTION_2:
                if (b1 >= SECTION_3 && b1 <= SECTION_4) {
                    return true;
                }
            case SECTION_5:
                if (b1 == SECTION_6) {
                    return true;
                }
            default:
                return false;
        }
    }

    /**
     * 将IPv4地址转换成字节
     *
     * @param text IPv4地址
     * @return byte 字节
     */
    public static byte[] textToNumericFormatV4(String text) {
        if (text.length() == 0) {
            return null;
        }

        byte[] bytes = new byte[4];
        String[] elements = text.split("\\.", -1);
        try {
            long l;
            int i;
            switch (elements.length) {
                case 1:
                    l = Long.parseLong(elements[0]);
                    if ((l < 0L) || (l > 4294967295L)) {
                        return null;
                    }
                    bytes[0] = (byte) (int) (l >> 24 & 0xFF);
                    bytes[1] = (byte) (int) ((l & 0xFFFFFF) >> 16 & 0xFF);
                    bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF);
                    bytes[3] = (byte) (int) (l & 0xFF);
                    break;
                case 2:
                    l = Integer.parseInt(elements[0]);
                    if ((l < 0L) || (l > 255L)) {
                        return null;
                    }
                    bytes[0] = (byte) (int) (l & 0xFF);
                    l = Integer.parseInt(elements[1]);
                    if ((l < 0L) || (l > 16777215L)) {
                        return null;
                    }
                    bytes[1] = (byte) (int) (l >> 16 & 0xFF);
                    bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF);
                    bytes[3] = (byte) (int) (l & 0xFF);
                    break;
                case 3:
                    for (i = 0; i < 2; ++i) {
                        l = Integer.parseInt(elements[i]);
                        if ((l < 0L) || (l > 255L)) {
                            return null;
                        }
                        bytes[i] = (byte) (int) (l & 0xFF);
                    }
                    l = Integer.parseInt(elements[2]);
                    if ((l < 0L) || (l > 65535L)) {
                        return null;
                    }
                    bytes[2] = (byte) (int) (l >> 8 & 0xFF);
                    bytes[3] = (byte) (int) (l & 0xFF);
                    break;
                case 4:
                    for (i = 0; i < 4; ++i) {
                        l = Integer.parseInt(elements[i]);
                        if ((l < 0L) || (l > 255L)) {
                            return null;
                        }
                        bytes[i] = (byte) (int) (l & 0xFF);
                    }
                    break;
                default:
                    return null;
            }
        } catch (NumberFormatException e) {
            return null;
        }
        return bytes;
    }

    /**
     * 获取IP地址
     *
     * @return 本地IP地址
     */
    public static String getHostIp() {
        try {
            return InetAddress.getLocalHost().getHostAddress();
        } catch (UnknownHostException ignored) {
        }
        return "127.0.0.1";
    }

    /**
     * 获取主机名
     *
     * @return 本地主机名
     */
    public static String getHostName() {
        try {
            return InetAddress.getLocalHost().getHostName();
        } catch (UnknownHostException ignored) {
        }
        return "未知";
    }

    /**
     * 从多级反向代理中获得第一个非unknown IP地址
     *
     * @param ip 获得的IP地址
     * @return 第一个非unknown IP地址
     */
    public static String getMultistageReverseProxyIp(String ip) {
        // 多级反向代理检测
        if (ip != null && ip.indexOf(StrUtil.COMMA) > 0) {
            final String[] ips = ip.trim().split(StrUtil.COMMA);
            for (String subIp : ips) {
                if (!isUnknown(subIp)) {
                    ip = subIp;
                    break;
                }
            }
        }
        return StrUtil.sub(ip, 0, 255);
    }

    /**
     * 检测给定字符串是否为未知,多用于检测HTTP请求相关
     *
     * @param checkString 被检测的字符串
     * @return 是否未知
     */
    public static boolean isUnknown(String checkString) {
        return StrUtil.isBlank(checkString) || StrUtil.UNKNOWN.equalsIgnoreCase(checkString);
    }


    /**
     * 是否为IP
     */
    public static boolean isIP(String ip) {
        return StrUtil.isNotBlank(ip) && ip.matches(REGX_IP);
    }

    /**
     * 是否为IP,或 *为间隔的通配符地址
     */
    public static boolean isIpWildCard(String ip) {
        return StrUtil.isNotBlank(ip) && ip.matches(REGX_IP_WILDCARD);
    }

    /**
     * 检测参数是否在ip通配符里
     */
    public static boolean ipIsInWildCardNoCheck(String ipWildCard, String ip) {
        String[] s1 = ipWildCard.split("\\.");
        String[] s2 = ip.split("\\.");
        boolean isMatchedSeg = true;
        for (int i = 0; i < s1.length && !s1[i].equals("*"); i++) {
            if (!s1[i].equals(s2[i])) {
                isMatchedSeg = false;
                break;
            }
        }
        return isMatchedSeg;
    }

    /**
     * 是否为特定格式如:“10.10.10.1-10.10.10.99”的ip段字符串
     */
    public static boolean isIPSegment(String ipSeg) {
        return StrUtil.isNotBlank(ipSeg) && ipSeg.matches(REGX_IP_SEG);
    }

    /**
     * 判断ip是否在指定网段中
     */
    public static boolean ipIsInNetNoCheck(String iparea, String ip) {
        int idx = iparea.indexOf('-');
        String[] sips = iparea.substring(0, idx).split("\\.");
        String[] sipe = iparea.substring(idx + 1).split("\\.");
        String[] sipt = ip.split("\\.");
        long ips = 0L, ipe = 0L, ipt = 0L;
        for (int i = 0; i < 4; ++i) {
            ips = ips << 8 | Integer.parseInt(sips[i]);
            ipe = ipe << 8 | Integer.parseInt(sipe[i]);
            ipt = ipt << 8 | Integer.parseInt(sipt[i]);
        }
        if (ips > ipe) {
            long t = ips;
            ips = ipe;
            ipe = t;
        }
        return ips <= ipt && ipt <= ipe;
    }

    /**
     * 校验ip是否符合过滤串规则
     *
     * @param filter 过滤IP列表,支持后缀'*'通配,支持网段如:`10.10.10.1-10.10.10.99`
     * @param ip     校验IP地址
     * @return boolean 结果
     */
    public static boolean isMatchedIp(String filter, String ip) {
        if (StrUtil.isEmpty(filter) || StrUtil.isEmpty(ip)) {
            return false;
        }
        String[] ips = filter.split(StrUtil.SEMICOLON);
        for (String iStr : ips) {
            if (isIP(iStr) && iStr.equals(ip)) {
                return true;
            } else if (isIpWildCard(iStr) && ipIsInWildCardNoCheck(iStr, ip)) {
                return true;
            } else if (isIPSegment(iStr) && ipIsInNetNoCheck(iStr, ip)) {
                return true;
            }
        }
        return false;
    }
}

Jwt工具类 | 会员端

package com.xueyi.common.core.utils.jwt;

import com.xueyi.common.core.constant.basic.SecurityConstants;
import io.jsonwebtoken.Claims;

/**
 * Jwt工具类 | 会员端
 *
 * @author xueyi
 */
public class JwtMemberUtil extends JwtUtil {

    /**
     * 根据令牌获取平台应用Id
     *
     * @param token 令牌
     * @return 平台应用Id
     */
    public static String getApplicationId(String token) {
        Claims claims = parseToken(token);
        return getApplicationId(claims);
    }

    /**
     * 根据令牌获取平台应用Id
     *
     * @param claims 身份信息
     * @return 平台应用Id
     */
    public static String getApplicationId(Claims claims) {
        return getValue(claims, SecurityConstants.MemberSecurity.APPLICATION_ID.getCode());
    }

    /**
     * 根据令牌获取应用AppId
     *
     * @param token 令牌
     * @return 应用AppId
     */
    public static String getAppId(String token) {
        Claims claims = parseToken(token);
        return getAppId(claims);
    }

    /**
     * 根据令牌获取应用AppId
     *
     * @param claims 身份信息
     * @return 应用AppId
     */
    public static String getAppId(Claims claims) {
        return getValue(claims, SecurityConstants.MemberSecurity.APP_ID.getCode());
    }
}

Jwt工具类 | 平台端

package com.xueyi.common.core.utils.jwt;

import com.xueyi.common.core.constant.basic.SecurityConstants;
import io.jsonwebtoken.Claims;

/**
 * Jwt工具类 | 平台端
 *
 * @author xueyi
 */
public class JwtPlatformUtil extends JwtUtil {

    /**
     * 根据令牌获取平台应用Id
     *
     * @param token 令牌
     * @return 平台应用Id
     */
    public static String getAppId(String token) {
        Claims claims = parseToken(token);
        return getAppId(claims);
    }

    /**
     * 根据令牌获取平台应用Id
     *
     * @param claims 身份信息
     * @return 平台应用Id
     */
    public static String getAppId(Claims claims) {
        return getValue(claims, SecurityConstants.PlatformSecurity.APP_ID.getCode());
    }
}

Jwt工具类

package com.xueyi.common.core.utils.jwt;

import com.xueyi.common.core.constant.basic.SecurityConstants;
import com.xueyi.common.core.constant.basic.TokenConstants;
import com.xueyi.common.core.utils.core.ConvertUtil;
import com.xueyi.common.core.utils.core.StrUtil;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;

import java.util.Map;

/**
 * Jwt工具类
 *
 * @author xueyi
 */
public class JwtUtil {

    public static String secret = TokenConstants.SECRET;

    /**
     * 从令牌中获取数据声明
     *
     * @param token 令牌
     * @return 数据声明
     */
    public static Claims parseToken(String token) {
        return Jwts.parserBuilder().setSigningKey(Keys.hmacShaKeyFor(secret.getBytes())).build().parseClaimsJws(token).getBody();
    }

    /**
     * 从数据声明生成令牌
     *
     * @param claims 数据声明
     * @return 令牌
     */
    public static String createToken(Map<String, Object> claims) {
        return Jwts.builder().setClaims(claims).signWith(Keys.hmacShaKeyFor(secret.getBytes()), SignatureAlgorithm.HS512).compact();
    }

    /**
     * 根据令牌获取企业Id
     *
     * @param token 令牌
     * @return 企业Id
     */

    public static String getEnterpriseId(String token) {
        Claims claims = parseToken(token);
        return getEnterpriseId(claims);
    }

    /**
     * 根据身份信息获取企业Id
     *
     * @param claims 身份信息
     * @return 企业Id
     */
    public static String getEnterpriseId(Claims claims) {
        return getValue(claims, SecurityConstants.BaseSecurity.ENTERPRISE_ID.getCode());
    }

    /**
     * 根据令牌获取企业账号
     *
     * @param token 令牌
     * @return 企业账号
     */
    public static String getEnterpriseName(String token) {
        Claims claims = parseToken(token);
        return getEnterpriseName(claims);
    }

    /**
     * 根据身份信息获取企业账号
     *
     * @param claims 身份信息
     * @return 企业账号
     */
    public static String getEnterpriseName(Claims claims) {
        return getValue(claims, SecurityConstants.BaseSecurity.ENTERPRISE_NAME.getCode());
    }

    /**
     * 根据令牌获取企业类型
     *
     * @param token 令牌
     * @return 企业类型
     */
    public static String getIsLessor(String token) {
        Claims claims = parseToken(token);
        return getIsLessor(claims);
    }

    /**
     * 根据身份信息获取企业类型
     *
     * @param claims 身份信息
     * @return 企业类型
     */
    public static String getIsLessor(Claims claims) {
        return getValue(claims, SecurityConstants.BaseSecurity.IS_LESSOR.getCode());
    }

    /**
     * 根据令牌获取用户Id
     *
     * @param token 令牌
     * @return 用户Id
     */
    public static String getUserId(String token) {
        Claims claims = parseToken(token);
        return getUserId(claims);
    }

    /**
     * 根据身份信息获取用户Id
     *
     * @param claims 身份信息
     * @return 用户Id
     */
    public static String getUserId(Claims claims) {
        return getValue(claims, SecurityConstants.BaseSecurity.USER_ID.getCode());
    }

    /**
     * 根据令牌获取用户账号
     *
     * @param token 令牌
     * @return 用户账号
     */
    public static String getUserName(String token) {
        Claims claims = parseToken(token);
        return getUserName(claims);
    }

    /**
     * 根据身份信息获取用户账号
     *
     * @param claims 身份信息
     * @return 用户账号
     */
    public static String getUserName(Claims claims) {
        return getValue(claims, SecurityConstants.BaseSecurity.USER_NAME.getCode());
    }

    /**
     * 根据令牌获取用户账号
     *
     * @param token 令牌
     * @return 用户账号
     */
    public static String getNickName(String token) {
        Claims claims = parseToken(token);
        return getNickName(claims);
    }

    /**
     * 根据身份信息获取用户账号
     *
     * @param claims 身份信息
     * @return 用户账号
     */
    public static String getNickName(Claims claims) {
        return getValue(claims, SecurityConstants.BaseSecurity.NICK_NAME.getCode());
    }

    /**
     * 根据令牌获取用户类型
     *
     * @param token 令牌
     * @return 用户类型
     */
    public static String getUserType(String token) {
        Claims claims = parseToken(token);
        return getUserType(claims);
    }

    /**
     * 根据身份信息获取用户类型
     *
     * @param claims 身份信息
     * @return 用户类型
     */
    public static String getUserType(Claims claims) {
        return getValue(claims, SecurityConstants.BaseSecurity.USER_TYPE.getCode());
    }

    /**
     * 根据令牌获取账户类型
     *
     * @param token 令牌
     * @return 账户类型
     */
    public static String getAccountType(String token) {
        Claims claims = parseToken(token);
        return getAccountType(claims);
    }

    /**
     * 根据令牌获取账户类型
     *
     * @param claims 身份信息
     * @return 账户类型
     */
    public static String getAccountType(Claims claims) {
        return getValue(claims, SecurityConstants.BaseSecurity.ACCOUNT_TYPE.getCode());
    }

    /**
     * 根据令牌获取租户策略源
     *
     * @param token 令牌
     * @return 租户策略源
     */
    public static String getSourceName(String token) {
        Claims claims = parseToken(token);
        return getSourceName(claims);
    }

    /**
     * 根据令牌获取租户策略源
     *
     * @param claims 身份信息
     * @return 租户策略源
     */
    public static String getSourceName(Claims claims) {
        return getValue(claims, SecurityConstants.BaseSecurity.SOURCE_NAME.getCode());
    }

    /**
     * 根据令牌获取用户令牌
     *
     * @param token 令牌
     * @return 用户令牌
     */
    public static String getUserKey(String token) {
        Claims claims = parseToken(token);
        return getUserKey(claims);
    }

    /**
     * 根据令牌获取用户令牌
     *
     * @param claims 身份信息
     * @return 用户令牌
     */
    public static String getUserKey(Claims claims) {
        String accessToken = getValue(claims, SecurityConstants.BaseSecurity.REFRESH_TOKEN.getCode());
        if (StrUtil.isNotEmpty(accessToken) && StrUtil.startWith(accessToken, TokenConstants.PREFIX))
            return StrUtil.replaceFirst(accessToken, TokenConstants.PREFIX, StrUtil.EMPTY);
        return null;
    }

    /**
     * 根据令牌获取访问令牌
     *
     * @param token 令牌
     * @return 访问令牌
     */
    public static String getAccessKey(String token) {
        Claims claims = parseToken(token);
        return getUserKey(claims);
    }

    /**
     * 根据令牌获取访问令牌
     *
     * @param claims 身份信息
     * @return 访问令牌
     */
    public static String getAccessKey(Claims claims) {
        return getValue(claims, SecurityConstants.BaseSecurity.ACCESS_TOKEN.getCode());
    }

    /**
     * 根据身份信息获取键值
     *
     * @param claims 身份信息
     * @param key    键
     * @return 值
     */
    public static String getValue(Claims claims, String key) {
        return ConvertUtil.toStr(claims.get(key), StrUtil.EMPTY);
    }
}

分页工具类

package com.xueyi.common.core.utils.page;

import com.github.pagehelper.PageHelper;
import com.xueyi.common.core.utils.core.ObjectUtil;
import com.xueyi.common.core.utils.sql.SqlUtil;
import com.xueyi.common.core.web.page.PageDomain;
import com.xueyi.common.core.web.page.TableSupport;

/**
 * 分页工具类
 *
 * @author xueyi
 */
public class PageUtil extends PageHelper {

    /**
     * 设置请求分页数据
     */
    public static void startPage() {
        PageDomain pageDomain = TableSupport.buildPageRequest();
        Integer pageNum = pageDomain.getPageNum();
        Integer pageSize = pageDomain.getPageSize();
        if (ObjectUtil.isAllNotEmpty(pageNum, pageSize)) {
            String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy());
            Boolean reasonable = pageDomain.getReasonable();
            PageHelper.startPage(pageNum, pageSize, orderBy).setReasonable(reasonable);
        }
    }

    /**
     * 清理分页的线程变量
     */
    public static void clearPage() {
        PageHelper.clearPage();
    }
}

Excel数据格式处理适配器

package com.xueyi.common.core.utils.poi;

import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Workbook;

/**
 * Excel数据格式处理适配器
 *
 * @author xueyi
 */
public interface ExcelHandlerAdapter {

    /**
     * 格式化
     *
     * @param value 单元格数据值
     * @param args  excel注解args参数组
     * @param cell  单元格对象
     * @param wb    工作簿对象
     * @return 处理后的值
     */
    Object format(Object value, String[] args, Cell cell, Workbook wb);
}

Excel相关处理

package com.xueyi.common.core.utils.poi;

import com.xueyi.common.core.annotation.Excel;
import com.xueyi.common.core.annotation.Excel.ColumnType;
import com.xueyi.common.core.annotation.Excel.Type;
import com.xueyi.common.core.annotation.Excels;
import com.xueyi.common.core.exception.UtilException;
import com.xueyi.common.core.utils.DateUtil;
import com.xueyi.common.core.utils.core.CollUtil;
import com.xueyi.common.core.utils.core.ConvertUtil;
import com.xueyi.common.core.utils.core.ObjectUtil;
import com.xueyi.common.core.utils.core.ReflectUtil;
import com.xueyi.common.core.utils.core.StrUtil;
import com.xueyi.common.core.utils.file.FileTypeUtil;
import com.xueyi.common.core.utils.file.ImageUtil;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.RegExUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.CellRangeAddressList;
import org.apache.poi.util.IOUtils;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
import org.apache.poi.xssf.usermodel.XSSFDataValidation;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * Excel相关处理
 *
 * @author xueyi
 */
@Slf4j
public class ExcelUtil<T> {

    public static final String FORMULA_REGEX_STR = "=|-|\\+|@";

    public static final String[] FORMULA_STR = {"=", "-", "+", "@"};

    /**
     * Excel sheet最大行数,默认65536
     */
    public static final int sheetSize = 65536;

    /**
     * 工作表名称
     */
    private String sheetName;

    /**
     * 导出类型(EXPORT:导出数据;IMPORT:导入模板)
     */
    private Type type;

    /**
     * 工作薄对象
     */
    private Workbook wb;

    /**
     * 工作表对象
     */
    private Sheet sheet;

    /**
     * 样式列表
     */
    private Map<String, CellStyle> styles;

    /**
     * 导入导出数据列表
     */
    private List<T> list;

    /**
     * 注解列表
     */
    private List<Object[]> fields;

    /**
     * 当前行号
     */
    private int rownum;

    /**
     * 标题
     */
    private String title;

    /**
     * 最大高度
     */
    private short maxHeight;

    /**
     * 合并后最后行数
     */
    private int subMergedLastRowNum = 0;

    /**
     * 合并后开始行数
     */
    private int subMergedFirstRowNum = 1;

    /**
     * 对象的子列表方法
     */
    private Method subMethod;

    /**
     * 对象的子列表属性
     */
    private List<Field> subFields;

    /**
     * 统计列表
     */
    private Map<Integer, Double> statistics = new HashMap<>();

    /**
     * 数字格式
     */
    private static final DecimalFormat DOUBLE_FORMAT = new DecimalFormat("######0.00");

    /**
     * 实体对象
     */
    public Class<T> clazz;

    /**
     * 需要排除列属性
     */
    public String[] excludeFields;

    public ExcelUtil(Class<T> clazz) {
        this.clazz = clazz;
    }

    /**
     * 隐藏Excel中列属性
     *
     * @param fields 列属性名 示例[单个"name"/多个"id","name"]
     */
    public void hideColumn(String... fields) {
        this.excludeFields = fields;
    }

    public void init(List<T> list, String sheetName, String title, Type type) {
        if (list == null) {
            list = new ArrayList<T>();
        }
        this.list = list;
        this.sheetName = sheetName;
        this.type = type;
        this.title = title;
        createExcelField();
        createWorkbook();
        createTitle();
        createSubHead();
    }

    /**
     * 创建excel第一行标题
     */
    public void createTitle() {
        if (StrUtil.isNotEmpty(title)) {
            subMergedFirstRowNum++;
            subMergedLastRowNum++;
            int titleLastCol = this.fields.size() - 1;
            if (isSubList()) {
                titleLastCol = titleLastCol + subFields.size() - 1;
            }
            Row titleRow = sheet.createRow(rownum == 0 ? rownum++ : 0);
            titleRow.setHeightInPoints(30);
            Cell titleCell = titleRow.createCell(0);
            titleCell.setCellStyle(styles.get("title"));
            titleCell.setCellValue(title);
            sheet.addMergedRegion(new CellRangeAddress(titleRow.getRowNum(), titleRow.getRowNum(), titleRow.getRowNum(), titleLastCol));
        }
    }

    /**
     * 创建对象的子列表名称
     */
    public void createSubHead() {
        if (isSubList()) {
            subMergedFirstRowNum++;
            subMergedLastRowNum++;
            Row subRow = sheet.createRow(rownum);
            int excelNum = 0;
            for (Object[] objects : fields) {
                Excel attr = (Excel) objects[1];
                Cell headCell1 = subRow.createCell(excelNum);
                headCell1.setCellValue(attr.name());
                headCell1.setCellStyle(styles.get(StrUtil.format("header_{}_{}", attr.headerColor(), attr.headerBackgroundColor())));
                excelNum++;
            }
            int headFirstRow = excelNum - 1;
            int headLastRow = headFirstRow + subFields.size() - 1;
            if (headLastRow > headFirstRow) {
                sheet.addMergedRegion(new CellRangeAddress(rownum, rownum, headFirstRow, headLastRow));
            }
            rownum++;
        }
    }

    /**
     * 对excel表单默认第一个索引名转换成list
     *
     * @param is 输入流
     * @return 转换后集合
     */
    public List<T> importExcel(InputStream is) {
        List<T> list;
        try {
            list = importExcel(is, 0);
        } catch (Exception e) {
            log.error("导入Excel异常{}", e.getMessage());
            throw new UtilException(e.getMessage());
        } finally {
            IOUtils.closeQuietly(is);
        }
        return list;
    }

    /**
     * 对excel表单默认第一个索引名转换成list
     *
     * @param is       输入流
     * @param titleNum 标题占用行数
     * @return 转换后集合
     */
    public List<T> importExcel(InputStream is, int titleNum) throws Exception {
        return importExcel(StrUtil.EMPTY, is, titleNum);
    }

    /**
     * 对excel表单指定表格索引名转换成list
     *
     * @param sheetName 表格索引名
     * @param titleNum  标题占用行数
     * @param is        输入流
     * @return 转换后集合
     */
    public List<T> importExcel(String sheetName, InputStream is, int titleNum) throws Exception {
        this.type = Type.IMPORT;
        this.wb = WorkbookFactory.create(is);
        List<T> list = new ArrayList<T>();
        // 如果指定sheet名,则取指定sheet中的内容 否则默认指向第1个sheet
        Sheet sheet = StrUtil.isNotEmpty(sheetName) ? wb.getSheet(sheetName) : wb.getSheetAt(0);
        if (sheet == null) {
            throw new IOException("文件sheet不存在");
        }

        // 获取最后一个非空行的行下标,比如总行数为n,则返回的为n-1
        int rows = sheet.getLastRowNum();

        if (rows > 0) {
            // 定义一个map用于存放excel列的序号和field.
            Map<String, Integer> cellMap = new HashMap<String, Integer>();
            // 获取表头
            Row heard = sheet.getRow(titleNum);
            for (int i = 0; i < heard.getPhysicalNumberOfCells(); i++) {
                Cell cell = heard.getCell(i);
                if (ObjectUtil.isNotNull(cell)) {
                    String value = this.getCellValue(heard, i).toString();
                    cellMap.put(value, i);
                } else {
                    cellMap.put(null, i);
                }
            }
            // 有数据时才处理 得到类的所有field.
            List<Object[]> fields = this.getFields();
            Map<Integer, Object[]> fieldsMap = new HashMap<Integer, Object[]>();
            for (Object[] objects : fields) {
                Excel attr = (Excel) objects[1];
                Integer column = cellMap.get(attr.name());
                if (column != null) {
                    fieldsMap.put(column, objects);
                }
            }
            for (int i = titleNum + 1; i <= rows; i++) {
                // 从第2行开始取数据,默认第一行是表头.
                Row row = sheet.getRow(i);
                // 判断当前行是否是空行
                if (isRowEmpty(row)) {
                    continue;
                }
                T entity = null;
                for (Map.Entry<Integer, Object[]> entry : fieldsMap.entrySet()) {
                    Object val = this.getCellValue(row, entry.getKey());

                    // 如果不存在实例则新建.
                    entity = (entity == null ? clazz.newInstance() : entity);
                    // 从map中得到对应列的field.
                    Field field = (Field) entry.getValue()[0];
                    Excel attr = (Excel) entry.getValue()[1];
                    // 取得类型,并根据对象类型设置值.
                    Class<?> fieldType = field.getType();
                    if (String.class == fieldType) {
                        String s = ConvertUtil.toStr(val);
                        if (StrUtil.endWith(s, ".0")) {
                            val = StrUtil.subBefore(s, ".0");
                        } else {
                            String dateFormat = field.getAnnotation(Excel.class).dateFormat();
                            if (StrUtil.isNotEmpty(dateFormat)) {
                                val = parseDateToStr(dateFormat, val);
                            } else {
                                val = ConvertUtil.toStr(val);
                            }
                        }
                    } else if ((Integer.TYPE == fieldType || Integer.class == fieldType) && StrUtil.isNumeric(ConvertUtil.toStr(val))) {
                        val = ConvertUtil.toInt(val);
                    } else if ((Long.TYPE == fieldType || Long.class == fieldType) && StrUtil.isNumeric(ConvertUtil.toStr(val))) {
                        val = ConvertUtil.toLong(val);
                    } else if (Double.TYPE == fieldType || Double.class == fieldType) {
                        val = ConvertUtil.toDouble(val);
                    } else if (Float.TYPE == fieldType || Float.class == fieldType) {
                        val = ConvertUtil.toFloat(val);
                    } else if (BigDecimal.class == fieldType) {
                        val = ConvertUtil.toBigDecimal(val);
                    } else if (Date.class == fieldType) {
                        if (val instanceof String) {
                            val = DateUtil.parseDate(val);
                        } else if (val instanceof Double) {
                            val = org.apache.poi.ss.usermodel.DateUtil.getJavaDate((Double) val);
                        }
                    } else if (Boolean.TYPE == fieldType || Boolean.class == fieldType) {
                        val = ConvertUtil.toBool(val, false);
                    }
                    if (ObjectUtil.isNotNull(fieldType)) {
                        String propertyName = field.getName();
                        if (StrUtil.isNotEmpty(attr.targetAttr())) {
                            propertyName = field.getName() + "." + attr.targetAttr();
                        }
                        if (StrUtil.isNotEmpty(attr.readConverterExp())) {
                            val = reverseByExp(ConvertUtil.toStr(val), attr.readConverterExp(), attr.separator());
                        } else if (!attr.handler().equals(ExcelHandlerAdapter.class)) {
                            val = dataFormatHandlerAdapter(val, attr, null);
                        }
                        ReflectUtil.setFieldValue(entity, propertyName, val);
                    }
                }
                list.add(entity);
            }
        }
        return list;
    }

    /**
     * 对list数据源将其里面的数据导入到excel表单
     *
     * @param response  返回数据
     * @param list      导出数据集合
     * @param sheetName 工作表的名称
     */
    public void exportExcel(HttpServletResponse response, List<T> list, String sheetName) {
        exportExcel(response, list, sheetName, StrUtil.EMPTY);
    }

    /**
     * 对list数据源将其里面的数据导入到excel表单
     *
     * @param response  返回数据
     * @param list      导出数据集合
     * @param sheetName 工作表的名称
     * @param title     标题
     */
    public void exportExcel(HttpServletResponse response, List<T> list, String sheetName, String title) {
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        this.init(list, sheetName, title, Type.EXPORT);
        exportExcel(response);
    }

    /**
     * 对list数据源将其里面的数据导入到excel表单
     *
     * @param sheetName 工作表的名称
     */
    public void importTemplateExcel(HttpServletResponse response, String sheetName) {
        importTemplateExcel(response, sheetName, StrUtil.EMPTY);
    }

    /**
     * 对list数据源将其里面的数据导入到excel表单
     *
     * @param sheetName 工作表的名称
     * @param title     标题
     */
    public void importTemplateExcel(HttpServletResponse response, String sheetName, String title) {
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        this.init(null, sheetName, title, Type.IMPORT);
        exportExcel(response);
    }

    /**
     * 对list数据源将其里面的数据导入到excel表单
     */
    public void exportExcel(HttpServletResponse response) {
        try {
            writeSheet();
            wb.write(response.getOutputStream());
        } catch (Exception e) {
            log.error("导出Excel异常{}", e.getMessage());
        } finally {
            IOUtils.closeQuietly(wb);
        }
    }

    /**
     * 创建写入数据到Sheet
     */
    public void writeSheet() {
        // 取出一共有多少个sheet.
        int sheetNo = Math.max(1, (int) Math.ceil(list.size() * 1.0 / sheetSize));
        for (int index = 0; index < sheetNo; index++) {
            createSheet(sheetNo, index);

            // 产生一行
            Row row = sheet.createRow(rownum);
            int column = 0;
            // 写入各个字段的列头名称
            for (Object[] os : fields) {
                Field field = (Field) os[0];
                Excel excel = (Excel) os[1];
                if (Collection.class.isAssignableFrom(field.getType())) {
                    for (Field subField : subFields) {
                        Excel subExcel = subField.getAnnotation(Excel.class);
                        this.createHeadCell(subExcel, row, column++);
                    }
                } else {
                    this.createHeadCell(excel, row, column++);
                }
            }
            if (Type.EXPORT.equals(type)) {
                fillExcelData(index, row);
                addStatisticsRow();
            }
        }
    }

    /**
     * 填充excel数据
     *
     * @param index 序号
     * @param row   单元格行
     */
    @SuppressWarnings("unchecked")
    public void fillExcelData(int index, Row row) {
        int startNo = index * sheetSize;
        int endNo = Math.min(startNo + sheetSize, list.size());
        int rowNo = (1 + rownum) - startNo;
        for (int i = startNo; i < endNo; i++) {
            rowNo = isSubList() ? (i > 1 ? rowNo + 1 : rowNo + i) : i + 1 + rownum - startNo;
            row = sheet.createRow(rowNo);
            // 得到导出对象.
            T vo = (T) list.get(i);
            Collection<?> subList = null;
            if (isSubList()) {
                if (isSubListValue(vo)) {
                    subList = getListCellValue(vo);
                    subMergedLastRowNum = subMergedLastRowNum + subList.size();
                } else {
                    subMergedFirstRowNum++;
                    subMergedLastRowNum++;
                }
            }

            int column = 0;
            for (Object[] os : fields) {
                Field field = (Field) os[0];
                Excel excel = (Excel) os[1];
                if (Collection.class.isAssignableFrom(field.getType()) && CollUtil.isNotEmpty(subList)) {
                    boolean subFirst = false;
                    for (Object obj : subList) {
                        if (subFirst) {
                            rowNo++;
                            row = sheet.createRow(rowNo);
                        }
                        List<Field> subFields = FieldUtils.getFieldsListWithAnnotation(obj.getClass(), Excel.class);
                        int subIndex = 0;
                        for (Field subField : subFields) {
                            if (subField.isAnnotationPresent(Excel.class)) {
                                subField.setAccessible(true);
                                Excel attr = subField.getAnnotation(Excel.class);
                                this.addCell(attr, row, (T) obj, subField, column + subIndex);
                            }
                            subIndex++;
                        }
                        subFirst = true;
                    }
                    this.subMergedFirstRowNum = this.subMergedFirstRowNum + subList.size();
                } else {
                    this.addCell(excel, row, vo, field, column++);
                }
            }
        }
    }

    /**
     * 创建表格样式
     *
     * @param wb 工作薄对象
     * @return 样式列表
     */
    private Map<String, CellStyle> createStyles(Workbook wb) {
        // 写入各条记录,每条记录对应excel表中的一行
        Map<String, CellStyle> styles = new HashMap<String, CellStyle>();
        CellStyle style = wb.createCellStyle();
        style.setAlignment(HorizontalAlignment.CENTER);
        style.setVerticalAlignment(VerticalAlignment.CENTER);
        Font titleFont = wb.createFont();
        titleFont.setFontName("Arial");
        titleFont.setFontHeightInPoints((short) 16);
        titleFont.setBold(true);
        style.setFont(titleFont);
        styles.put("title", style);

        style = wb.createCellStyle();
        style.setAlignment(HorizontalAlignment.CENTER);
        style.setVerticalAlignment(VerticalAlignment.CENTER);
        style.setBorderRight(BorderStyle.THIN);
        style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
        style.setBorderLeft(BorderStyle.THIN);
        style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
        style.setBorderTop(BorderStyle.THIN);
        style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
        style.setBorderBottom(BorderStyle.THIN);
        style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
        Font dataFont = wb.createFont();
        dataFont.setFontName("Arial");
        dataFont.setFontHeightInPoints((short) 10);
        style.setFont(dataFont);
        styles.put("data", style);

        style = wb.createCellStyle();
        style.setAlignment(HorizontalAlignment.CENTER);
        style.setVerticalAlignment(VerticalAlignment.CENTER);
        Font totalFont = wb.createFont();
        totalFont.setFontName("Arial");
        totalFont.setFontHeightInPoints((short) 10);
        style.setFont(totalFont);
        styles.put("total", style);

        styles.putAll(annotationHeaderStyles(wb, styles));

        styles.putAll(annotationDataStyles(wb));

        return styles;
    }

    /**
     * 根据Excel注解创建表格头样式
     *
     * @param wb 工作薄对象
     * @return 自定义样式列表
     */
    private Map<String, CellStyle> annotationHeaderStyles(Workbook wb, Map<String, CellStyle> styles) {
        Map<String, CellStyle> headerStyles = new HashMap<String, CellStyle>();
        for (Object[] os : fields) {
            Excel excel = (Excel) os[1];
            String key = StrUtil.format("header_{}_{}", excel.headerColor(), excel.headerBackgroundColor());
            if (!headerStyles.containsKey(key)) {
                CellStyle style = wb.createCellStyle();
                style.cloneStyleFrom(styles.get("data"));
                style.setAlignment(HorizontalAlignment.CENTER);
                style.setVerticalAlignment(VerticalAlignment.CENTER);
                style.setFillForegroundColor(excel.headerBackgroundColor().index);
                style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
                Font headerFont = wb.createFont();
                headerFont.setFontName("Arial");
                headerFont.setFontHeightInPoints((short) 10);
                headerFont.setBold(true);
                headerFont.setColor(excel.headerColor().index);
                style.setFont(headerFont);
                headerStyles.put(key, style);
            }
        }
        return headerStyles;
    }

    /**
     * 根据Excel注解创建表格列样式
     *
     * @param wb 工作薄对象
     * @return 自定义样式列表
     */
    private Map<String, CellStyle> annotationDataStyles(Workbook wb) {
        Map<String, CellStyle> styles = new HashMap<String, CellStyle>();
        for (Object[] os : fields) {
            Excel excel = (Excel) os[1];
            String key = StrUtil.format("data_{}_{}_{}", excel.align(), excel.color(), excel.backgroundColor());
            if (!styles.containsKey(key)) {
                CellStyle style = wb.createCellStyle();
                style.setAlignment(excel.align());
                style.setVerticalAlignment(VerticalAlignment.CENTER);
                style.setBorderRight(BorderStyle.THIN);
                style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
                style.setBorderLeft(BorderStyle.THIN);
                style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
                style.setBorderTop(BorderStyle.THIN);
                style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
                style.setBorderBottom(BorderStyle.THIN);
                style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
                style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
                style.setFillForegroundColor(excel.backgroundColor().getIndex());
                Font dataFont = wb.createFont();
                dataFont.setFontName("Arial");
                dataFont.setFontHeightInPoints((short) 10);
                dataFont.setColor(excel.color().index);
                style.setFont(dataFont);
                styles.put(key, style);
            }
        }
        return styles;
    }

    /**
     * 创建单元格
     */
    public Cell createHeadCell(Excel attr, Row row, int column) {
        // 创建列
        Cell cell = row.createCell(column);
        // 写入列信息
        cell.setCellValue(attr.name());
        setDataValidation(attr, row, column);
        cell.setCellStyle(styles.get(StrUtil.format("header_{}_{}", attr.headerColor(), attr.headerBackgroundColor())));
        if (isSubList()) {
            // 填充默认样式,防止合并单元格样式失效
            sheet.setDefaultColumnStyle(column, styles.get(StrUtil.format("data_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor())));
            if (attr.needMerge()) {
                sheet.addMergedRegion(new CellRangeAddress(rownum - 1, rownum, column, column));
            }
        }
        return cell;
    }

    /**
     * 设置单元格信息
     *
     * @param value 单元格值
     * @param attr  注解相关
     * @param cell  单元格信息
     */
    public void setCellVo(Object value, Excel attr, Cell cell) {
        if (ColumnType.STRING == attr.cellType()) {
            String cellValue = ConvertUtil.toStr(value);
            // 对于任何以表达式触发字符 =-+@开头的单元格,直接使用tab字符作为前缀,防止CSV注入。
            if (StrUtil.startWithAny(cellValue, FORMULA_STR)) {
                cellValue = RegExUtils.replaceFirst(cellValue, FORMULA_REGEX_STR, "\t$0");
            }
            if (value instanceof Collection && StrUtil.equals(StrUtil.BRACKET, cellValue)) {
                cellValue = StrUtil.EMPTY;
            }
            cell.setCellValue(ObjectUtil.isNull(cellValue) ? attr.defaultValue() : cellValue + attr.suffix());
        } else if (ColumnType.NUMERIC == attr.cellType()) {
            if (ObjectUtil.isNotNull(value)) {
                cell.setCellValue(StrUtil.contains(ConvertUtil.toStr(value), StrUtil.DOT) ? ConvertUtil.toDouble(value) : ConvertUtil.toInt(value));
            }
        } else if (ColumnType.IMAGE == attr.cellType()) {
            ClientAnchor anchor = new XSSFClientAnchor(0, 0, 0, 0, (short) cell.getColumnIndex(), cell.getRow()
                    .getRowNum(), (short) (cell.getColumnIndex() + 1), cell.getRow().getRowNum() + 1);
            String imagePath = ConvertUtil.toStr(value);
            if (StrUtil.isNotEmpty(imagePath)) {
                byte[] data = ImageUtil.getImage(imagePath);
                getDrawingPatriarch(cell.getSheet()).createPicture(anchor,
                        cell.getSheet().getWorkbook().addPicture(data, getImageType(data)));
            }
        }
    }

    /**
     * 获取画布
     */
    public static Drawing<?> getDrawingPatriarch(Sheet sheet) {
        if (sheet.getDrawingPatriarch() == null) {
            sheet.createDrawingPatriarch();
        }
        return sheet.getDrawingPatriarch();
    }

    /**
     * 获取图片类型,设置图片插入类型
     */
    public int getImageType(byte[] value) {
        String type = FileTypeUtil.getFileExtendName(value);
        if ("JPG".equalsIgnoreCase(type)) {
            return Workbook.PICTURE_TYPE_JPEG;
        } else if ("PNG".equalsIgnoreCase(type)) {
            return Workbook.PICTURE_TYPE_PNG;
        }
        return Workbook.PICTURE_TYPE_JPEG;
    }

    /**
     * 创建表格样式
     */
    public void setDataValidation(Excel attr, Row row, int column) {
        if (attr.name().contains("注:")) {
            sheet.setColumnWidth(column, 6000);
        } else {
            // 设置列宽
            sheet.setColumnWidth(column, (int) ((attr.width() + 0.72) * 256));
        }
        if (StrUtil.isNotEmpty(attr.prompt()) || attr.combo().length > 0) {
            // 提示信息或只能选择不能输入的列内容.
            if (attr.combo().length > 15 || StringUtils.join(attr.combo()).length() > 255) {
                // 如果下拉数大于15或字符串长度大于255,则使用一个新sheet存储,避免生成的模板下拉值获取不到
                setXSSFValidationWithHidden(sheet, attr.combo(), attr.prompt(), 1, 100, column, column);
            } else {
                // 提示信息或只能选择不能输入的列内容.
                setPromptOrValidation(sheet, attr.combo(), attr.prompt(), 1, 100, column, column);
            }
        }
    }

    /**
     * 添加单元格
     */
    public Cell addCell(Excel attr, Row row, T vo, Field field, int column) {
        Cell cell = null;
        try {
            // 设置行高
            row.setHeight(maxHeight);
            // 根据Excel中设置情况决定是否导出,有些情况需要保持为空,希望用户填写这一列.
            if (attr.isExport()) {
                // 创建cell
                cell = row.createCell(column);
                if (isSubListValue(vo) && getListCellValue(vo).size() > 1 && attr.needMerge()) {
                    CellRangeAddress cellAddress = new CellRangeAddress(subMergedFirstRowNum, subMergedLastRowNum, column, column);
                    sheet.addMergedRegion(cellAddress);
                }
                cell.setCellStyle(styles.get(StrUtil.format("data_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor())));

                // 用于读取对象中的属性
                Object value = getTargetValue(vo, field, attr);
                String dateFormat = attr.dateFormat();
                String readConverterExp = attr.readConverterExp();
                String separator = attr.separator();
                if (StrUtil.isNotEmpty(dateFormat) && ObjectUtil.isNotNull(value)) {
                    cell.setCellValue(parseDateToStr(dateFormat, value));
                } else if (StrUtil.isNotEmpty(readConverterExp) && ObjectUtil.isNotNull(value)) {
                    cell.setCellValue(convertByExp(ConvertUtil.toStr(value), readConverterExp, separator));
                } else if (value instanceof BigDecimal && -1 != attr.scale()) {
                    cell.setCellValue((((BigDecimal) value).setScale(attr.scale(), attr.roundingMode())).doubleValue());
                } else if (!attr.handler().equals(ExcelHandlerAdapter.class)) {
                    cell.setCellValue(dataFormatHandlerAdapter(value, attr, cell));
                } else {
                    // 设置列类型
                    setCellVo(value, attr, cell);
                }
                addStatisticsData(column, ConvertUtil.toStr(value), attr);
            }
        } catch (Exception e) {
            log.error("导出Excel失败{}", e);
        }
        return cell;
    }

    /**
     * 设置 POI XSSFSheet 单元格提示或选择框
     *
     * @param sheet         表单
     * @param textList      下拉框显示的内容
     * @param promptContent 提示内容
     * @param firstRow      开始行
     * @param endRow        结束行
     * @param firstCol      开始列
     * @param endCol        结束列
     */
    public void setPromptOrValidation(Sheet sheet, String[] textList, String promptContent, int firstRow, int endRow,
                                      int firstCol, int endCol) {
        DataValidationHelper helper = sheet.getDataValidationHelper();
        DataValidationConstraint constraint = textList.length > 0 ? helper.createExplicitListConstraint(textList) : helper.createCustomConstraint("DD1");
        CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol);
        DataValidation dataValidation = helper.createValidation(constraint, regions);
        if (StrUtil.isNotEmpty(promptContent)) {
            // 如果设置了提示信息则鼠标放上去提示
            dataValidation.createPromptBox("", promptContent);
            dataValidation.setShowPromptBox(true);
        }
        // 处理Excel兼容性问题
        if (dataValidation instanceof XSSFDataValidation) {
            dataValidation.setSuppressDropDownArrow(true);
            dataValidation.setShowErrorBox(true);
        } else {
            dataValidation.setSuppressDropDownArrow(false);
        }
        sheet.addValidationData(dataValidation);
    }

    /**
     * 解析导出值 0=男,1=女,2=未知
     *
     * @param propertyValue 参数值
     * @param converterExp  翻译注解
     * @param separator     分隔符
     * @return 解析后值
     */
    public static String convertByExp(String propertyValue, String converterExp, String separator) {
        StringBuilder propertyString = new StringBuilder();
        String[] convertSource = converterExp.split(",");
        for (String item : convertSource) {
            String[] itemArray = item.split("=");
            if (StrUtil.containsAny(propertyValue, separator)) {
                for (String value : propertyValue.split(separator)) {
                    if (itemArray[0].equals(value)) {
                        propertyString.append(itemArray[1]).append(separator);
                        break;
                    }
                }
            } else {
                if (itemArray[0].equals(propertyValue)) {
                    return itemArray[1];
                }
            }
        }
        return StrUtil.removeAll(propertyString.toString(), separator);
    }

    /**
     * 反向解析值 男=0,女=1,未知=2
     *
     * @param propertyValue 参数值
     * @param converterExp  翻译注解
     * @param separator     分隔符
     * @return 解析后值
     */
    public static String reverseByExp(String propertyValue, String converterExp, String separator) {
        StringBuilder propertyString = new StringBuilder();
        String[] convertSource = converterExp.split(",");
        for (String item : convertSource) {
            String[] itemArray = item.split("=");
            if (StrUtil.containsAny(propertyValue, separator)) {
                for (String value : propertyValue.split(separator)) {
                    if (itemArray[1].equals(value)) {
                        propertyString.append(itemArray[0]).append(separator);
                        break;
                    }
                }
            } else {
                if (itemArray[1].equals(propertyValue)) {
                    return itemArray[0];
                }
            }
        }
        return StrUtil.removeAll(propertyString.toString(), separator);
    }

    /**
     * 数据处理器
     *
     * @param value 数据值
     * @param excel 数据注解
     * @return
     */
    public String dataFormatHandlerAdapter(Object value, Excel excel, Cell cell) {
        try {
            Object instance = excel.handler().getDeclaredConstructor().newInstance();
            Method formatMethod = excel.handler().getMethod("format", Object.class, String[].class, Cell.class, Workbook.class);
            value = formatMethod.invoke(instance, value, excel.args(), cell, this.wb);
        } catch (Exception e) {
            log.error("不能格式化数据{}{} ", excel.handler(), e.getMessage());
        }
        return ConvertUtil.toStr(value);
    }

    /**
     * 合计统计信息
     */
    private void addStatisticsData(Integer index, String text, Excel entity) {
        if (entity != null && entity.isStatistics()) {
            Double temp = 0D;
            if (!statistics.containsKey(index)) {
                statistics.put(index, temp);
            }
            try {
                temp = Double.valueOf(text);
            } catch (NumberFormatException ignored) {
            }
            statistics.put(index, statistics.get(index) + temp);
        }
    }

    /**
     * 创建统计行
     */
    public void addStatisticsRow() {
        if (statistics.size() > 0) {
            Row row = sheet.createRow(sheet.getLastRowNum() + 1);
            Set<Integer> keys = statistics.keySet();
            Cell cell = row.createCell(0);
            cell.setCellStyle(styles.get("total"));
            cell.setCellValue("合计");

            for (Integer key : keys) {
                cell = row.createCell(key);
                cell.setCellStyle(styles.get("total"));
                cell.setCellValue(DOUBLE_FORMAT.format(statistics.get(key)));
            }
            statistics.clear();
        }
    }

    /**
     * 获取bean中的属性值
     *
     * @param vo    实体对象
     * @param field 字段
     * @param excel 注解
     * @return 最终的属性值
     */
    private Object getTargetValue(T vo, Field field, Excel excel) throws Exception {
        Object o = field.get(vo);
        if (StrUtil.isNotEmpty(excel.targetAttr())) {
            String target = excel.targetAttr();
            if (target.contains(".")) {
                String[] targets = target.split("[.]");
                for (String name : targets) {
                    o = getValue(o, name);
                }
            } else {
                o = getValue(o, target);
            }
        }
        return o;
    }

    /**
     * 以类的属性的get方法方法形式获取值
     *
     * @param o
     * @param name
     * @return value
     * @throws Exception
     */
    private Object getValue(Object o, String name) throws Exception {
        if (ObjectUtil.isNotNull(o) && StrUtil.isNotEmpty(name)) {
            Class<?> clazz = o.getClass();
            Field field = clazz.getDeclaredField(name);
            field.setAccessible(true);
            o = field.get(o);
        }
        return o;
    }

    /**
     * 得到所有定义字段
     */
    private void createExcelField() {
        this.fields = getFields();
        this.fields = this.fields.stream().sorted(Comparator.comparing(objects -> ((Excel) objects[1]).sort()))
                .collect(Collectors.toList());
        this.maxHeight = getRowHeight();
    }

    /**
     * 获取字段注解信息
     */
    public List<Object[]> getFields() {
        List<Object[]> fields = new ArrayList<Object[]>();
        List<Field> tempFields = new ArrayList<>();
        tempFields.addAll(Arrays.asList(clazz.getSuperclass().getDeclaredFields()));
        tempFields.addAll(Arrays.asList(clazz.getDeclaredFields()));
        for (Field field : tempFields) {
            if (!ArrayUtils.contains(this.excludeFields, field.getName())) {
                // 单注解
                if (field.isAnnotationPresent(Excel.class)) {
                    Excel attr = field.getAnnotation(Excel.class);
                    if (attr != null && (attr.type() == Type.ALL || attr.type() == type)) {
                        field.setAccessible(true);
                        fields.add(new Object[]{field, attr});
                    }
                    if (Collection.class.isAssignableFrom(field.getType())) {
                        subMethod = getSubMethod(field.getName(), clazz);
                        ParameterizedType pt = (ParameterizedType) field.getGenericType();
                        Class<?> subClass = (Class<?>) pt.getActualTypeArguments()[0];
                        this.subFields = FieldUtils.getFieldsListWithAnnotation(subClass, Excel.class);
                    }
                }

                // 多注解
                if (field.isAnnotationPresent(Excels.class)) {
                    Excels attrs = field.getAnnotation(Excels.class);
                    Excel[] excels = attrs.value();
                    for (Excel attr : excels) {
                        if (attr != null && !ArrayUtils.contains(this.excludeFields, field.getName() + "." + attr.targetAttr())
                                && ((attr.type() == Type.ALL || attr.type() == type))) {
                            field.setAccessible(true);
                            fields.add(new Object[]{field, attr});
                        }
                    }
                }
            }
        }
        return fields;
    }

    /**
     * 根据注解获取最大行高
     */
    public short getRowHeight() {
        double maxHeight = 0;
        for (Object[] os : this.fields) {
            Excel excel = (Excel) os[1];
            maxHeight = Math.max(maxHeight, excel.height());
        }
        return (short) (maxHeight * 20);
    }

    /**
     * 创建一个工作簿
     */
    public void createWorkbook() {
        this.wb = new SXSSFWorkbook(500);
        this.sheet = wb.createSheet();
        wb.setSheetName(0, sheetName);
        this.styles = createStyles(wb);
    }

    /**
     * 创建工作表
     *
     * @param sheetNo sheet数量
     * @param index   序号
     */
    public void createSheet(int sheetNo, int index) {
        // 设置工作表的名称.
        if (sheetNo > 1 && index > 0) {
            this.sheet = wb.createSheet();
            this.createTitle();
            wb.setSheetName(index, sheetName + index);
        }
    }

    /**
     * 获取单元格值
     *
     * @param row    获取的行
     * @param column 获取单元格列号
     * @return 单元格值
     */
    public Object getCellValue(Row row, int column) {
        if (row == null) {
            return row;
        }
        Object val = "";
        try {
            Cell cell = row.getCell(column);
            if (ObjectUtil.isNotNull(cell)) {
                if (cell.getCellType() == CellType.NUMERIC || cell.getCellType() == CellType.FORMULA) {
                    val = cell.getNumericCellValue();
                    if (org.apache.poi.ss.usermodel.DateUtil.isCellDateFormatted(cell)) {
                        val = org.apache.poi.ss.usermodel.DateUtil.getJavaDate((Double) val); // POI Excel 日期格式转换
                    } else {
                        if ((Double) val % 1 != 0) {
                            val = new BigDecimal(val.toString());
                        } else {
                            val = new DecimalFormat("0").format(val);
                        }
                    }
                } else if (cell.getCellType() == CellType.STRING) {
                    val = cell.getStringCellValue();
                } else if (cell.getCellType() == CellType.BOOLEAN) {
                    val = cell.getBooleanCellValue();
                } else if (cell.getCellType() == CellType.ERROR) {
                    val = cell.getErrorCellValue();
                }

            }
        } catch (Exception e) {
            return val;
        }
        return val;
    }

    /**
     * 判断是否是空行
     *
     * @param row 判断的行
     * @return
     */
    private boolean isRowEmpty(Row row) {
        if (row == null) {
            return true;
        }
        for (int i = row.getFirstCellNum(); i < row.getLastCellNum(); i++) {
            Cell cell = row.getCell(i);
            if (cell != null && cell.getCellType() != CellType.BLANK) {
                return false;
            }
        }
        return true;
    }

    /**
     * 格式化不同类型的日期对象
     *
     * @param dateFormat 日期格式
     * @param val        被格式化的日期对象
     * @return 格式化后的日期字符
     */
    public String parseDateToStr(String dateFormat, Object val) {
        if (val == null) {
            return "";
        }
        String str;
        if (val instanceof Date) {
            str = DateUtil.parseDateToStr(dateFormat, (Date) val);
        } else if (val instanceof LocalDateTime) {
            str = DateUtil.parseDateToStr(dateFormat, DateUtil.toDate((LocalDateTime) val));
        } else if (val instanceof LocalDate) {
            str = DateUtil.parseDateToStr(dateFormat, DateUtil.toDate((LocalDate) val));
        } else {
            str = val.toString();
        }
        return str;
    }

    /**
     * 是否有对象的子列表
     */
    public boolean isSubList() {
        return CollUtil.isNotEmpty(subFields);
    }

    /**
     * 是否有对象的子列表,集合不为空
     */
    public boolean isSubListValue(T vo) {
        return CollUtil.isNotEmpty(subFields) && CollUtil.isNotEmpty(getListCellValue(vo));
    }

    /**
     * 获取集合的值
     */
    public Collection<?> getListCellValue(Object obj) {
        Object value;
        try {
            value = subMethod.invoke(obj);
        } catch (Exception e) {
            return new ArrayList<>();
        }
        return (Collection<?>) value;
    }

    /**
     * 获取对象的子列表方法
     *
     * @param name      名称
     * @param pojoClass 类对象
     * @return 子列表方法
     */
    public Method getSubMethod(String name, Class<?> pojoClass) {
        StringBuilder getMethodName = new StringBuilder("get");
        getMethodName.append(name.substring(0, 1).toUpperCase());
        getMethodName.append(name.substring(1));
        Method method = null;
        try {
            method = pojoClass.getMethod(getMethodName.toString());
        } catch (Exception e) {
            log.error("获取对象异常{}", e.getMessage());
        }
        return method;
    }

    /**
     * 设置某些列的值只能输入预制的数据,显示下拉框(兼容超出一定数量的下拉框).
     *
     * @param sheet         要设置的sheet.
     * @param textList      下拉框显示的内容
     * @param promptContent 提示内容
     * @param firstRow      开始行
     * @param endRow        结束行
     * @param firstCol      开始列
     * @param endCol        结束列
     */
    public void setXSSFValidationWithHidden(Sheet sheet, String[] textList, String promptContent, int firstRow, int endRow, int firstCol, int endCol) {
        String hideSheetName = "combo_" + firstCol + "_" + endCol;
        Sheet hideSheet = wb.createSheet(hideSheetName); // 用于存储 下拉菜单数据
        for (int i = 0; i < textList.length; i++) {
            hideSheet.createRow(i).createCell(0).setCellValue(textList[i]);
        }
        // 创建名称,可被其他单元格引用
        Name name = wb.createName();
        name.setNameName(hideSheetName + "_data");
        name.setRefersToFormula(hideSheetName + "!$A$1:$A$" + textList.length);
        DataValidationHelper helper = sheet.getDataValidationHelper();
        // 加载下拉列表内容
        DataValidationConstraint constraint = helper.createFormulaListConstraint(hideSheetName + "_data");
        // 设置数据有效性加载在哪个单元格上,四个参数分别是:起始行、终止行、起始列、终止列
        CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol);
        // 数据有效性对象
        DataValidation dataValidation = helper.createValidation(constraint, regions);
        if (StringUtils.isNotEmpty(promptContent)) {
            // 如果设置了提示信息则鼠标放上去提示
            dataValidation.createPromptBox("", promptContent);
            dataValidation.setShowPromptBox(true);
        }
        // 处理Excel兼容性问题
        if (dataValidation instanceof XSSFDataValidation) {
            dataValidation.setSuppressDropDownArrow(true);
            dataValidation.setShowErrorBox(true);
        } else {
            dataValidation.setSuppressDropDownArrow(false);
        }

        sheet.addValidationData(dataValidation);
        // 设置hiddenSheet隐藏
        wb.setSheetHidden(wb.getSheetIndex(hideSheet), true);
    }

}

客户端工具类

package com.xueyi.common.core.utils.servlet;

import cn.hutool.core.exceptions.UtilException;
import cn.hutool.core.io.IoUtil;
import com.alibaba.fastjson2.JSON;
import com.xueyi.common.core.constant.basic.HttpConstants;
import com.xueyi.common.core.constant.basic.TokenConstants;
import com.xueyi.common.core.utils.core.ArrayUtil;
import com.xueyi.common.core.utils.core.ConvertUtil;
import com.xueyi.common.core.utils.core.StrUtil;
import com.xueyi.common.core.web.result.R;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.util.LinkedCaseInsensitiveMap;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

/**
 * 客户端工具类
 *
 * @author xueyi
 */
@Slf4j
public class ServletUtil {

    /**
     * 获取请求token
     */
    public static String getToken(ServerHttpRequest request) {
        String token = request.getHeaders().getFirst(TokenConstants.SUPPLY_AUTHORIZATION);
        if (StrUtil.isBlank(token))
            token = request.getHeaders().getFirst(TokenConstants.AUTHENTICATION);
        return getToken(token);
    }

    /**
     * 获取请求token
     */
    public static String getToken(String token) {
        if (StrUtil.isNotEmpty(token) && StrUtil.startWith(token, TokenConstants.PREFIX)) {
            token = StrUtil.replaceFirst(token, TokenConstants.PREFIX, StrUtil.EMPTY);
        }
        return token;
    }

    /**
     * 鉴权失败处理
     */
    public static Mono<Void> unauthorizedResponse(ServerWebExchange exchange, String msg) {
        log.error("[鉴权异常处理]请求路径:{}", exchange.getRequest().getPath());
        return ServletUtil.webFluxResponseWriter(exchange.getResponse(), msg, HttpConstants.Status.UNAUTHORIZED.getCode());
    }

    /**
     * 增加请求头
     */
    public static void addHeader(ServerHttpRequest.Builder mutate, String name, Object value) {
        if (value == null)
            return;
        String valueStr = value.toString();
        String valueEncode = ServletUtil.urlEncode(valueStr);
        mutate.header(name, valueEncode);
    }

    /**
     * 移除请求头
     */
    public static void removeHeader(ServerHttpRequest.Builder mutate, String name) {
        mutate.headers(httpHeaders -> httpHeaders.remove(name)).build();
    }

    /**
     * 获取String参数
     */
    public static String getParameter(String name) {
        return getRequest().getParameter(name);
    }

    /**
     * 获取参数Map
     */
    public static MultiValueMap<String, String> getParameters(HttpServletRequest request) {
        Map<String, String[]> parameterMap = request.getParameterMap();
        MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>(parameterMap.size());
        parameterMap.forEach((key, values) -> {
            for (String value : values) {
                parameters.add(key, value);
            }
        });
        return parameters;
    }

    /**
     * 获取String参数
     */
    public static String getParameter(String name, String defaultValue) {
        return ConvertUtil.toStr(getRequest().getParameter(name), defaultValue);
    }

    /**
     * 获取Integer参数
     */
    public static Integer getParameterToInt(String name) {
        return ConvertUtil.toInt(getRequest().getParameter(name));
    }

    /**
     * 获取Integer参数
     */
    public static Integer getParameterToInt(String name, Integer defaultValue) {
        return ConvertUtil.toInt(getRequest().getParameter(name), defaultValue);
    }

    /**
     * 获取Boolean参数
     */
    public static Boolean getParameterToBool(String name) {
        return ConvertUtil.toBool(getRequest().getParameter(name));
    }

    /**
     * 获取Boolean参数
     */
    public static Boolean getParameterToBool(String name, Boolean defaultValue) {
        return ConvertUtil.toBool(getRequest().getParameter(name), defaultValue);
    }

    /**
     * 获得所有请求参数
     *
     * @param request 请求对象{@link ServletRequest}
     * @return Map
     */
    public static Map<String, String[]> getParams(ServletRequest request) {
        final Map<String, String[]> map = request.getParameterMap();
        return Collections.unmodifiableMap(map);
    }

    /**
     * 获得所有请求参数
     *
     * @param request 请求对象{@link ServletRequest}
     * @return Map
     */
    public static Map<String, String> getParamMap(ServletRequest request) {
        Map<String, String> params = new HashMap<>();
        for (Map.Entry<String, String[]> entry : getParams(request).entrySet()) {
            params.put(entry.getKey(), ArrayUtil.join(entry.getValue(), StrUtil.COMMA));
        }
        return params;
    }

    /**
     * 获取request
     */
    public static HttpServletRequest getRequest() {
        try {
            return getRequestAttributes().getRequest();
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 获取response
     */
    public static HttpServletResponse getResponse() {
        try {
            return getRequestAttributes().getResponse();
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 获取session
     */
    public static HttpSession getSession() {
        return getRequest().getSession();
    }

    public static ServletRequestAttributes getRequestAttributes() {
        try {
            RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
            return (ServletRequestAttributes) attributes;
        } catch (Exception e) {
            return null;
        }
    }

    public static String getHeader(HttpServletRequest request, String name) {
        String value = request.getHeader(name);
        if (StrUtil.isEmpty(value)) {
            return StrUtil.EMPTY;
        }
        return urlDecode(value);
    }

    public static Map<String, String> getHeaders(HttpServletRequest request) {
        Map<String, String> map = new LinkedCaseInsensitiveMap<>();
        Enumeration<String> enumeration = request.getHeaderNames();
        if (enumeration != null) {
            while (enumeration.hasMoreElements()) {
                String key = enumeration.nextElement();
                String value = request.getHeader(key);
                map.put(key, value);
            }
        }
        return map;
    }

    /**
     * 将字符串渲染到客户端
     *
     * @param response 渲染对象
     * @param string   待渲染的字符串
     */
    public static void renderString(HttpServletResponse response, String string) {
        try {
            response.setStatus(HttpConstants.Status.SUCCESS.getCode());
            response.setContentType(MediaType.APPLICATION_JSON_VALUE);
            response.setCharacterEncoding(StandardCharsets.UTF_8.name());
            response.getWriter().print(string);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 是否是Ajax异步请求
     *
     * @param request 请求
     */
    public static boolean isAjaxRequest(HttpServletRequest request) {
        String accept = request.getHeader("accept");
        if (accept != null && accept.contains(MediaType.APPLICATION_JSON_VALUE)) {
            return true;
        }

        String xRequestedWith = request.getHeader("X-Requested-With");
        if (xRequestedWith != null && xRequestedWith.contains("XMLHttpRequest")) {
            return true;
        }

        String uri = request.getRequestURI();
        if (StrUtil.inStringIgnoreCase(uri, ".json", ".xml")) {
            return true;
        }

        String ajax = request.getParameter("__ajax");
        return StrUtil.inStringIgnoreCase(ajax, "json", "xml");
    }

    /**
     * 内容编码
     *
     * @param str 内容
     * @return 编码后的内容
     */
    public static String urlEncode(String str) {
        try {
            return URLEncoder.encode(str, HttpConstants.Character.UTF8.getCode());
        } catch (UnsupportedEncodingException e) {
            return StrUtil.EMPTY;
        }
    }

    /**
     * 内容解码
     *
     * @param str 内容
     * @return 解码后的内容
     */
    public static String urlDecode(String str) {
        try {
            return URLDecoder.decode(str, HttpConstants.Character.UTF8.getCode());
        } catch (UnsupportedEncodingException e) {
            return StrUtil.EMPTY;
        }
    }

    /**
     * 设置webflux模型响应
     *
     * @param response ServerHttpResponse
     * @param value    响应内容
     * @return Mono
     */
    public static Mono<Void> webFluxResponseWriter(ServerHttpResponse response, Object value) {
        return webFluxResponseWriter(response, HttpStatus.OK, value, R.FAIL);
    }

    /**
     * 设置webflux模型响应
     *
     * @param response ServerHttpResponse
     * @param code     响应状态码
     * @param value    响应内容
     * @return Mono
     */
    public static Mono<Void> webFluxResponseWriter(ServerHttpResponse response, Object value, int code) {
        return webFluxResponseWriter(response, HttpStatus.OK, value, code);
    }

    /**
     * 设置webflux模型响应
     *
     * @param response ServerHttpResponse
     * @param status   http状态码
     * @param code     响应状态码
     * @param value    响应内容
     * @return Mono
     */
    public static Mono<Void> webFluxResponseWriter(ServerHttpResponse response, HttpStatus status, Object value, int code) {
        return webFluxResponseWriter(response, MediaType.APPLICATION_JSON_VALUE, status, value, code);
    }

    /**
     * 设置webflux模型响应
     *
     * @param response    ServerHttpResponse
     * @param contentType content-type
     * @param status      http状态码
     * @param code        响应状态码
     * @param value       响应内容
     * @return Mono
     */
    public static Mono<Void> webFluxResponseWriter(ServerHttpResponse response, String contentType, HttpStatus status, Object value, int code) {
        response.setStatusCode(status);
        response.getHeaders().add(HttpHeaders.CONTENT_TYPE, contentType);
        R<?> result = R.fail(code, value.toString());
        DataBuffer dataBuffer = response.bufferFactory().wrap(JSON.toJSONString(result).getBytes());
        return response.writeWith(Mono.just(dataBuffer));
    }


    /**
     * 设置servlet响应
     *
     * @param response HttpServletResponse
     * @param value    响应内容
     */
    public static void webResponseWriter(HttpServletResponse response, Object value) {
        webResponseWriter(response, MediaType.APPLICATION_JSON_VALUE, value);
    }

    /**
     * 设置servlet响应
     *
     * @param response    HttpServletResponse
     * @param contentType content-type
     * @param value       响应内容
     */
    public static void webResponseWriter(HttpServletResponse response, String contentType, Object value) {
        response.setContentType(contentType);
        response.setCharacterEncoding(StandardCharsets.UTF_8.name());
        Writer writer = null;
        try {
            writer = response.getWriter();
            writer.write(JSON.toJSONString(value));
            writer.flush();
        } catch (IOException var8) {
            throw new UtilException(var8);
        } finally {
            IoUtil.close(writer);
        }
    }
}

sql操作工具类

package com.xueyi.common.core.utils.sql;

import com.xueyi.common.core.exception.UtilException;
import com.xueyi.common.core.utils.core.StrUtil;

/**
 * sql操作工具类
 *
 * @author xueyi
 */
public class SqlUtil {

    /** 定义常用的 sql关键字 */
    public static String SQL_REGEX = "select |insert |delete |update |drop |count |exec |chr |mid |master |truncate |char |and |declare ";

    /** 仅支持字母、数字、下划线、空格、逗号、小数点(支持多个字段排序) */
    public static String SQL_PATTERN = "[a-zA-Z0-9_\\ \\,\\.]+";

    /** 限制orderBy最大长度 */
    private static final int ORDER_BY_MAX_LENGTH = 500;

    /**
     * 检查字符,防止注入绕过
     */
    public static String escapeOrderBySql(String value) {
        if (StrUtil.isNotEmpty(value) && !isValidOrderBySql(value)) {
            throw new UtilException("参数不符合规范,不能进行查询");
        }
        if (StrUtil.length(value) > ORDER_BY_MAX_LENGTH) {
            throw new UtilException("参数已超过最大限制,不能进行查询");
        }
        return value;
    }

    /**
     * 验证 order by 语法是否符合规范
     */
    public static boolean isValidOrderBySql(String value) {
        return value.matches(SQL_PATTERN);
    }


    /**
     * SQL关键字检查
     */
    public static void filterKeyword(String value) {
        if (StrUtil.isEmpty(value)) {
            return;
        }
        String[] sqlKeywords = StrUtil.splitToArray(SQL_REGEX, "\\|");
        for (String sqlKeyword : sqlKeywords) {
            if (StrUtil.indexOfIgnoreCase(value, sqlKeyword) > -1) {
                throw new UtilException("参数存在SQL注入风险");
            }
        }
    }

}

时间工具类

package com.xueyi.common.core.utils;

import org.apache.commons.lang3.time.DateFormatUtils;

import java.lang.management.ManagementFactory;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Date;

/**
 * 时间工具类
 *
 * @author xueyi
 */
public class DateUtil extends org.apache.commons.lang3.time.DateUtils {

    public static final String YYYY = "yyyy";

    public static final String YYYY_MM = "yyyy-MM";

    public static final String YYYY_MM_DD = "yyyy-MM-dd";

    public static final String YYYYMMDDHHMMSS = "yyyyMMddHHmmss";

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

    private static final String[] parsePatterns = {
            "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM",
            "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM",
            "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"};

    /**
     * 获取当前Date型日期
     *
     * @return Date() 当前日期
     */
    public static Date getNowDate() {
        return new Date();
    }

    /**
     * 获取当前日期, 默认格式为yyyy-MM-dd
     *
     * @return String
     */
    public static String getDate() {
        return dateTimeNow(YYYY_MM_DD);
    }

    public static String getTime() {
        return dateTimeNow(YYYY_MM_DD_HH_MM_SS);
    }

    public static String dateTimeNow() {
        return dateTimeNow(YYYYMMDDHHMMSS);
    }

    public static String dateTimeNow(final String format) {
        return parseDateToStr(format, new Date());
    }

    public static String dateTime(final Date date) {
        return parseDateToStr(YYYY_MM_DD, date);
    }

    public static String parseDateToStr(final String format, final Date date) {
        return new SimpleDateFormat(format).format(date);
    }

    public static Date dateTime(final String format, final String ts) {
        try {
            return new SimpleDateFormat(format).parse(ts);
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 日期路径 即年/月/日 如2018/08/08
     */
    public static String datePath() {
        Date now = new Date();
        return DateFormatUtils.format(now, "yyyy/MM/dd");
    }

    /**
     * 日期路径 即年/月/日 如20180808
     */
    public static String dateTime() {
        Date now = new Date();
        return DateFormatUtils.format(now, "yyyyMMdd");
    }

    /**
     * 日期型字符串转化为日期 格式
     */
    public static Date parseDate(Object str) {
        if (str == null) {
            return null;
        }
        try {
            return parseDate(str.toString(), parsePatterns);
        } catch (ParseException e) {
            return null;
        }
    }

    /**
     * 获取服务器启动时间
     */
    public static Date getServerStartDate() {
        long time = ManagementFactory.getRuntimeMXBean().getStartTime();
        return new Date(time);
    }

    /**
     * 计算时间差
     *
     * @param startTime 开始时间
     * @param endTime   最后时间
     * @return 时间差(天/小时/分钟)
     */
    public static String timeDistance(Date startTime, Date endTime) {
        long nd = 1000 * 24 * 60 * 60;
        long nh = 1000 * 60 * 60;
        long nm = 1000 * 60;
        // long ns = 1000;
        // 获得两个时间的毫秒时间差异
        long diff = endTime.getTime() - startTime.getTime();
        // 计算差多少天
        long day = diff / nd;
        // 计算差多少小时
        long hour = diff % nd / nh;
        // 计算差多少分钟
        long min = diff % nd % nh / nm;
        // 计算差多少秒//输出结果
        // long sec = diff % nd % nh % nm / ns;
        return day + "天" + hour + "小时" + min + "分钟";
    }


    /**
     * 增加 LocalDateTime ==> Date
     */
    public static Date toDate(LocalDateTime temporalAccessor) {
        ZonedDateTime zdt = temporalAccessor.atZone(ZoneId.systemDefault());
        return Date.from(zdt.toInstant());
    }

    /**
     * 增加 LocalDate ==> Date
     */
    public static Date toDate(LocalDate temporalAccessor) {
        LocalDateTime localDateTime = LocalDateTime.of(temporalAccessor, LocalTime.of(0, 0, 0));
        ZonedDateTime zdt = localDateTime.atZone(ZoneId.systemDefault());
        return Date.from(zdt.toInstant());
    }
}

错误信息处理类

package com.xueyi.common.core.utils;

import com.xueyi.common.core.utils.core.StrUtil;
import org.apache.commons.lang3.exception.ExceptionUtils;

import java.io.PrintWriter;
import java.io.StringWriter;

/**
 * 错误信息处理类
 *
 * @author xueyi
 */
public class ExceptionUtil {

    /**
     * 获取exception的详细错误信息。
     */
    public static String getExceptionMessage(Throwable e) {
        StringWriter sw = new StringWriter();
        e.printStackTrace(new PrintWriter(sw, true));
        return sw.toString();
    }

    public static String getRootErrorMessage(Exception e) {
        Throwable root = ExceptionUtils.getRootCause(e);
        root = (root == null ? e : root);
        if (root == null) {
            return StrUtil.EMPTY;
        }
        String msg = root.getMessage();
        if (msg == null) {
            return StrUtil.NULL;
        }
        return StrUtil.nullToEmpty(msg);
    }
}

树结构组装工具类

package com.xueyi.common.core.utils;

import com.xueyi.common.core.constant.basic.BaseConstants;
import com.xueyi.common.core.exception.UtilException;
import com.xueyi.common.core.exception.base.BaseException;
import com.xueyi.common.core.utils.core.CollUtil;
import com.xueyi.common.core.utils.core.ObjectUtil;
import com.xueyi.common.core.utils.core.StrUtil;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;

/**
 * 树结构组装工具类
 *
 * @author xueyi
 */
public class TreeUtil {

    /**
     * 构建树结构
     * 存在默认参数 详见BaseConstants.Entity
     * IdName = ID | FIdName = PARENT_ID | childrenName = CHILDREN | topNode = TOP_NODE | killScattered = false | killNoneChild = true
     *
     * @param list 组装列表
     * @return 树结构列表
     */
    public static <T> List<T> buildTree(List<T> list) {
        return buildTree(list, BaseConstants.Entity.ID.getCode(),
                BaseConstants.Entity.PARENT_ID.getCode(),
                BaseConstants.Entity.CHILDREN.getCode(),
                BaseConstants.TOP_ID,
                false,
                true);
    }

    /**
     * 构建树结构
     * 存在默认参数 详见BaseConstants.Entity
     * IdName = ID | FIdName = PARENT_ID | childrenName = CHILDREN | topNode = TOP_NODE | killNoneChild = true
     *
     * @param list          组装列表
     * @param killScattered 是否移除无法追溯到顶级节点 (true 是 | false 否)
     * @return 树结构列表
     */
    public static <T> List<T> buildTree(List<T> list, boolean killScattered) {
        return buildTree(list, BaseConstants.Entity.ID.getCode(),
                BaseConstants.Entity.PARENT_ID.getCode(),
                BaseConstants.Entity.CHILDREN.getCode(),
                BaseConstants.TOP_ID,
                killScattered,
                true);
    }

    /**
     * 构建树结构
     * 存在默认参数 详见BaseConstants.Entity
     * IdName = ID | FIdName = PARENT_ID | childrenName = CHILDREN | topNode = TOP_NODE
     *
     * @param list          组装列表
     * @param killScattered 是否移除无法追溯到顶级节点 (true 是 | false 否)
     * @param killNoneChild 是否移除空子节点集合
     * @return 树结构列表
     */
    public static <T> List<T> buildTree(List<T> list, boolean killScattered, boolean killNoneChild) {
        return buildTree(list, BaseConstants.Entity.ID.getCode(),
                BaseConstants.Entity.PARENT_ID.getCode(),
                BaseConstants.Entity.CHILDREN.getCode(),
                BaseConstants.TOP_ID,
                killScattered,
                killNoneChild);
    }

    /**
     * 构建树结构
     * 存在默认参数 详见BaseConstants.Entity
     * IdName = ID | FIdName = PARENT_ID | childrenName = CHILDREN | killNoneChild = true
     *
     * @param list          组装列表
     * @param topNode       顶级节点
     * @param killScattered 是否移除无法追溯到顶级节点 (true 是 | false 否)
     * @return 树结构列表
     */
    public static <T> List<T> buildTree(List<T> list, Serializable topNode, boolean killScattered) {
        return buildTree(list, BaseConstants.Entity.ID.getCode(),
                BaseConstants.Entity.PARENT_ID.getCode(),
                BaseConstants.Entity.CHILDREN.getCode(),
                topNode,
                killScattered,
                true);
    }

    /**
     * 构建树结构
     *
     * @param list          组装列表
     * @param IdName        Id字段名称
     * @param FIdName       父级Id字段名称
     * @param childrenName  children字段名称
     * @param topNode       顶级节点
     * @param killScattered 是否移除无法追溯到顶级节点 (true 是 | false 否)
     * @param killNoneChild 是否移除空子节点集合
     * @return 树结构列表
     */
    public static <T> List<T> buildTree(List<T> list, String IdName, String FIdName, String childrenName, Serializable topNode, boolean killScattered, boolean killNoneChild) {
        List<T> returnList = new ArrayList<>();
        List<Long> tempList = new ArrayList<>();
        T top = null;
        boolean hasTopNode = false;
        try {
            if (CollUtil.isNotEmpty(list)) {
                int IdKey = 0;
                for (T vo : list) {
                    if (IdKey == 0)
                        IdKey = checkAttribute(vo, IdName, IdKey);
                    Field Id = depthRecursive(vo, IdKey).getDeclaredField(IdName);
                    Id.setAccessible(true);
                    if (ObjectUtil.equal(Id.get(vo), topNode)) {
                        hasTopNode = true;
                        top = vo;
                        list.remove(vo);
                        break;
                    }
                }
                for (T vo : list) {
                    Field Id = depthRecursive(vo, IdKey).getDeclaredField(IdName);
                    Id.setAccessible(true);
                    tempList.add((Long) Id.get(vo));
                }
                int FIdKey = 0;
                for (T vo : list) {
                    if (FIdKey == 0)
                        FIdKey = checkAttribute(vo, FIdName, FIdKey);
                    Field FId = depthRecursive(vo, FIdKey).getDeclaredField(FIdName);
                    FId.setAccessible(true);
                    // 如果是顶级节点, 遍历该父节点的所有子节点
                    if (!tempList.contains((Long) FId.get(vo))) {
                        recursionFn(list, vo, IdName, FIdName, childrenName, killNoneChild);
                        returnList.add(vo);
                    }
                }
            }
        } catch (NoSuchFieldException | IllegalAccessException e) {
            throw new UtilException(e);
        }
        if (returnList.isEmpty()) {
            returnList = list;
        }
        if (killScattered) {
            deleteNoTopNode(returnList, FIdName, topNode);
        }
        if (hasTopNode && ObjectUtil.isNotNull(top)) {
            List<T> topList = new ArrayList<>();
            try {
                Field children = depthRecursive(top, checkAttribute(top, childrenName, 0)).getDeclaredField(childrenName);
                children.setAccessible(true);
                if (killNoneChild) {
                    if (CollUtil.isNotEmpty(returnList))
                        children.set(top, returnList);
                } else {
                    children.set(top, returnList);
                }
                topList.add(top);
                return topList;
            } catch (NoSuchFieldException | IllegalAccessException e) {
                throw new UtilException(e);
            }
        }
        return returnList;
    }

    /**
     * 检查是否存在属性
     */
    private static <T> int checkAttribute(T t, String fieldName, int key) {
        Class<?> clazz = t.getClass();
        while (Object.class != clazz) {
            key++;
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                if (StrUtil.equals(fieldName, field.getName())) {
                    return key;
                }
            }
            clazz = clazz.getSuperclass();
        }
        throw new BaseException(StrUtil.format("对象不存在属性{}", fieldName));
    }

    /**
     * 递归泛型深度
     */
    private static <T> Class<?> depthRecursive(T t, int key) {
        Class<?> clazz = t.getClass();
        int i = 1;
        while (i < key) {
            clazz = clazz.getSuperclass();
            i++;
        }
        return clazz;
    }

    /**
     * 递归列表
     */
    private static <T> void recursionFn(List<T> list, T t, String IdName, String FIdName, String childrenName, boolean killNoneChild) {
        // 得到子节点列表
        List<T> childList = getChildList(list, t, IdName, FIdName);
        try {
            Field children = depthRecursive(t, checkAttribute(t, childrenName, 0)).getDeclaredField(childrenName);
            children.setAccessible(true);
            if (killNoneChild) {
                if (CollUtil.isNotEmpty(childList))
                    children.set(t, childList);
            } else {
                children.set(t, childList);
            }
        } catch (NoSuchFieldException | IllegalAccessException e) {
            throw new UtilException(e);
        }
        for (T tChild : childList) {
            if (hasChild(list, tChild, IdName, FIdName)) {
                recursionFn(list, tChild, IdName, FIdName, childrenName, killNoneChild);
            }
        }
    }

    /**
     * 得到子节点列表
     */
    private static <T> List<T> getChildList(List<T> list, T t, String IdName, String FIdName) {
        List<T> tList = new ArrayList<T>();
        Iterator<T> it = list.iterator();
        try {
            while (it.hasNext()) {
                T n = (T) it.next();
                Field Id = depthRecursive(t, checkAttribute(t, IdName, 0)).getDeclaredField(IdName);
                Id.setAccessible(true);
                Field FId = depthRecursive(n, checkAttribute(n, FIdName, 0)).getDeclaredField(FIdName);
                FId.setAccessible(true);
                if (ObjectUtil.isNotNull((Long) FId.get(n)) && ((Long) FId.get(n)).longValue() == ((Long) Id.get(t)).longValue()) {
                    tList.add(n);
                }
            }
        } catch (NoSuchFieldException | IllegalAccessException e) {
            throw new UtilException(e);
        }
        return tList;
    }

    /**
     * 判断是否有子节点
     */
    private static <T> boolean hasChild(List<T> list, T t, String IdName, String FIdName) {
        return CollUtil.isNotEmpty(getChildList(list, t, IdName, FIdName));
    }

    /**
     * 删除无法溯源至顶级节点的值
     */
    private static <T> void deleteNoTopNode(List<T> list, String FIdName, Serializable topNode) {
        list.removeIf(vo -> {
            try {
                Field FId = depthRecursive(vo, checkAttribute(vo, FIdName, 0)).getDeclaredField(FIdName);
                FId.setAccessible(true);
                return !Objects.equals(FId.get(vo), topNode);
            } catch (NoSuchFieldException | IllegalAccessException e) {
                throw new UtilException(e);
            }
        });
    }

    /**
     * 获取树结构叶子节点集合
     * 存在默认参数 详见BaseConstants.Entity
     * childrenName = CHILDREN
     *
     * @param list 组装列表
     * @return 叶子节点集合
     */
    public static <T> List<T> getLeafNodes(List<T> list) {
        return getLeafNodes(list, BaseConstants.Entity.CHILDREN.getCode());
    }

    /**
     * 获取树结构叶子节点集合
     * 存在默认参数 详见BaseConstants.Entity
     *
     * @param list         组装列表
     * @param childrenName children字段名称
     * @return 叶子节点集合
     */
    public static <T> List<T> getLeafNodes(List<T> list, String childrenName) {
        List<T> returnList = new ArrayList<>();
        try {
            if (CollUtil.isNotEmpty(list)) {
                T t = list.stream().findFirst().orElse(null);
                assert t != null;
                int childrenKey = checkAttribute(t, childrenName, 0);
                recursionLeafFn(returnList, list, childrenName, childrenKey);
            }
        } catch (Exception ignored) {

        }
        return returnList;
    }

    /**
     * 递归列表
     */
    private static <T> void recursionLeafFn(List<T> returnList, List<T> list, String childrenName, int childrenKey) {
        for (T vo : list) {
            try {
                Field children = depthRecursive(vo, childrenKey).getDeclaredField(childrenName);
                children.setAccessible(true);
                List<T> childList = (List<T>) children.get(vo);
                if (CollUtil.isEmpty(childList)) {
                    returnList.add(vo);
                } else {
                    recursionLeafFn(returnList, childList, childrenName, childrenKey);
                }
            } catch (NoSuchFieldException | IllegalAccessException e) {
                throw new UtilException(e);
            }
        }
    }
}

Base 基类

package com.xueyi.common.core.web.entity.base;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.FieldStrategy;
import com.baomidou.mybatisplus.annotation.OrderBy;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import lombok.EqualsAndHashCode;

import java.io.Serial;
import java.time.LocalDateTime;

import static com.baomidou.mybatisplus.annotation.SqlCondition.LIKE;

/**
 * Base 基类
 *
 * @author xueyi
 */
@Data
@EqualsAndHashCode(callSuper = true)
public class BaseEntity extends BasisEntity {

    @Serial
    private static final long serialVersionUID = 1L;

    /** 名称 */
    @TableField(condition = LIKE)
    protected String name;

    /** 状态(0启用 1禁用) */
    protected String status;

    /** 显示顺序 */
    @OrderBy(asc = true, sort = 10)
    protected Integer sort;

    /** 备注 */
    @TableField(condition = LIKE, updateStrategy = FieldStrategy.ALWAYS)
    protected String remark;

    /** 创建者Id */
    @TableField(fill = FieldFill.INSERT, updateStrategy = FieldStrategy.NEVER)
    protected Long createBy;

    /** 创建时间 */
    @OrderBy(sort = 20)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    @TableField(insertStrategy = FieldStrategy.NEVER, updateStrategy = FieldStrategy.NEVER)
    protected LocalDateTime createTime;

    /** 更新者Id */
    @TableField(fill = FieldFill.UPDATE, insertStrategy = FieldStrategy.NEVER)
    protected Long updateBy;

    /** 更新时间 */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    @TableField(insertStrategy = FieldStrategy.NEVER, updateStrategy = FieldStrategy.NEVER)
    protected LocalDateTime updateTime;

    /** 删除标志 */
    @JsonIgnore
    @TableLogic
    @TableField(select = false)
    protected Long delFlag;

    /** 创建者 */
    @TableField(exist = false)
    protected String createName;

    /** 创建者 */
    @TableField(exist = false)
    protected String updateName;

}

Basis 基类

package com.xueyi.common.core.web.entity.base;

import com.baomidou.mybatisplus.annotation.OrderBy;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.xueyi.common.core.constant.basic.BaseConstants;
import com.xueyi.common.core.utils.core.ObjectUtil;
import com.xueyi.common.core.web.validate.V_E;
import jakarta.validation.constraints.NotNull;
import lombok.Data;

import java.io.Serial;
import java.io.Serializable;
import java.util.Map;

/**
 * Basis 基类
 *
 * @author xueyi
 */
@Data
public class BasisEntity implements Serializable {

    @Serial
    private static final long serialVersionUID = 1L;

    /** Id */
    @TableId
    @OrderBy
    @NotNull(message = "id不能为空", groups = {V_E.class})
    protected Long id;

    /** 显示顺序 */
    @TableField(exist = false)
    protected Integer sort;

    /** 数据源名称 */
    @JsonIgnore
    @TableField(exist = false)
    protected String sourceName;

    /** 操作类型 */
    @JsonIgnore
    @TableField(exist = false)
    protected BaseConstants.Operate operate;

    /** 请求参数 */
    @JsonInclude(JsonInclude.Include.NON_EMPTY)
    @TableField(exist = false)
    protected Map<String, Object> params;

    @JsonIgnore
    public String getIdStr() {
        return ObjectUtil.isNotNull(id) ? id.toString() : null;
    }

    @JsonIgnore
    @SuppressWarnings("all")
    public void initId() {
        id = null;
    }

    /**
     * 初始化操作类型
     *
     * @param operate 操作类型
     */
    @JsonIgnore
    public void initOperate(BaseConstants.Operate operate) {
        if (ObjectUtil.isNull(this.operate))
            this.operate = operate;
    }
}

Tree 基类

package com.xueyi.common.core.web.entity.base;

import com.baomidou.mybatisplus.annotation.TableField;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.xueyi.common.core.utils.core.ConvertUtil;
import com.xueyi.common.core.utils.core.NumberUtil;
import com.xueyi.common.core.utils.core.ObjectUtil;
import com.xueyi.common.core.utils.core.StrUtil;
import lombok.Data;
import lombok.EqualsAndHashCode;

import java.io.Serial;
import java.util.List;

/**
 * Tree 基类
 *
 * @param  Dto
 * @author xueyi
 */
@Data
@EqualsAndHashCode(callSuper = true)
public class TreeEntity<D> extends BaseEntity {

    @Serial
    private static final long serialVersionUID = 1L;

    /** 父级Id */
    protected Long parentId;

    /** 父级名称 */
    @TableField(exist = false)
    protected String parentName;

    /** 祖籍列表 */
    @JsonIgnore
    protected String ancestors;

    /** 层级 */
    protected Integer level;

    /** 存在默认顶级(true存在 false不存在) */
    @JsonIgnore
    @TableField(exist = false)
    protected Boolean defaultNode;

    /** 自定义顶级节点名称 */
    @JsonIgnore
    @TableField(exist = false)
    protected String topNodeName;

    /** 移除当前及子节点(true是 false否) */
    @JsonIgnore
    @TableField(exist = false)
    protected Boolean exNodes;

    /** 子节点集合 */
    @TableField(exist = false)
    protected List<D> children;

    /** 原始祖籍列表 */
    @JsonIgnore
    @TableField(exist = false)
    protected String oldAncestors;

    /** 原始层级 */
    @JsonIgnore
    @TableField(exist = false)
    protected Integer oldLevel;

    /** 判定是否存在默认顶级 */
    @JsonIgnore
    public Boolean getDefaultNode() {
        return ObjectUtil.isNotNull(this.defaultNode) ? this.defaultNode : Boolean.FALSE;
    }

    /** 判定是否移除当前及子节点 */
    @JsonIgnore
    public Boolean getExNodes() {
        return ObjectUtil.isNotNull(this.exNodes) ? this.exNodes : Boolean.FALSE;
    }

    /** 获取当前节点的子节点对应的祖籍 */
    @JsonIgnore
    public String getChildAncestors() {
        return this.ancestors + StrUtil.COMMA + this.id;
    }

    /** 获取当前节点的子节点对应的原始祖籍 */
    @JsonIgnore
    public String getOldChildAncestors() {
        return this.oldAncestors + StrUtil.COMMA + this.id;
    }

    /** 计算节点变化的层级数 */
    @JsonIgnore
    public int getLevelChange() {
        return ConvertUtil.toInt(this.level, NumberUtil.Zero) - ConvertUtil.toInt(this.oldLevel, NumberUtil.Zero);
    }
}

Base 混合基类

package com.xueyi.common.core.web.entity.common;

import com.baomidou.mybatisplus.annotation.FieldStrategy;
import com.baomidou.mybatisplus.annotation.TableField;
import com.xueyi.common.core.constant.basic.DictConstants;
import com.xueyi.common.core.utils.core.StrUtil;
import com.xueyi.common.core.web.entity.base.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;

import java.io.Serial;

/**
 * Base 混合基类
 *
 * @author xueyi
 */
@Data
@EqualsAndHashCode(callSuper = true)
public class CBaseEntity extends BaseEntity {

    @Serial
    private static final long serialVersionUID = 1L;

    /** 公共数据(Y是 N否) */
    @TableField(updateStrategy = FieldStrategy.NEVER)
    protected String isCommon;

    /** 校验是否为公共数据 */
    public boolean isCommon() {
        return StrUtil.equals(DictConstants.DicCommonPrivate.COMMON.getCode(), getIsCommon());
    }

    /** 校验是否非公共数据 */
    public boolean isNotCommon() {
        return !isCommon();
    }
}

Basis 混合基类

package com.xueyi.common.core.web.entity.common;

import com.baomidou.mybatisplus.annotation.FieldStrategy;
import com.baomidou.mybatisplus.annotation.TableField;
import com.xueyi.common.core.constant.basic.DictConstants;
import com.xueyi.common.core.utils.core.StrUtil;
import com.xueyi.common.core.web.entity.base.BasisEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;

import java.io.Serial;

/**
 * Basis 混合基类
 *
 * @author xueyi
 */
@Data
@EqualsAndHashCode(callSuper = true)
public class CBasisEntity extends BasisEntity {

    @Serial
    private static final long serialVersionUID = 1L;

    /** 公共数据(0是 1否) */
    @TableField(updateStrategy = FieldStrategy.NEVER)
    protected String isCommon;

    /** 校验是否为公共数据 */
    public boolean isCommon() {
        return StrUtil.equals(DictConstants.DicCommonPrivate.COMMON.getCode(), getIsCommon());
    }

    /** 校验是否非公共数据 */
    public boolean isNotCommon() {
        return !isCommon();
    }
}

Tree 混合基类

package com.xueyi.common.core.web.entity.common;

import com.baomidou.mybatisplus.annotation.FieldStrategy;
import com.baomidou.mybatisplus.annotation.TableField;
import com.xueyi.common.core.constant.basic.DictConstants;
import com.xueyi.common.core.utils.core.StrUtil;
import com.xueyi.common.core.web.entity.base.TreeEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;

import java.io.Serial;

/**
 * Tree 混合基类
 *
 * @param  Dto
 * @author xueyi
 */
@Data
@EqualsAndHashCode(callSuper = true)
public class CTreeEntity<D> extends TreeEntity<D> {

    @Serial
    private static final long serialVersionUID = 1L;

    /** 公共数据(Y是 N否) */
    @TableField(updateStrategy = FieldStrategy.NEVER)
    protected String isCommon;

    /** 校验是否为公共数据 */
    public boolean isCommon() {
        return StrUtil.equals(DictConstants.DicCommonPrivate.COMMON.getCode(), getIsCommon());
    }

    /** 校验是否非公共数据 */
    public boolean isNotCommon() {
        return !isCommon();
    }
}

Base 基类 对象映射器

package com.xueyi.common.core.web.entity.model;

import com.github.pagehelper.Page;
import com.xueyi.common.core.web.entity.base.BaseEntity;

import java.util.Collection;
import java.util.List;

/**
 * Base 基类 对象映射器
 *
 * @param  Query
 * @param  Dto
 * @param 

Po * @author xueyi */ public interface BaseConverter<Q extends P, D extends P, P extends BaseEntity> { /** * 类型转换 | 持久化对象 -> 数据传输对象 * * @param po 持久化对象 * @return dto */ D mapperDto(P po); /** * 类型转换 | 持久化对象集合 -> 数据传输对象集合 * * @param poList 持久化对象集合 * @return dtoList */ List<D> mapperDto(Collection<P> poList); /** * 类型转换 | 持久化对象集合 -> 数据传输分页对象集合 * * @param poList 持久化对象集合 * @return dtoList */ Page<D> mapperPageDto(Collection<P> poList); /** * 类型转换 | 数据传输对象 -> 持久化对象 * * @param dto 数据传输对象 * @return po */ P mapperPo(D dto); /** * 类型转换 | 数据传输对象集合 -> 持久化对象集合 * * @param dtoList 数据传输对象集合 * @return poList */ List<P> mapperPo(Collection<D> dtoList); /** * 类型转换 | 数据传输对象集合 -> 持久化分页对象集合 * * @param dtoList 数据传输对象集合 * @return poList */ Page<P> mapperPagePo(Collection<D> dtoList); }

Tree 基类 对象映射器

package com.xueyi.common.core.web.entity.model;

import com.xueyi.common.core.web.entity.base.TreeEntity;
import org.mapstruct.Mapping;

import java.util.Collection;
import java.util.List;

/**
 * Tree 基类 对象映射器
 *
 * @param  Query
 * @param  Dto
 * @param 

Po * @author xueyi */ public interface TreeConverter<Q extends P, D extends P, P extends TreeEntity<D>> extends BaseConverter<Q, D, P> { @Mapping(target = "children", ignore = true) D mapperDto(P po); @Mapping(target = "children", ignore = true) List<D> mapperDto(Collection<P> poList); @Mapping(target = "children", ignore = true) P mapperPo(D po); @Mapping(target = "children", ignore = true) List<P> mapperPo(Collection<D> poList); }

tion.TableField;
import com.xueyi.common.core.constant.basic.DictConstants;
import com.xueyi.common.core.utils.core.StrUtil;
import com.xueyi.common.core.web.entity.base.BasisEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;

import java.io.Serial;

/**

  • Basis 混合基类

  • @author xueyi
    */
    @Data
    @EqualsAndHashCode(callSuper = true)
    public class CBasisEntity extends BasisEntity {

    @Serial
    private static final long serialVersionUID = 1L;

    /** 公共数据(0是 1否) */
    @TableField(updateStrategy = FieldStrategy.NEVER)
    protected String isCommon;

    /** 校验是否为公共数据 */
    public boolean isCommon() {
    return StrUtil.equals(DictConstants.DicCommonPrivate.COMMON.getCode(), getIsCommon());
    }

    /** 校验是否非公共数据 */
    public boolean isNotCommon() {
    return !isCommon();
    }
    }


## Tree 混合基类	

~~~java
package com.xueyi.common.core.web.entity.common;

import com.baomidou.mybatisplus.annotation.FieldStrategy;
import com.baomidou.mybatisplus.annotation.TableField;
import com.xueyi.common.core.constant.basic.DictConstants;
import com.xueyi.common.core.utils.core.StrUtil;
import com.xueyi.common.core.web.entity.base.TreeEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;

import java.io.Serial;

/**
 * Tree 混合基类
 *
 * @param  Dto
 * @author xueyi
 */
@Data
@EqualsAndHashCode(callSuper = true)
public class CTreeEntity extends TreeEntity {

    @Serial
    private static final long serialVersionUID = 1L;

    /** 公共数据(Y是 N否) */
    @TableField(updateStrategy = FieldStrategy.NEVER)
    protected String isCommon;

    /** 校验是否为公共数据 */
    public boolean isCommon() {
        return StrUtil.equals(DictConstants.DicCommonPrivate.COMMON.getCode(), getIsCommon());
    }

    /** 校验是否非公共数据 */
    public boolean isNotCommon() {
        return !isCommon();
    }
}

Base 基类 对象映射器

package com.xueyi.common.core.web.entity.model;

import com.github.pagehelper.Page;
import com.xueyi.common.core.web.entity.base.BaseEntity;

import java.util.Collection;
import java.util.List;

/**
 * Base 基类 对象映射器
 *
 * @param  Query
 * @param  Dto
 * @param 

Po * @author xueyi */ public interface BaseConverter<Q extends P, D extends P, P extends BaseEntity> { /** * 类型转换 | 持久化对象 -> 数据传输对象 * * @param po 持久化对象 * @return dto */ D mapperDto(P po); /** * 类型转换 | 持久化对象集合 -> 数据传输对象集合 * * @param poList 持久化对象集合 * @return dtoList */ List<D> mapperDto(Collection<P> poList); /** * 类型转换 | 持久化对象集合 -> 数据传输分页对象集合 * * @param poList 持久化对象集合 * @return dtoList */ Page<D> mapperPageDto(Collection<P> poList); /** * 类型转换 | 数据传输对象 -> 持久化对象 * * @param dto 数据传输对象 * @return po */ P mapperPo(D dto); /** * 类型转换 | 数据传输对象集合 -> 持久化对象集合 * * @param dtoList 数据传输对象集合 * @return poList */ List<P> mapperPo(Collection<D> dtoList); /** * 类型转换 | 数据传输对象集合 -> 持久化分页对象集合 * * @param dtoList 数据传输对象集合 * @return poList */ Page<P> mapperPagePo(Collection<D> dtoList); }

Tree 基类 对象映射器

package com.xueyi.common.core.web.entity.model;

import com.xueyi.common.core.web.entity.base.TreeEntity;
import org.mapstruct.Mapping;

import java.util.Collection;
import java.util.List;

/**
 * Tree 基类 对象映射器
 *
 * @param  Query
 * @param  Dto
 * @param 

Po * @author xueyi */ public interface TreeConverter<Q extends P, D extends P, P extends TreeEntity<D>> extends BaseConverter<Q, D, P> { @Mapping(target = "children", ignore = true) D mapperDto(P po); @Mapping(target = "children", ignore = true) List<D> mapperDto(Collection<P> poList); @Mapping(target = "children", ignore = true) P mapperPo(D po); @Mapping(target = "children", ignore = true) List<P> mapperPo(Collection<D> poList); }

你可能感兴趣的:(Java,secret,Java,secret)