ret) {
+ return Result.SUCCESS == ret.getCode();
+ }
+
+}
diff --git a/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/enums/UserStatus.java b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/enums/UserStatus.java
new file mode 100644
index 0000000..32ff39a
--- /dev/null
+++ b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/enums/UserStatus.java
@@ -0,0 +1,26 @@
+package com.muyu.common.core.enums;
+
+/**
+ * 用户状态
+ *
+ * @author muyu
+ */
+public enum UserStatus {
+ OK("0", "正常"), DISABLE("1", "停用"), DELETED("2", "删除");
+
+ private final String code;
+ private final String info;
+
+ UserStatus (String code, String info) {
+ this.code = code;
+ this.info = info;
+ }
+
+ public String getCode () {
+ return code;
+ }
+
+ public String getInfo () {
+ return info;
+ }
+}
diff --git a/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/exception/CaptchaException.java b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/exception/CaptchaException.java
new file mode 100644
index 0000000..eb32d0b
--- /dev/null
+++ b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/exception/CaptchaException.java
@@ -0,0 +1,14 @@
+package com.muyu.common.core.exception;
+
+/**
+ * 验证码错误异常类
+ *
+ * @author muyu
+ */
+public class CaptchaException extends RuntimeException {
+ private static final long serialVersionUID = 1L;
+
+ public CaptchaException (String msg) {
+ super(msg);
+ }
+}
diff --git a/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/exception/CheckedException.java b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/exception/CheckedException.java
new file mode 100644
index 0000000..4f12893
--- /dev/null
+++ b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/exception/CheckedException.java
@@ -0,0 +1,26 @@
+package com.muyu.common.core.exception;
+
+/**
+ * 检查异常
+ *
+ * @author muyu
+ */
+public class CheckedException extends RuntimeException {
+ private static final long serialVersionUID = 1L;
+
+ public CheckedException (String message) {
+ super(message);
+ }
+
+ public CheckedException (Throwable cause) {
+ super(cause);
+ }
+
+ public CheckedException (String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public CheckedException (String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
+ super(message, cause, enableSuppression, writableStackTrace);
+ }
+}
diff --git a/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/exception/DemoModeException.java b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/exception/DemoModeException.java
new file mode 100644
index 0000000..82249cf
--- /dev/null
+++ b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/exception/DemoModeException.java
@@ -0,0 +1,13 @@
+package com.muyu.common.core.exception;
+
+/**
+ * 演示模式异常
+ *
+ * @author muyu
+ */
+public class DemoModeException extends RuntimeException {
+ private static final long serialVersionUID = 1L;
+
+ public DemoModeException () {
+ }
+}
diff --git a/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/exception/GlobalException.java b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/exception/GlobalException.java
new file mode 100644
index 0000000..b14e03c
--- /dev/null
+++ b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/exception/GlobalException.java
@@ -0,0 +1,51 @@
+package com.muyu.common.core.exception;
+
+/**
+ * 全局异常
+ *
+ * @author muyu
+ */
+public class GlobalException extends RuntimeException {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 错误提示
+ */
+ private String message;
+
+ /**
+ * 错误明细,内部调试错误
+ *
+ * 和 {@link CommonResult#getDetailMessage()} 一致的设计
+ */
+ private String detailMessage;
+
+ /**
+ * 空构造方法,避免反序列化问题
+ */
+ public GlobalException () {
+ }
+
+ public GlobalException (String message) {
+ this.message = message;
+ }
+
+ public String getDetailMessage () {
+ return detailMessage;
+ }
+
+ public GlobalException setDetailMessage (String detailMessage) {
+ this.detailMessage = detailMessage;
+ return this;
+ }
+
+ @Override
+ public String getMessage () {
+ return message;
+ }
+
+ public GlobalException setMessage (String message) {
+ this.message = message;
+ return this;
+ }
+}
diff --git a/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/exception/InnerAuthException.java b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/exception/InnerAuthException.java
new file mode 100644
index 0000000..f211c7f
--- /dev/null
+++ b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/exception/InnerAuthException.java
@@ -0,0 +1,14 @@
+package com.muyu.common.core.exception;
+
+/**
+ * 内部认证异常
+ *
+ * @author muyu
+ */
+public class InnerAuthException extends RuntimeException {
+ private static final long serialVersionUID = 1L;
+
+ public InnerAuthException (String message) {
+ super(message);
+ }
+}
diff --git a/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/exception/PreAuthorizeException.java b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/exception/PreAuthorizeException.java
new file mode 100644
index 0000000..6cb8636
--- /dev/null
+++ b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/exception/PreAuthorizeException.java
@@ -0,0 +1,13 @@
+package com.muyu.common.core.exception;
+
+/**
+ * 权限异常
+ *
+ * @author muyu
+ */
+public class PreAuthorizeException extends RuntimeException {
+ private static final long serialVersionUID = 1L;
+
+ public PreAuthorizeException () {
+ }
+}
diff --git a/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/exception/ServiceException.java b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/exception/ServiceException.java
new file mode 100644
index 0000000..5039bc0
--- /dev/null
+++ b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/exception/ServiceException.java
@@ -0,0 +1,65 @@
+package com.muyu.common.core.exception;
+
+/**
+ * 业务异常
+ *
+ * @author muyu
+ */
+public final class ServiceException extends RuntimeException {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 错误码
+ */
+ private Integer code;
+
+ /**
+ * 错误提示
+ */
+ private String message;
+
+ /**
+ * 错误明细,内部调试错误
+ *
+ * 和 {@link CommonResult#getDetailMessage()} 一致的设计
+ */
+ private String detailMessage;
+
+ /**
+ * 空构造方法,避免反序列化问题
+ */
+ public ServiceException () {
+ }
+
+ public ServiceException (String message) {
+ this.message = message;
+ }
+
+ public ServiceException (String message, Integer code) {
+ this.message = message;
+ this.code = code;
+ }
+
+ public String getDetailMessage () {
+ return detailMessage;
+ }
+
+ public ServiceException setDetailMessage (String detailMessage) {
+ this.detailMessage = detailMessage;
+ return this;
+ }
+
+ @Override
+ public String getMessage () {
+ return message;
+ }
+
+ public ServiceException setMessage (String message) {
+ this.message = message;
+ return this;
+ }
+
+ public Integer getCode () {
+ return code;
+ }
+}
diff --git a/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/exception/UtilException.java b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/exception/UtilException.java
new file mode 100644
index 0000000..8de4bbf
--- /dev/null
+++ b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/exception/UtilException.java
@@ -0,0 +1,22 @@
+package com.muyu.common.core.exception;
+
+/**
+ * 工具类异常
+ *
+ * @author muyu
+ */
+public class UtilException extends RuntimeException {
+ private static final long serialVersionUID = 8247610319171014183L;
+
+ public UtilException (Throwable e) {
+ super(e.getMessage(), e);
+ }
+
+ public UtilException (String message) {
+ super(message);
+ }
+
+ public UtilException (String message, Throwable throwable) {
+ super(message, throwable);
+ }
+}
diff --git a/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/exception/auth/NotLoginException.java b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/exception/auth/NotLoginException.java
new file mode 100644
index 0000000..40293bf
--- /dev/null
+++ b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/exception/auth/NotLoginException.java
@@ -0,0 +1,14 @@
+package com.muyu.common.core.exception.auth;
+
+/**
+ * 未能通过的登录认证异常
+ *
+ * @author muyu
+ */
+public class NotLoginException extends RuntimeException {
+ private static final long serialVersionUID = 1L;
+
+ public NotLoginException (String message) {
+ super(message);
+ }
+}
diff --git a/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/exception/auth/NotPermissionException.java b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/exception/auth/NotPermissionException.java
new file mode 100644
index 0000000..e464840
--- /dev/null
+++ b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/exception/auth/NotPermissionException.java
@@ -0,0 +1,20 @@
+package com.muyu.common.core.exception.auth;
+
+import org.apache.commons.lang3.StringUtils;
+
+/**
+ * 未能通过的权限认证异常
+ *
+ * @author muyu
+ */
+public class NotPermissionException extends RuntimeException {
+ private static final long serialVersionUID = 1L;
+
+ public NotPermissionException (String permission) {
+ super(permission);
+ }
+
+ public NotPermissionException (String[] permissions) {
+ super(StringUtils.join(permissions, ","));
+ }
+}
diff --git a/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/exception/auth/NotRoleException.java b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/exception/auth/NotRoleException.java
new file mode 100644
index 0000000..53a1522
--- /dev/null
+++ b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/exception/auth/NotRoleException.java
@@ -0,0 +1,20 @@
+package com.muyu.common.core.exception.auth;
+
+import org.apache.commons.lang3.StringUtils;
+
+/**
+ * 未能通过的角色认证异常
+ *
+ * @author muyu
+ */
+public class NotRoleException extends RuntimeException {
+ private static final long serialVersionUID = 1L;
+
+ public NotRoleException (String role) {
+ super(role);
+ }
+
+ public NotRoleException (String[] roles) {
+ super(StringUtils.join(roles, ","));
+ }
+}
diff --git a/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/exception/base/BaseException.java b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/exception/base/BaseException.java
new file mode 100644
index 0000000..9bb1356
--- /dev/null
+++ b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/exception/base/BaseException.java
@@ -0,0 +1,69 @@
+package com.muyu.common.core.exception.base;
+
+/**
+ * 基础异常
+ *
+ * @author muyu
+ */
+public class BaseException extends RuntimeException {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 所属模块
+ */
+ private String module;
+
+ /**
+ * 错误码
+ */
+ private String code;
+
+ /**
+ * 错误码对应的参数
+ */
+ private Object[] args;
+
+ /**
+ * 错误消息
+ */
+ private String defaultMessage;
+
+ public BaseException (String module, String code, Object[] args, String defaultMessage) {
+ this.module = module;
+ this.code = code;
+ this.args = args;
+ this.defaultMessage = defaultMessage;
+ }
+
+ public BaseException (String module, String code, Object[] args) {
+ this(module, code, args, null);
+ }
+
+ public BaseException (String module, String defaultMessage) {
+ this(module, null, null, defaultMessage);
+ }
+
+ public BaseException (String code, Object[] args) {
+ this(null, code, args, null);
+ }
+
+ public BaseException (String defaultMessage) {
+ this(null, null, null, defaultMessage);
+ }
+
+ public String getModule () {
+ return module;
+ }
+
+ public String getCode () {
+ return code;
+ }
+
+ public Object[] getArgs () {
+ return args;
+ }
+
+ public String getDefaultMessage () {
+ return defaultMessage;
+ }
+}
diff --git a/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/exception/file/FileException.java b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/exception/file/FileException.java
new file mode 100644
index 0000000..ae2e184
--- /dev/null
+++ b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/exception/file/FileException.java
@@ -0,0 +1,17 @@
+package com.muyu.common.core.exception.file;
+
+import com.muyu.common.core.exception.base.BaseException;
+
+/**
+ * 文件信息异常类
+ *
+ * @author muyu
+ */
+public class FileException extends BaseException {
+ private static final long serialVersionUID = 1L;
+
+ public FileException (String code, Object[] args, String msg) {
+ super("file", code, args, msg);
+ }
+
+}
diff --git a/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/exception/file/FileNameLengthLimitExceededException.java b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/exception/file/FileNameLengthLimitExceededException.java
new file mode 100644
index 0000000..3a85df3
--- /dev/null
+++ b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/exception/file/FileNameLengthLimitExceededException.java
@@ -0,0 +1,14 @@
+package com.muyu.common.core.exception.file;
+
+/**
+ * 文件名称超长限制异常类
+ *
+ * @author muyu
+ */
+public class FileNameLengthLimitExceededException extends FileException {
+ private static final long serialVersionUID = 1L;
+
+ public FileNameLengthLimitExceededException (int defaultFileNameLength) {
+ super("upload.filename.exceed.length", new Object[]{defaultFileNameLength}, "the filename is too long");
+ }
+}
diff --git a/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/exception/file/FileSizeLimitExceededException.java b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/exception/file/FileSizeLimitExceededException.java
new file mode 100644
index 0000000..7570be5
--- /dev/null
+++ b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/exception/file/FileSizeLimitExceededException.java
@@ -0,0 +1,14 @@
+package com.muyu.common.core.exception.file;
+
+/**
+ * 文件名大小限制异常类
+ *
+ * @author muyu
+ */
+public class FileSizeLimitExceededException extends FileException {
+ private static final long serialVersionUID = 1L;
+
+ public FileSizeLimitExceededException (long defaultMaxSize) {
+ super("upload.exceed.maxSize", new Object[]{defaultMaxSize}, "the filesize is too large");
+ }
+}
diff --git a/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/exception/file/FileUploadException.java b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/exception/file/FileUploadException.java
new file mode 100644
index 0000000..94341ab
--- /dev/null
+++ b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/exception/file/FileUploadException.java
@@ -0,0 +1,52 @@
+package com.muyu.common.core.exception.file;
+
+import java.io.PrintStream;
+import java.io.PrintWriter;
+
+/**
+ * 文件上传异常类
+ *
+ * @author muyu
+ */
+public class FileUploadException extends Exception {
+
+ private static final long serialVersionUID = 1L;
+
+ private final Throwable cause;
+
+ public FileUploadException () {
+ this(null, null);
+ }
+
+ public FileUploadException (final String msg) {
+ this(msg, null);
+ }
+
+ public FileUploadException (String msg, Throwable cause) {
+ super(msg);
+ this.cause = cause;
+ }
+
+ @Override
+ public void printStackTrace (PrintStream stream) {
+ super.printStackTrace(stream);
+ if (cause != null) {
+ stream.println("Caused by:");
+ cause.printStackTrace(stream);
+ }
+ }
+
+ @Override
+ public void printStackTrace (PrintWriter writer) {
+ super.printStackTrace(writer);
+ if (cause != null) {
+ writer.println("Caused by:");
+ cause.printStackTrace(writer);
+ }
+ }
+
+ @Override
+ public Throwable getCause () {
+ return cause;
+ }
+}
diff --git a/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/exception/file/InvalidExtensionException.java b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/exception/file/InvalidExtensionException.java
new file mode 100644
index 0000000..3a993c2
--- /dev/null
+++ b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/exception/file/InvalidExtensionException.java
@@ -0,0 +1,67 @@
+package com.muyu.common.core.exception.file;
+
+import java.util.Arrays;
+
+/**
+ * 文件上传 误异常类
+ *
+ * @author muyu
+ */
+public class InvalidExtensionException extends FileUploadException {
+ private static final long serialVersionUID = 1L;
+
+ private String[] allowedExtension;
+ private String extension;
+ private String filename;
+
+ public InvalidExtensionException (String[] allowedExtension, String extension, String filename) {
+ super("filename : [" + filename + "], extension : [" + extension + "], allowed extension : [" + Arrays.toString(allowedExtension) + "]");
+ this.allowedExtension = allowedExtension;
+ this.extension = extension;
+ this.filename = filename;
+ }
+
+ public String[] getAllowedExtension () {
+ return allowedExtension;
+ }
+
+ public String getExtension () {
+ return extension;
+ }
+
+ public String getFilename () {
+ return filename;
+ }
+
+ public static class InvalidImageExtensionException extends InvalidExtensionException {
+ private static final long serialVersionUID = 1L;
+
+ public InvalidImageExtensionException (String[] allowedExtension, String extension, String filename) {
+ super(allowedExtension, extension, filename);
+ }
+ }
+
+ public static class InvalidFlashExtensionException extends InvalidExtensionException {
+ private static final long serialVersionUID = 1L;
+
+ public InvalidFlashExtensionException (String[] allowedExtension, String extension, String filename) {
+ super(allowedExtension, extension, filename);
+ }
+ }
+
+ public static class InvalidMediaExtensionException extends InvalidExtensionException {
+ private static final long serialVersionUID = 1L;
+
+ public InvalidMediaExtensionException (String[] allowedExtension, String extension, String filename) {
+ super(allowedExtension, extension, filename);
+ }
+ }
+
+ public static class InvalidVideoExtensionException extends InvalidExtensionException {
+ private static final long serialVersionUID = 1L;
+
+ public InvalidVideoExtensionException (String[] allowedExtension, String extension, String filename) {
+ super(allowedExtension, extension, filename);
+ }
+ }
+}
diff --git a/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/exception/job/TaskException.java b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/exception/job/TaskException.java
new file mode 100644
index 0000000..aee364a
--- /dev/null
+++ b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/exception/job/TaskException.java
@@ -0,0 +1,29 @@
+package com.muyu.common.core.exception.job;
+
+/**
+ * 计划策略异常
+ *
+ * @author muyu
+ */
+public class TaskException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ private Code code;
+
+ public TaskException (String msg, Code code) {
+ this(msg, code, null);
+ }
+
+ public TaskException (String msg, Code code, Exception nestedEx) {
+ super(msg, nestedEx);
+ this.code = code;
+ }
+
+ public Code getCode () {
+ return code;
+ }
+
+ public enum Code {
+ TASK_EXISTS, NO_TASK_EXISTS, TASK_ALREADY_STARTED, UNKNOWN, CONFIG_ERROR, TASK_NODE_NOT_AVAILABLE
+ }
+}
diff --git a/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/exception/user/CaptchaExpireException.java b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/exception/user/CaptchaExpireException.java
new file mode 100644
index 0000000..a95a57b
--- /dev/null
+++ b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/exception/user/CaptchaExpireException.java
@@ -0,0 +1,14 @@
+package com.muyu.common.core.exception.user;
+
+/**
+ * 验证码失效异常类
+ *
+ * @author muyu
+ */
+public class CaptchaExpireException extends UserException {
+ private static final long serialVersionUID = 1L;
+
+ public CaptchaExpireException () {
+ super("user.jcaptcha.expire", null);
+ }
+}
diff --git a/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/exception/user/UserException.java b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/exception/user/UserException.java
new file mode 100644
index 0000000..f113749
--- /dev/null
+++ b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/exception/user/UserException.java
@@ -0,0 +1,16 @@
+package com.muyu.common.core.exception.user;
+
+import com.muyu.common.core.exception.base.BaseException;
+
+/**
+ * 用户信息异常类
+ *
+ * @author muyu
+ */
+public class UserException extends BaseException {
+ private static final long serialVersionUID = 1L;
+
+ public UserException (String code, Object[] args) {
+ super("user", code, args, null);
+ }
+}
diff --git a/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/exception/user/UserPasswordNotMatchException.java b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/exception/user/UserPasswordNotMatchException.java
new file mode 100644
index 0000000..7615cda
--- /dev/null
+++ b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/exception/user/UserPasswordNotMatchException.java
@@ -0,0 +1,14 @@
+package com.muyu.common.core.exception.user;
+
+/**
+ * 用户密码不正确或不符合规范异常类
+ *
+ * @author muyu
+ */
+public class UserPasswordNotMatchException extends UserException {
+ private static final long serialVersionUID = 1L;
+
+ public UserPasswordNotMatchException () {
+ super("user.password.not.match", null);
+ }
+}
diff --git a/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/text/CharsetKit.java b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/text/CharsetKit.java
new file mode 100644
index 0000000..fe5e9fa
--- /dev/null
+++ b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/text/CharsetKit.java
@@ -0,0 +1,94 @@
+package com.muyu.common.core.text;
+
+import com.muyu.common.core.utils.StringUtils;
+
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * 字符集工具类
+ *
+ * @author muyu
+ */
+public class CharsetKit {
+ /**
+ * ISO-8859-1
+ */
+ public static final String ISO_8859_1 = "ISO-8859-1";
+ /**
+ * UTF-8
+ */
+ public static final String UTF_8 = "UTF-8";
+ /**
+ * GBK
+ */
+ public static final String GBK = "GBK";
+
+ /**
+ * ISO-8859-1
+ */
+ public static final Charset CHARSET_ISO_8859_1 = Charset.forName(ISO_8859_1);
+ /**
+ * UTF-8
+ */
+ public static final Charset CHARSET_UTF_8 = Charset.forName(UTF_8);
+ /**
+ * GBK
+ */
+ public static final Charset CHARSET_GBK = Charset.forName(GBK);
+
+ /**
+ * 转换为Charset对象
+ *
+ * @param charset 字符集,为空则返回默认字符集
+ *
+ * @return Charset
+ */
+ public static Charset charset (String charset) {
+ return StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset);
+ }
+
+ /**
+ * 转换字符串的字符集编码
+ *
+ * @param source 字符串
+ * @param srcCharset 源字符集,默认ISO-8859-1
+ * @param destCharset 目标字符集,默认UTF-8
+ *
+ * @return 转换后的字符集
+ */
+ public static String convert (String source, String srcCharset, String destCharset) {
+ return convert(source, Charset.forName(srcCharset), Charset.forName(destCharset));
+ }
+
+ /**
+ * 转换字符串的字符集编码
+ *
+ * @param source 字符串
+ * @param srcCharset 源字符集,默认ISO-8859-1
+ * @param destCharset 目标字符集,默认UTF-8
+ *
+ * @return 转换后的字符集
+ */
+ public static String convert (String source, Charset srcCharset, Charset destCharset) {
+ if (null == srcCharset) {
+ srcCharset = StandardCharsets.ISO_8859_1;
+ }
+
+ if (null == destCharset) {
+ destCharset = StandardCharsets.UTF_8;
+ }
+
+ if (StringUtils.isEmpty(source) || srcCharset.equals(destCharset)) {
+ return source;
+ }
+ return new String(source.getBytes(srcCharset), destCharset);
+ }
+
+ /**
+ * @return 系统字符集编码
+ */
+ public static String systemCharset () {
+ return Charset.defaultCharset().name();
+ }
+}
diff --git a/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/text/Convert.java b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/text/Convert.java
new file mode 100644
index 0000000..fb57cb9
--- /dev/null
+++ b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/text/Convert.java
@@ -0,0 +1,903 @@
+package com.muyu.common.core.text;
+
+import com.muyu.common.core.utils.StringUtils;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.math.RoundingMode;
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.text.NumberFormat;
+import java.util.Set;
+
+/**
+ * 类型转换器
+ *
+ * @author muyu
+ */
+public class Convert {
+ /**
+ * 转换为字符串
+ * 如果给定的值为null,或者转换失败,返回默认值
+ * 转换失败不会报错
+ *
+ * @param value 被转换的值
+ * @param defaultValue 转换错误时的默认值
+ *
+ * @return 结果
+ */
+ public static String toStr (Object value, String defaultValue) {
+ if (null == value) {
+ return defaultValue;
+ }
+ if (value instanceof String) {
+ return (String) value;
+ }
+ return value.toString();
+ }
+
+ /**
+ * 转换为字符串
+ * 如果给定的值为null
,或者转换失败,返回默认值null
+ * 转换失败不会报错
+ *
+ * @param value 被转换的值
+ *
+ * @return 结果
+ */
+ public static String toStr (Object value) {
+ return toStr(value, null);
+ }
+
+ /**
+ * 转换为字符
+ * 如果给定的值为null,或者转换失败,返回默认值
+ * 转换失败不会报错
+ *
+ * @param value 被转换的值
+ * @param defaultValue 转换错误时的默认值
+ *
+ * @return 结果
+ */
+ public static Character toChar (Object value, Character defaultValue) {
+ if (null == value) {
+ return defaultValue;
+ }
+ if (value instanceof Character) {
+ return (Character) value;
+ }
+
+ final String valueStr = toStr(value, null);
+ return StringUtils.isEmpty(valueStr) ? defaultValue : valueStr.charAt(0);
+ }
+
+ /**
+ * 转换为字符
+ * 如果给定的值为null
,或者转换失败,返回默认值null
+ * 转换失败不会报错
+ *
+ * @param value 被转换的值
+ *
+ * @return 结果
+ */
+ public static Character toChar (Object value) {
+ return toChar(value, null);
+ }
+
+ /**
+ * 转换为byte
+ * 如果给定的值为null
,或者转换失败,返回默认值
+ * 转换失败不会报错
+ *
+ * @param value 被转换的值
+ * @param defaultValue 转换错误时的默认值
+ *
+ * @return 结果
+ */
+ public static Byte toByte (Object value, Byte defaultValue) {
+ if (value == null) {
+ return defaultValue;
+ }
+ if (value instanceof Byte) {
+ return (Byte) value;
+ }
+ if (value instanceof Number) {
+ return ((Number) value).byteValue();
+ }
+ final String valueStr = toStr(value, null);
+ if (StringUtils.isEmpty(valueStr)) {
+ return defaultValue;
+ }
+ try {
+ return Byte.parseByte(valueStr);
+ } catch (Exception e) {
+ return defaultValue;
+ }
+ }
+
+ /**
+ * 转换为byte
+ * 如果给定的值为null
,或者转换失败,返回默认值null
+ * 转换失败不会报错
+ *
+ * @param value 被转换的值
+ *
+ * @return 结果
+ */
+ public static Byte toByte (Object value) {
+ return toByte(value, null);
+ }
+
+ /**
+ * 转换为Short
+ * 如果给定的值为null
,或者转换失败,返回默认值
+ * 转换失败不会报错
+ *
+ * @param value 被转换的值
+ * @param defaultValue 转换错误时的默认值
+ *
+ * @return 结果
+ */
+ public static Short toShort (Object value, Short defaultValue) {
+ if (value == null) {
+ return defaultValue;
+ }
+ if (value instanceof Short) {
+ return (Short) value;
+ }
+ if (value instanceof Number) {
+ return ((Number) value).shortValue();
+ }
+ final String valueStr = toStr(value, null);
+ if (StringUtils.isEmpty(valueStr)) {
+ return defaultValue;
+ }
+ try {
+ return Short.parseShort(valueStr.trim());
+ } catch (Exception e) {
+ return defaultValue;
+ }
+ }
+
+ /**
+ * 转换为Short
+ * 如果给定的值为null
,或者转换失败,返回默认值null
+ * 转换失败不会报错
+ *
+ * @param value 被转换的值
+ *
+ * @return 结果
+ */
+ public static Short toShort (Object value) {
+ return toShort(value, null);
+ }
+
+ /**
+ * 转换为Number
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错
+ *
+ * @param value 被转换的值
+ * @param defaultValue 转换错误时的默认值
+ *
+ * @return 结果
+ */
+ public static Number toNumber (Object value, Number defaultValue) {
+ if (value == null) {
+ return defaultValue;
+ }
+ if (value instanceof Number) {
+ return (Number) value;
+ }
+ final String valueStr = toStr(value, null);
+ if (StringUtils.isEmpty(valueStr)) {
+ return defaultValue;
+ }
+ try {
+ return NumberFormat.getInstance().parse(valueStr);
+ } catch (Exception e) {
+ return defaultValue;
+ }
+ }
+
+ /**
+ * 转换为Number
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错
+ *
+ * @param value 被转换的值
+ *
+ * @return 结果
+ */
+ public static Number toNumber (Object value) {
+ return toNumber(value, null);
+ }
+
+ /**
+ * 转换为int
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错
+ *
+ * @param value 被转换的值
+ * @param defaultValue 转换错误时的默认值
+ *
+ * @return 结果
+ */
+ public static Integer toInt (Object value, Integer defaultValue) {
+ if (value == null) {
+ return defaultValue;
+ }
+ if (value instanceof Integer) {
+ return (Integer) value;
+ }
+ if (value instanceof Number) {
+ return ((Number) value).intValue();
+ }
+ final String valueStr = toStr(value, null);
+ if (StringUtils.isEmpty(valueStr)) {
+ return defaultValue;
+ }
+ try {
+ return Integer.parseInt(valueStr.trim());
+ } catch (Exception e) {
+ return defaultValue;
+ }
+ }
+
+ /**
+ * 转换为int
+ * 如果给定的值为null
,或者转换失败,返回默认值null
+ * 转换失败不会报错
+ *
+ * @param value 被转换的值
+ *
+ * @return 结果
+ */
+ public static Integer toInt (Object value) {
+ return toInt(value, null);
+ }
+
+ /**
+ * 转换为Integer数组
+ *
+ * @param str 被转换的值
+ *
+ * @return 结果
+ */
+ public static Integer[] toIntArray (String str) {
+ return toIntArray(",", str);
+ }
+
+ /**
+ * 转换为Long数组
+ *
+ * @param str 被转换的值
+ *
+ * @return 结果
+ */
+ public static Long[] toLongArray (String str) {
+ return toLongArray(",", str);
+ }
+
+ /**
+ * 转换为Integer数组
+ *
+ * @param split 分隔符
+ * @param str 被转换的值
+ *
+ * @return 结果
+ */
+ public static Integer[] toIntArray (String split, String str) {
+ if (StringUtils.isEmpty(str)) {
+ return new Integer[]{};
+ }
+ String[] arr = str.split(split);
+ final Integer[] ints = new Integer[arr.length];
+ for (int i = 0 ; i < arr.length ; i++) {
+ final Integer v = toInt(arr[i], 0);
+ ints[i] = v;
+ }
+ return ints;
+ }
+
+ /**
+ * 转换为Long数组
+ *
+ * @param split 分隔符
+ * @param str 被转换的值
+ *
+ * @return 结果
+ */
+ public static Long[] toLongArray (String split, String str) {
+ if (StringUtils.isEmpty(str)) {
+ return new Long[]{};
+ }
+ String[] arr = str.split(split);
+ final Long[] longs = new Long[arr.length];
+ for (int i = 0 ; i < arr.length ; i++) {
+ final Long v = toLong(arr[i], null);
+ longs[i] = v;
+ }
+ return longs;
+ }
+
+ /**
+ * 转换为String数组
+ *
+ * @param str 被转换的值
+ *
+ * @return 结果
+ */
+ public static String[] toStrArray (String str) {
+ return toStrArray(",", str);
+ }
+
+ /**
+ * 转换为String数组
+ *
+ * @param split 分隔符
+ * @param str 被转换的值
+ *
+ * @return 结果
+ */
+ public static String[] toStrArray (String split, String str) {
+ return str.split(split);
+ }
+
+ /**
+ * 转换为long
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错
+ *
+ * @param value 被转换的值
+ * @param defaultValue 转换错误时的默认值
+ *
+ * @return 结果
+ */
+ public static Long toLong (Object value, Long defaultValue) {
+ if (value == null) {
+ return defaultValue;
+ }
+ if (value instanceof Long) {
+ return (Long) value;
+ }
+ if (value instanceof Number) {
+ return ((Number) value).longValue();
+ }
+ final String valueStr = toStr(value, null);
+ if (StringUtils.isEmpty(valueStr)) {
+ return defaultValue;
+ }
+ try {
+ // 支持科学计数法
+ return new BigDecimal(valueStr.trim()).longValue();
+ } catch (Exception e) {
+ return defaultValue;
+ }
+ }
+
+ /**
+ * 转换为long
+ * 如果给定的值为null
,或者转换失败,返回默认值null
+ * 转换失败不会报错
+ *
+ * @param value 被转换的值
+ *
+ * @return 结果
+ */
+ public static Long toLong (Object value) {
+ return toLong(value, null);
+ }
+
+ /**
+ * 转换为double
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错
+ *
+ * @param value 被转换的值
+ * @param defaultValue 转换错误时的默认值
+ *
+ * @return 结果
+ */
+ public static Double toDouble (Object value, Double defaultValue) {
+ if (value == null) {
+ return defaultValue;
+ }
+ if (value instanceof Double) {
+ return (Double) value;
+ }
+ if (value instanceof Number) {
+ return ((Number) value).doubleValue();
+ }
+ final String valueStr = toStr(value, null);
+ if (StringUtils.isEmpty(valueStr)) {
+ return defaultValue;
+ }
+ try {
+ // 支持科学计数法
+ return new BigDecimal(valueStr.trim()).doubleValue();
+ } catch (Exception e) {
+ return defaultValue;
+ }
+ }
+
+ /**
+ * 转换为double
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错
+ *
+ * @param value 被转换的值
+ *
+ * @return 结果
+ */
+ public static Double toDouble (Object value) {
+ return toDouble(value, null);
+ }
+
+ /**
+ * 转换为Float
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错
+ *
+ * @param value 被转换的值
+ * @param defaultValue 转换错误时的默认值
+ *
+ * @return 结果
+ */
+ public static Float toFloat (Object value, Float defaultValue) {
+ if (value == null) {
+ return defaultValue;
+ }
+ if (value instanceof Float) {
+ return (Float) value;
+ }
+ if (value instanceof Number) {
+ return ((Number) value).floatValue();
+ }
+ final String valueStr = toStr(value, null);
+ if (StringUtils.isEmpty(valueStr)) {
+ return defaultValue;
+ }
+ try {
+ return Float.parseFloat(valueStr.trim());
+ } catch (Exception e) {
+ return defaultValue;
+ }
+ }
+
+ /**
+ * 转换为Float
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错
+ *
+ * @param value 被转换的值
+ *
+ * @return 结果
+ */
+ public static Float toFloat (Object value) {
+ return toFloat(value, null);
+ }
+
+ /**
+ * 转换为boolean
+ * String支持的值为:true、false、yes、ok、no,1,0 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错
+ *
+ * @param value 被转换的值
+ * @param defaultValue 转换错误时的默认值
+ *
+ * @return 结果
+ */
+ public static Boolean toBool (Object value, Boolean defaultValue) {
+ if (value == null) {
+ return defaultValue;
+ }
+ if (value instanceof Boolean) {
+ return (Boolean) value;
+ }
+ String valueStr = toStr(value, null);
+ if (StringUtils.isEmpty(valueStr)) {
+ return defaultValue;
+ }
+ valueStr = valueStr.trim().toLowerCase();
+ switch (valueStr) {
+ case "true":
+ case "yes":
+ case "ok":
+ case "1":
+ return true;
+ case "false":
+ case "no":
+ case "0":
+ return false;
+ default:
+ return defaultValue;
+ }
+ }
+
+ /**
+ * 转换为boolean
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错
+ *
+ * @param value 被转换的值
+ *
+ * @return 结果
+ */
+ public static Boolean toBool (Object value) {
+ return toBool(value, null);
+ }
+
+ /**
+ * 转换为Enum对象
+ * 如果给定的值为空,或者转换失败,返回默认值
+ *
+ * @param clazz Enum的Class
+ * @param value 值
+ * @param defaultValue 默认值
+ *
+ * @return Enum
+ */
+ public static > E toEnum (Class clazz, Object value, E defaultValue) {
+ if (value == null) {
+ return defaultValue;
+ }
+ if (clazz.isAssignableFrom(value.getClass())) {
+ @SuppressWarnings("unchecked")
+ E myE = (E) value;
+ return myE;
+ }
+ final String valueStr = toStr(value, null);
+ if (StringUtils.isEmpty(valueStr)) {
+ return defaultValue;
+ }
+ try {
+ return Enum.valueOf(clazz, valueStr);
+ } catch (Exception e) {
+ return defaultValue;
+ }
+ }
+
+ /**
+ * 转换为Enum对象
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ *
+ * @param clazz Enum的Class
+ * @param value 值
+ *
+ * @return Enum
+ */
+ public static > E toEnum (Class clazz, Object value) {
+ return toEnum(clazz, value, null);
+ }
+
+ /**
+ * 转换为BigInteger
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错
+ *
+ * @param value 被转换的值
+ * @param defaultValue 转换错误时的默认值
+ *
+ * @return 结果
+ */
+ public static BigInteger toBigInteger (Object value, BigInteger defaultValue) {
+ if (value == null) {
+ return defaultValue;
+ }
+ if (value instanceof BigInteger) {
+ return (BigInteger) value;
+ }
+ if (value instanceof Long) {
+ return BigInteger.valueOf((Long) value);
+ }
+ final String valueStr = toStr(value, null);
+ if (StringUtils.isEmpty(valueStr)) {
+ return defaultValue;
+ }
+ try {
+ return new BigInteger(valueStr);
+ } catch (Exception e) {
+ return defaultValue;
+ }
+ }
+
+ /**
+ * 转换为BigInteger
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错
+ *
+ * @param value 被转换的值
+ *
+ * @return 结果
+ */
+ public static BigInteger toBigInteger (Object value) {
+ return toBigInteger(value, null);
+ }
+
+ /**
+ * 转换为BigDecimal
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错
+ *
+ * @param value 被转换的值
+ * @param defaultValue 转换错误时的默认值
+ *
+ * @return 结果
+ */
+ public static BigDecimal toBigDecimal (Object value, BigDecimal defaultValue) {
+ if (value == null) {
+ return defaultValue;
+ }
+ if (value instanceof BigDecimal) {
+ return (BigDecimal) value;
+ }
+ if (value instanceof Long) {
+ return new BigDecimal((Long) value);
+ }
+ if (value instanceof Double) {
+ return BigDecimal.valueOf((Double) value);
+ }
+ if (value instanceof Integer) {
+ return new BigDecimal((Integer) value);
+ }
+ final String valueStr = toStr(value, null);
+ if (StringUtils.isEmpty(valueStr)) {
+ return defaultValue;
+ }
+ try {
+ return new BigDecimal(valueStr);
+ } catch (Exception e) {
+ return defaultValue;
+ }
+ }
+
+ /**
+ * 转换为BigDecimal
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错
+ *
+ * @param value 被转换的值
+ *
+ * @return 结果
+ */
+ public static BigDecimal toBigDecimal (Object value) {
+ return toBigDecimal(value, null);
+ }
+
+ /**
+ * 将对象转为字符串
+ * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法
+ *
+ * @param obj 对象
+ *
+ * @return 字符串
+ */
+ public static String utf8Str (Object obj) {
+ return str(obj, CharsetKit.CHARSET_UTF_8);
+ }
+
+ /**
+ * 将对象转为字符串
+ * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法
+ *
+ * @param obj 对象
+ * @param charsetName 字符集
+ *
+ * @return 字符串
+ */
+ public static String str (Object obj, String charsetName) {
+ return str(obj, Charset.forName(charsetName));
+ }
+
+ /**
+ * 将对象转为字符串
+ * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法
+ *
+ * @param obj 对象
+ * @param charset 字符集
+ *
+ * @return 字符串
+ */
+ public static String str (Object obj, Charset charset) {
+ if (null == obj) {
+ return null;
+ }
+
+ if (obj instanceof String) {
+ return (String) obj;
+ } else if (obj instanceof byte[] || obj instanceof Byte[]) {
+ if (obj instanceof byte[]) {
+ return str((byte[]) obj, charset);
+ } else {
+ Byte[] bytes = (Byte[]) obj;
+ int length = bytes.length;
+ byte[] dest = new byte[length];
+ for (int i = 0 ; i < length ; i++) {
+ dest[i] = bytes[i];
+ }
+ return str(dest, charset);
+ }
+ } else if (obj instanceof ByteBuffer) {
+ return str((ByteBuffer) obj, charset);
+ }
+ return obj.toString();
+ }
+
+ /**
+ * 将byte数组转为字符串
+ *
+ * @param bytes byte数组
+ * @param charset 字符集
+ *
+ * @return 字符串
+ */
+ public static String str (byte[] bytes, String charset) {
+ return str(bytes, StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset));
+ }
+
+ /**
+ * 解码字节码
+ *
+ * @param data 字符串
+ * @param charset 字符集,如果此字段为空,则解码的结果取决于平台
+ *
+ * @return 解码后的字符串
+ */
+ public static String str (byte[] data, Charset charset) {
+ if (data == null) {
+ return null;
+ }
+
+ if (null == charset) {
+ return new String(data);
+ }
+ return new String(data, charset);
+ }
+
+ /**
+ * 将编码的byteBuffer数据转换为字符串
+ *
+ * @param data 数据
+ * @param charset 字符集,如果为空使用当前系统字符集
+ *
+ * @return 字符串
+ */
+ public static String str (ByteBuffer data, String charset) {
+ if (data == null) {
+ return null;
+ }
+
+ return str(data, Charset.forName(charset));
+ }
+
+ /**
+ * 将编码的byteBuffer数据转换为字符串
+ *
+ * @param data 数据
+ * @param charset 字符集,如果为空使用当前系统字符集
+ *
+ * @return 字符串
+ */
+ public static String str (ByteBuffer data, Charset charset) {
+ if (null == charset) {
+ charset = Charset.defaultCharset();
+ }
+ return charset.decode(data).toString();
+ }
+
+ // ----------------------------------------------------------------------- 全角半角转换
+
+ /**
+ * 半角转全角
+ *
+ * @param input String.
+ *
+ * @return 全角字符串.
+ */
+ public static String toSBC (String input) {
+ return toSBC(input, null);
+ }
+
+ /**
+ * 半角转全角
+ *
+ * @param input String
+ * @param notConvertSet 不替换的字符集合
+ *
+ * @return 全角字符串.
+ */
+ public static String toSBC (String input, Set notConvertSet) {
+ char[] c = input.toCharArray();
+ for (int i = 0 ; i < c.length ; i++) {
+ if (null != notConvertSet && notConvertSet.contains(c[i])) {
+ // 跳过不替换的字符
+ continue;
+ }
+
+ if (c[i] == ' ') {
+ c[i] = '\u3000';
+ } else if (c[i] < '\177') {
+ c[i] = (char) (c[i] + 65248);
+
+ }
+ }
+ return new String(c);
+ }
+
+ /**
+ * 全角转半角
+ *
+ * @param input String.
+ *
+ * @return 半角字符串
+ */
+ public static String toDBC (String input) {
+ return toDBC(input, null);
+ }
+
+ /**
+ * 替换全角为半角
+ *
+ * @param text 文本
+ * @param notConvertSet 不替换的字符集合
+ *
+ * @return 替换后的字符
+ */
+ public static String toDBC (String text, Set notConvertSet) {
+ char[] c = text.toCharArray();
+ for (int i = 0 ; i < c.length ; i++) {
+ if (null != notConvertSet && notConvertSet.contains(c[i])) {
+ // 跳过不替换的字符
+ continue;
+ }
+
+ if (c[i] == '\u3000') {
+ c[i] = ' ';
+ } else if (c[i] > '\uFF00' && c[i] < '\uFF5F') {
+ c[i] = (char) (c[i] - 65248);
+ }
+ }
+ return new String(c);
+ }
+
+ /**
+ * 数字金额大写转换 先写个完整的然后将如零拾替换成零
+ *
+ * @param n 数字
+ *
+ * @return 中文大写数字
+ */
+ public static String digitUppercase (double n) {
+ String[] fraction = {"角", "分"};
+ String[] digit = {"零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"};
+ String[][] unit = {{"元", "万", "亿"}, {"", "拾", "佰", "仟"}};
+
+ String head = n < 0 ? "负" : "";
+ n = Math.abs(n);
+
+ String s = "";
+ for (int i = 0 ; i < fraction.length ; i++) {
+ // 优化double计算精度丢失问题
+ BigDecimal nNum = new BigDecimal(n);
+ BigDecimal decimal = new BigDecimal(10);
+ BigDecimal scale = nNum.multiply(decimal).setScale(2, RoundingMode.HALF_EVEN);
+ double d = scale.doubleValue();
+ s += (digit[(int) (Math.floor(d * Math.pow(10, i)) % 10)] + fraction[i]).replaceAll("(零.)+", "");
+ }
+ if (s.length() < 1) {
+ s = "整";
+ }
+ int integerPart = (int) Math.floor(n);
+
+ for (int i = 0 ; i < unit[0].length && integerPart > 0 ; i++) {
+ String p = "";
+ for (int j = 0 ; j < unit[1].length && n > 0 ; j++) {
+ p = digit[integerPart % 10] + unit[1][j] + p;
+ integerPart = integerPart / 10;
+ }
+ s = p.replaceAll("(零.)*零$", "").replaceAll("^$", "零") + unit[0][i] + s;
+ }
+ return head + s.replaceAll("(零.)*零元", "元").replaceFirst("(零.)+", "").replaceAll("(零.)+", "零").replaceAll("^整$", "零元整");
+ }
+}
diff --git a/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/text/StrFormatter.java b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/text/StrFormatter.java
new file mode 100644
index 0000000..0c07cf5
--- /dev/null
+++ b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/text/StrFormatter.java
@@ -0,0 +1,77 @@
+package com.muyu.common.core.text;
+
+import com.muyu.common.core.utils.StringUtils;
+
+/**
+ * 字符串格式化
+ *
+ * @author muyu
+ */
+public class StrFormatter {
+ public static final String EMPTY_JSON = "{}";
+ public static final char C_BACKSLASH = '\\';
+ public static final char C_DELIM_START = '{';
+ public static final char C_DELIM_END = '}';
+
+ /**
+ * 格式化字符串
+ * 此方法只是简单将占位符 {} 按照顺序替换为参数
+ * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可
+ * 例:
+ * 通常使用:format("this is {} for {}", "a", "b") -> this is a for b
+ * 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a
+ * 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b
+ *
+ * @param strPattern 字符串模板
+ * @param argArray 参数列表
+ *
+ * @return 结果
+ */
+ public static String format (final String strPattern, final Object... argArray) {
+ if (StringUtils.isEmpty(strPattern) || StringUtils.isEmpty(argArray)) {
+ return strPattern;
+ }
+ final int strPatternLength = strPattern.length();
+
+ // 初始化定义好的长度以获得更好的性能
+ StringBuilder sbuf = new StringBuilder(strPatternLength + 50);
+
+ int handledPosition = 0;
+ int delimIndex;// 占位符所在位置
+ for (int argIndex = 0 ; argIndex < argArray.length ; argIndex++) {
+ delimIndex = strPattern.indexOf(EMPTY_JSON, handledPosition);
+ if (delimIndex == -1) {
+ if (handledPosition == 0) {
+ return strPattern;
+ } else { // 字符串模板剩余部分不再包含占位符,加入剩余部分后返回结果
+ sbuf.append(strPattern, handledPosition, strPatternLength);
+ return sbuf.toString();
+ }
+ } else {
+ if (delimIndex > 0 && strPattern.charAt(delimIndex - 1) == C_BACKSLASH) {
+ if (delimIndex > 1 && strPattern.charAt(delimIndex - 2) == C_BACKSLASH) {
+ // 转义符之前还有一个转义符,占位符依旧有效
+ sbuf.append(strPattern, handledPosition, delimIndex - 1);
+ sbuf.append(Convert.utf8Str(argArray[argIndex]));
+ handledPosition = delimIndex + 2;
+ } else {
+ // 占位符被转义
+ argIndex--;
+ sbuf.append(strPattern, handledPosition, delimIndex - 1);
+ sbuf.append(C_DELIM_START);
+ handledPosition = delimIndex + 1;
+ }
+ } else {
+ // 正常占位符
+ sbuf.append(strPattern, handledPosition, delimIndex);
+ sbuf.append(Convert.utf8Str(argArray[argIndex]));
+ handledPosition = delimIndex + 2;
+ }
+ }
+ }
+ // 加入最后一个占位符后所有的字符
+ sbuf.append(strPattern, handledPosition, strPattern.length());
+
+ return sbuf.toString();
+ }
+}
diff --git a/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/utils/DateUtils.java b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/utils/DateUtils.java
new file mode 100644
index 0000000..fb15c59
--- /dev/null
+++ b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/utils/DateUtils.java
@@ -0,0 +1,158 @@
+package com.muyu.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.*;
+import java.util.Date;
+
+/**
+ * 时间工具类
+ *
+ * @author muyu
+ */
+public class DateUtils extends org.apache.commons.lang3.time.DateUtils {
+ public static String YYYY = "yyyy";
+
+ public static String YYYY_MM = "yyyy-MM";
+
+ public static String YYYY_MM_DD = "yyyy-MM-dd";
+
+ public static String YYYYMMDDHHMMSS = "yyyyMMddHHmmss";
+
+ public static String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";
+
+ private static 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 final String getTime () {
+ return dateTimeNow(YYYY_MM_DD_HH_MM_SS);
+ }
+
+ public static final String dateTimeNow () {
+ return dateTimeNow(YYYYMMDDHHMMSS);
+ }
+
+ public static final String dateTimeNow (final String format) {
+ return parseDateToStr(format, new Date());
+ }
+
+ public static final String dateTime (final Date date) {
+ return parseDateToStr(YYYY_MM_DD, date);
+ }
+
+ public static final String parseDateToStr (final String format, final Date date) {
+ return new SimpleDateFormat(format).format(date);
+ }
+
+ public static final 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 final String datePath () {
+ Date now = new Date();
+ return DateFormatUtils.format(now, "yyyy/MM/dd");
+ }
+
+ /**
+ * 日期路径 即年/月/日 如20180808
+ */
+ public static final 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 endDate 最后时间
+ * @param startTime 开始时间
+ *
+ * @return 时间差(天/小时/分钟)
+ */
+ public static String timeDistance (Date endDate, Date startTime) {
+ long nd = 1000 * 24 * 60 * 60;
+ long nh = 1000 * 60 * 60;
+ long nm = 1000 * 60;
+ // long ns = 1000;
+ // 获得两个时间的毫秒时间差异
+ long diff = endDate.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());
+ }
+}
diff --git a/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/utils/ExceptionUtil.java b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/utils/ExceptionUtil.java
new file mode 100644
index 0000000..e6abdf9
--- /dev/null
+++ b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/utils/ExceptionUtil.java
@@ -0,0 +1,35 @@
+package com.muyu.common.core.utils;
+
+import org.apache.commons.lang3.exception.ExceptionUtils;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+/**
+ * 错误信息处理类。
+ *
+ * @author muyu
+ */
+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 "";
+ }
+ String msg = root.getMessage();
+ if (msg == null) {
+ return "null";
+ }
+ return StringUtils.defaultString(msg);
+ }
+}
diff --git a/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/utils/JwtUtils.java b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/utils/JwtUtils.java
new file mode 100644
index 0000000..a12b839
--- /dev/null
+++ b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/utils/JwtUtils.java
@@ -0,0 +1,123 @@
+package com.muyu.common.core.utils;
+
+import com.muyu.common.core.constant.SecurityConstants;
+import com.muyu.common.core.constant.TokenConstants;
+import com.muyu.common.core.text.Convert;
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.SignatureAlgorithm;
+
+import java.util.Map;
+
+/**
+ * Jwt工具类
+ *
+ * @author muyu
+ */
+public class JwtUtils {
+ public static String secret = TokenConstants.SECRET;
+
+ /**
+ * 从数据声明生成令牌
+ *
+ * @param claims 数据声明
+ *
+ * @return 令牌
+ */
+ public static String createToken (Map claims) {
+ String token = Jwts.builder().setClaims(claims).signWith(SignatureAlgorithm.HS512, secret).compact();
+ return token;
+ }
+
+ /**
+ * 从令牌中获取数据声明
+ *
+ * @param token 令牌
+ *
+ * @return 数据声明
+ */
+ public static Claims parseToken (String token) {
+ return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
+ }
+
+ /**
+ * 根据令牌获取用户标识
+ *
+ * @param token 令牌
+ *
+ * @return 用户ID
+ */
+ public static String getUserKey (String token) {
+ Claims claims = parseToken(token);
+ return getValue(claims, SecurityConstants.USER_KEY);
+ }
+
+ /**
+ * 根据令牌获取用户标识
+ *
+ * @param claims 身份信息
+ *
+ * @return 用户ID
+ */
+ public static String getUserKey (Claims claims) {
+ return getValue(claims, SecurityConstants.USER_KEY);
+ }
+
+ /**
+ * 根据令牌获取用户ID
+ *
+ * @param token 令牌
+ *
+ * @return 用户ID
+ */
+ public static String getUserId (String token) {
+ Claims claims = parseToken(token);
+ return getValue(claims, SecurityConstants.DETAILS_USER_ID);
+ }
+
+ /**
+ * 根据身份信息获取用户ID
+ *
+ * @param claims 身份信息
+ *
+ * @return 用户ID
+ */
+ public static String getUserId (Claims claims) {
+ return getValue(claims, SecurityConstants.DETAILS_USER_ID);
+ }
+
+ /**
+ * 根据令牌获取用户名
+ *
+ * @param token 令牌
+ *
+ * @return 用户名
+ */
+ public static String getUserName (String token) {
+ Claims claims = parseToken(token);
+ return getValue(claims, SecurityConstants.DETAILS_USERNAME);
+ }
+
+ /**
+ * 根据身份信息获取用户名
+ *
+ * @param claims 身份信息
+ *
+ * @return 用户名
+ */
+ public static String getUserName (Claims claims) {
+ return getValue(claims, SecurityConstants.DETAILS_USERNAME);
+ }
+
+ /**
+ * 根据身份信息获取键值
+ *
+ * @param claims 身份信息
+ * @param key 键
+ *
+ * @return 值
+ */
+ public static String getValue (Claims claims, String key) {
+ return Convert.toStr(claims.get(key), "");
+ }
+}
diff --git a/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/utils/PageUtils.java b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/utils/PageUtils.java
new file mode 100644
index 0000000..d4b0554
--- /dev/null
+++ b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/utils/PageUtils.java
@@ -0,0 +1,32 @@
+package com.muyu.common.core.utils;
+
+import com.github.pagehelper.PageHelper;
+import com.muyu.common.core.utils.sql.SqlUtil;
+import com.muyu.common.core.web.page.PageDomain;
+import com.muyu.common.core.web.page.TableSupport;
+
+/**
+ * 分页工具类
+ *
+ * @author muyu
+ */
+public class PageUtils extends PageHelper {
+ /**
+ * 设置请求分页数据
+ */
+ public static void startPage () {
+ PageDomain pageDomain = TableSupport.buildPageRequest();
+ Integer pageNum = pageDomain.getPageNum();
+ Integer pageSize = pageDomain.getPageSize();
+ String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy());
+ Boolean reasonable = pageDomain.getReasonable();
+ PageHelper.startPage(pageNum, pageSize, orderBy).setReasonable(reasonable);
+ }
+
+ /**
+ * 清理分页的线程变量
+ */
+ public static void clearPage () {
+ PageHelper.clearPage();
+ }
+}
diff --git a/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/utils/ServletUtils.java b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/utils/ServletUtils.java
new file mode 100644
index 0000000..c9a8998
--- /dev/null
+++ b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/utils/ServletUtils.java
@@ -0,0 +1,294 @@
+package com.muyu.common.core.utils;
+
+import com.alibaba.fastjson2.JSON;
+import com.muyu.common.core.constant.Constants;
+import com.muyu.common.core.domain.Result;
+import com.muyu.common.core.text.Convert;
+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.ServerHttpResponse;
+import org.springframework.util.LinkedCaseInsensitiveMap;
+import org.springframework.web.context.request.RequestAttributes;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+import reactor.core.publisher.Mono;
+
+import javax.servlet.ServletRequest;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.net.URLEncoder;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 客户端工具类
+ *
+ * @author muyu
+ */
+public class ServletUtils {
+ /**
+ * 获取String参数
+ */
+ public static String getParameter (String name) {
+ return getRequest().getParameter(name);
+ }
+
+ /**
+ * 获取String参数
+ */
+ public static String getParameter (String name, String defaultValue) {
+ return Convert.toStr(getRequest().getParameter(name), defaultValue);
+ }
+
+ /**
+ * 获取Integer参数
+ */
+ public static Integer getParameterToInt (String name) {
+ return Convert.toInt(getRequest().getParameter(name));
+ }
+
+ /**
+ * 获取Integer参数
+ */
+ public static Integer getParameterToInt (String name, Integer defaultValue) {
+ return Convert.toInt(getRequest().getParameter(name), defaultValue);
+ }
+
+ /**
+ * 获取Boolean参数
+ */
+ public static Boolean getParameterToBool (String name) {
+ return Convert.toBool(getRequest().getParameter(name));
+ }
+
+ /**
+ * 获取Boolean参数
+ */
+ public static Boolean getParameterToBool (String name, Boolean defaultValue) {
+ return Convert.toBool(getRequest().getParameter(name), defaultValue);
+ }
+
+ /**
+ * 获得所有请求参数
+ *
+ * @param request 请求对象{@link ServletRequest}
+ *
+ * @return Map
+ */
+ public static Map getParams (ServletRequest request) {
+ final Map map = request.getParameterMap();
+ return Collections.unmodifiableMap(map);
+ }
+
+ /**
+ * 获得所有请求参数
+ *
+ * @param request 请求对象{@link ServletRequest}
+ *
+ * @return Map
+ */
+ public static Map getParamMap (ServletRequest request) {
+ Map params = new HashMap<>();
+ for (Map.Entry entry : getParams(request).entrySet()) {
+ params.put(entry.getKey(), StringUtils.join(entry.getValue(), ","));
+ }
+ 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 (StringUtils.isEmpty(value)) {
+ return StringUtils.EMPTY;
+ }
+ return urlDecode(value);
+ }
+
+ public static Map getHeaders (HttpServletRequest request) {
+ Map map = new LinkedCaseInsensitiveMap<>();
+ Enumeration 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(200);
+ response.setContentType("application/json");
+ response.setCharacterEncoding("utf-8");
+ 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("application/json")) {
+ return true;
+ }
+
+ String xRequestedWith = request.getHeader("X-Requested-With");
+ if (xRequestedWith != null && xRequestedWith.contains("XMLHttpRequest")) {
+ return true;
+ }
+
+ String uri = request.getRequestURI();
+ if (StringUtils.inStringIgnoreCase(uri, ".json", ".xml")) {
+ return true;
+ }
+
+ String ajax = request.getParameter("__ajax");
+ return StringUtils.inStringIgnoreCase(ajax, "json", "xml");
+ }
+
+ /**
+ * 内容编码
+ *
+ * @param str 内容
+ *
+ * @return 编码后的内容
+ */
+ public static String urlEncode (String str) {
+ try {
+ return URLEncoder.encode(str, Constants.UTF8);
+ } catch (UnsupportedEncodingException e) {
+ return StringUtils.EMPTY;
+ }
+ }
+
+ /**
+ * 内容解码
+ *
+ * @param str 内容
+ *
+ * @return 解码后的内容
+ */
+ public static String urlDecode (String str) {
+ try {
+ return URLDecoder.decode(str, Constants.UTF8);
+ } catch (UnsupportedEncodingException e) {
+ return StringUtils.EMPTY;
+ }
+ }
+
+ /**
+ * 设置webflux模型响应
+ *
+ * @param response ServerHttpResponse
+ * @param value 响应内容
+ *
+ * @return Mono
+ */
+ public static Mono webFluxResponseWriter (ServerHttpResponse response, Object value) {
+ return webFluxResponseWriter(response, HttpStatus.OK, value, Result.FAIL);
+ }
+
+ /**
+ * 设置webflux模型响应
+ *
+ * @param response ServerHttpResponse
+ * @param code 响应状态码
+ * @param value 响应内容
+ *
+ * @return Mono
+ */
+ public static Mono 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 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 webFluxResponseWriter (ServerHttpResponse response, String contentType, HttpStatus status, Object value, int code) {
+ response.setStatusCode(status);
+ response.getHeaders().add(HttpHeaders.CONTENT_TYPE, contentType);
+ Result> result = Result.error(code, value.toString());
+ DataBuffer dataBuffer = response.bufferFactory().wrap(JSON.toJSONString(result).getBytes());
+ return response.writeWith(Mono.just(dataBuffer));
+ }
+}
diff --git a/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/utils/SpringUtils.java b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/utils/SpringUtils.java
new file mode 100644
index 0000000..c37a65c
--- /dev/null
+++ b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/utils/SpringUtils.java
@@ -0,0 +1,114 @@
+package com.muyu.common.core.utils;
+
+import org.springframework.aop.framework.AopContext;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.NoSuchBeanDefinitionException;
+import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.springframework.stereotype.Component;
+
+/**
+ * spring工具类 方便在非spring管理环境中获取bean
+ *
+ * @author muyu
+ */
+@Component
+public final class SpringUtils implements BeanFactoryPostProcessor {
+ /**
+ * Spring应用上下文环境
+ */
+ private static ConfigurableListableBeanFactory beanFactory;
+
+ /**
+ * 获取对象
+ *
+ * @param name
+ *
+ * @return Object 一个以所给名字注册的bean的实例
+ *
+ * @throws org.springframework.beans.BeansException
+ */
+ @SuppressWarnings("unchecked")
+ public static T getBean (String name) throws BeansException {
+ return (T) beanFactory.getBean(name);
+ }
+
+ /**
+ * 获取类型为requiredType的对象
+ *
+ * @param clz
+ *
+ * @return
+ *
+ * @throws org.springframework.beans.BeansException
+ */
+ public static T getBean (Class clz) throws BeansException {
+ T result = (T) beanFactory.getBean(clz);
+ return result;
+ }
+
+ /**
+ * 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true
+ *
+ * @param name
+ *
+ * @return boolean
+ */
+ public static boolean containsBean (String name) {
+ return beanFactory.containsBean(name);
+ }
+
+ /**
+ * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException)
+ *
+ * @param name
+ *
+ * @return boolean
+ *
+ * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
+ */
+ public static boolean isSingleton (String name) throws NoSuchBeanDefinitionException {
+ return beanFactory.isSingleton(name);
+ }
+
+ /**
+ * @param name
+ *
+ * @return Class 注册对象的类型
+ *
+ * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
+ */
+ public static Class> getType (String name) throws NoSuchBeanDefinitionException {
+ return beanFactory.getType(name);
+ }
+
+ /**
+ * 如果给定的bean名字在bean定义中有别名,则返回这些别名
+ *
+ * @param name
+ *
+ * @return
+ *
+ * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
+ */
+ public static String[] getAliases (String name) throws NoSuchBeanDefinitionException {
+ return beanFactory.getAliases(name);
+ }
+
+ /**
+ * 获取aop代理对象
+ *
+ * @param invoker
+ *
+ * @return
+ */
+ @SuppressWarnings("unchecked")
+ public static T getAopProxy (T invoker) {
+ return (T) AopContext.currentProxy();
+ }
+
+ @Override
+ public void postProcessBeanFactory (ConfigurableListableBeanFactory beanFactory) throws BeansException {
+ SpringUtils.beanFactory = beanFactory;
+ }
+}
diff --git a/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/utils/StringUtils.java b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/utils/StringUtils.java
new file mode 100644
index 0000000..ec8f557
--- /dev/null
+++ b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/utils/StringUtils.java
@@ -0,0 +1,504 @@
+package com.muyu.common.core.utils;
+
+import com.muyu.common.core.constant.Constants;
+import com.muyu.common.core.text.StrFormatter;
+import org.springframework.util.AntPathMatcher;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 字符串工具类
+ *
+ * @author muyu
+ */
+public class StringUtils extends org.apache.commons.lang3.StringUtils {
+ /**
+ * 空字符串
+ */
+ private static final String NULLSTR = "";
+
+ /**
+ * 下划线
+ */
+ private static final char SEPARATOR = '_';
+
+ /**
+ * 获取参数不为空值
+ *
+ * @param value defaultValue 要判断的value
+ *
+ * @return value 返回值
+ */
+ public static T nvl (T value, T defaultValue) {
+ return value != null ? value : defaultValue;
+ }
+
+ /**
+ * * 判断一个Collection是否为空, 包含List,Set,Queue
+ *
+ * @param coll 要判断的Collection
+ *
+ * @return true:为空 false:非空
+ */
+ public static boolean isEmpty (Collection> coll) {
+ return isNull(coll) || coll.isEmpty();
+ }
+
+ /**
+ * * 判断一个Collection是否非空,包含List,Set,Queue
+ *
+ * @param coll 要判断的Collection
+ *
+ * @return true:非空 false:空
+ */
+ public static boolean isNotEmpty (Collection> coll) {
+ return !isEmpty(coll);
+ }
+
+ /**
+ * * 判断一个对象数组是否为空
+ *
+ * @param objects 要判断的对象数组
+ * * @return true:为空 false:非空
+ */
+ public static boolean isEmpty (Object[] objects) {
+ return isNull(objects) || (objects.length == 0);
+ }
+
+ /**
+ * * 判断一个对象数组是否非空
+ *
+ * @param objects 要判断的对象数组
+ *
+ * @return true:非空 false:空
+ */
+ public static boolean isNotEmpty (Object[] objects) {
+ return !isEmpty(objects);
+ }
+
+ /**
+ * * 判断一个Map是否为空
+ *
+ * @param map 要判断的Map
+ *
+ * @return true:为空 false:非空
+ */
+ public static boolean isEmpty (Map, ?> map) {
+ return isNull(map) || map.isEmpty();
+ }
+
+ /**
+ * * 判断一个Map是否为空
+ *
+ * @param map 要判断的Map
+ *
+ * @return true:非空 false:空
+ */
+ public static boolean isNotEmpty (Map, ?> map) {
+ return !isEmpty(map);
+ }
+
+ /**
+ * * 判断一个字符串是否为空串
+ *
+ * @param str String
+ *
+ * @return true:为空 false:非空
+ */
+ public static boolean isEmpty (String str) {
+ return isNull(str) || NULLSTR.equals(str.trim());
+ }
+
+ /**
+ * * 判断一个字符串是否为非空串
+ *
+ * @param str String
+ *
+ * @return true:非空串 false:空串
+ */
+ public static boolean isNotEmpty (String str) {
+ return !isEmpty(str);
+ }
+
+ /**
+ * * 判断一个对象是否为空
+ *
+ * @param object Object
+ *
+ * @return true:为空 false:非空
+ */
+ public static boolean isNull (Object object) {
+ return object == null;
+ }
+
+ /**
+ * * 判断一个对象是否非空
+ *
+ * @param object Object
+ *
+ * @return true:非空 false:空
+ */
+ public static boolean isNotNull (Object object) {
+ return !isNull(object);
+ }
+
+ /**
+ * * 判断一个对象是否是数组类型(Java基本型别的数组)
+ *
+ * @param object 对象
+ *
+ * @return true:是数组 false:不是数组
+ */
+ public static boolean isArray (Object object) {
+ return isNotNull(object) && object.getClass().isArray();
+ }
+
+ /**
+ * 去空格
+ */
+ public static String trim (String str) {
+ return (str == null ? "" : str.trim());
+ }
+
+ /**
+ * 截取字符串
+ *
+ * @param str 字符串
+ * @param start 开始
+ *
+ * @return 结果
+ */
+ public static String substring (final String str, int start) {
+ if (str == null) {
+ return NULLSTR;
+ }
+
+ if (start < 0) {
+ start = str.length() + start;
+ }
+
+ if (start < 0) {
+ start = 0;
+ }
+ if (start > str.length()) {
+ return NULLSTR;
+ }
+
+ return str.substring(start);
+ }
+
+ /**
+ * 截取字符串
+ *
+ * @param str 字符串
+ * @param start 开始
+ * @param end 结束
+ *
+ * @return 结果
+ */
+ public static String substring (final String str, int start, int end) {
+ if (str == null) {
+ return NULLSTR;
+ }
+
+ if (end < 0) {
+ end = str.length() + end;
+ }
+ if (start < 0) {
+ start = str.length() + start;
+ }
+
+ if (end > str.length()) {
+ end = str.length();
+ }
+
+ if (start > end) {
+ return NULLSTR;
+ }
+
+ if (start < 0) {
+ start = 0;
+ }
+ if (end < 0) {
+ end = 0;
+ }
+
+ return str.substring(start, end);
+ }
+
+ /**
+ * 判断是否为空,并且不是空白字符
+ *
+ * @param str 要判断的value
+ *
+ * @return 结果
+ */
+ public static boolean hasText (String str) {
+ return (str != null && !str.isEmpty() && containsText(str));
+ }
+
+ private static boolean containsText (CharSequence str) {
+ int strLen = str.length();
+ for (int i = 0 ; i < strLen ; i++) {
+ if (!Character.isWhitespace(str.charAt(i))) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * 格式化文本, {} 表示占位符
+ * 此方法只是简单将占位符 {} 按照顺序替换为参数
+ * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可
+ * 例:
+ * 通常使用:format("this is {} for {}", "a", "b") -> this is a for b
+ * 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a
+ * 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b
+ *
+ * @param template 文本模板,被替换的部分用 {} 表示
+ * @param params 参数值
+ *
+ * @return 格式化后的文本
+ */
+ public static String format (String template, Object... params) {
+ if (isEmpty(params) || isEmpty(template)) {
+ return template;
+ }
+ return StrFormatter.format(template, params);
+ }
+
+ /**
+ * 是否为http(s)://开头
+ *
+ * @param link 链接
+ *
+ * @return 结果
+ */
+ public static boolean ishttp (String link) {
+ return StringUtils.startsWithAny(link, Constants.HTTP, Constants.HTTPS);
+ }
+
+ /**
+ * 判断给定的collection列表中是否包含数组array 判断给定的数组array中是否包含给定的元素value
+ *
+ * @param collection 给定的集合
+ * @param array 给定的数组
+ *
+ * @return boolean 结果
+ */
+ public static boolean containsAny (Collection collection, String... array) {
+ if (isEmpty(collection) || isEmpty(array)) {
+ return false;
+ } else {
+ for (String str : array) {
+ if (collection.contains(str)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ /**
+ * 驼峰转下划线命名
+ */
+ public static String toUnderScoreCase (String str) {
+ if (str == null) {
+ return null;
+ }
+ StringBuilder sb = new StringBuilder();
+ // 前置字符是否大写
+ boolean preCharIsUpperCase = true;
+ // 当前字符是否大写
+ boolean curreCharIsUpperCase = true;
+ // 下一字符是否大写
+ boolean nexteCharIsUpperCase = true;
+ for (int i = 0 ; i < str.length() ; i++) {
+ char c = str.charAt(i);
+ if (i > 0) {
+ preCharIsUpperCase = Character.isUpperCase(str.charAt(i - 1));
+ } else {
+ preCharIsUpperCase = false;
+ }
+
+ curreCharIsUpperCase = Character.isUpperCase(c);
+
+ if (i < (str.length() - 1)) {
+ nexteCharIsUpperCase = Character.isUpperCase(str.charAt(i + 1));
+ }
+
+ if (preCharIsUpperCase && curreCharIsUpperCase && !nexteCharIsUpperCase) {
+ sb.append(SEPARATOR);
+ } else if ((i != 0 && !preCharIsUpperCase) && curreCharIsUpperCase) {
+ sb.append(SEPARATOR);
+ }
+ sb.append(Character.toLowerCase(c));
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * 是否包含字符串
+ *
+ * @param str 验证字符串
+ * @param strs 字符串组
+ *
+ * @return 包含返回true
+ */
+ public static boolean inStringIgnoreCase (String str, String... strs) {
+ if (str != null && strs != null) {
+ for (String s : strs) {
+ if (str.equalsIgnoreCase(trim(s))) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。 例如:HELLO_WORLD->HelloWorld
+ *
+ * @param name 转换前的下划线大写方式命名的字符串
+ *
+ * @return 转换后的驼峰式命名的字符串
+ */
+ public static String convertToCamelCase (String name) {
+ StringBuilder result = new StringBuilder();
+ // 快速检查
+ if (name == null || name.isEmpty()) {
+ // 没必要转换
+ return "";
+ } else if (!name.contains("_")) {
+ // 不含下划线,仅将首字母大写
+ return name.substring(0, 1).toUpperCase() + name.substring(1);
+ }
+ // 用下划线将原始字符串分割
+ String[] camels = name.split("_");
+ for (String camel : camels) {
+ // 跳过原始字符串中开头、结尾的下换线或双重下划线
+ if (camel.isEmpty()) {
+ continue;
+ }
+ // 首字母大写
+ result.append(camel.substring(0, 1).toUpperCase());
+ result.append(camel.substring(1).toLowerCase());
+ }
+ return result.toString();
+ }
+
+ /**
+ * 驼峰式命名法
+ * 例如:user_name->userName
+ */
+ public static String toCamelCase (String s) {
+ if (s == null) {
+ return null;
+ }
+ if (s.indexOf(SEPARATOR) == -1) {
+ return s;
+ }
+ s = s.toLowerCase();
+ StringBuilder sb = new StringBuilder(s.length());
+ boolean upperCase = false;
+ for (int i = 0 ; i < s.length() ; i++) {
+ char c = s.charAt(i);
+
+ if (c == SEPARATOR) {
+ upperCase = true;
+ } else if (upperCase) {
+ sb.append(Character.toUpperCase(c));
+ upperCase = false;
+ } else {
+ sb.append(c);
+ }
+ }
+ return sb.toString();
+ }
+
+ /**
+ * 查找指定字符串是否匹配指定字符串列表中的任意一个字符串
+ *
+ * @param str 指定字符串
+ * @param strs 需要检查的字符串数组
+ *
+ * @return 是否匹配
+ */
+ public static boolean matches (String str, List strs) {
+ if (isEmpty(str) || isEmpty(strs)) {
+ return false;
+ }
+ for (String pattern : strs) {
+ if (isMatch(pattern, str)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * 判断url是否与规则配置:
+ * ? 表示单个字符;
+ * * 表示一层路径内的任意字符串,不可跨层级;
+ * ** 表示任意层路径;
+ *
+ * @param pattern 匹配规则
+ * @param url 需要匹配的url
+ *
+ * @return
+ */
+ public static boolean isMatch (String pattern, String url) {
+ AntPathMatcher matcher = new AntPathMatcher();
+ return matcher.match(pattern, url);
+ }
+
+ @SuppressWarnings("unchecked")
+ public static T cast (Object obj) {
+ return (T) obj;
+ }
+
+ /**
+ * 数字左边补齐0,使之达到指定长度。注意,如果数字转换为字符串后,长度大于size,则只保留 最后size个字符。
+ *
+ * @param num 数字对象
+ * @param size 字符串指定长度
+ *
+ * @return 返回数字的字符串格式,该字符串为指定长度。
+ */
+ public static final String padl (final Number num, final int size) {
+ return padl(num.toString(), size, '0');
+ }
+
+ /**
+ * 字符串左补齐。如果原始字符串s长度大于size,则只保留最后size个字符。
+ *
+ * @param s 原始字符串
+ * @param size 字符串指定长度
+ * @param c 用于补齐的字符
+ *
+ * @return 返回指定长度的字符串,由原字符串左补齐或截取得到。
+ */
+ public static final String padl (final String s, final int size, final char c) {
+ final StringBuilder sb = new StringBuilder(size);
+ if (s != null) {
+ final int len = s.length();
+ if (s.length() <= size) {
+ for (int i = size - len ; i > 0 ; i--) {
+ sb.append(c);
+ }
+ sb.append(s);
+ } else {
+ return s.substring(len - size, len);
+ }
+ } else {
+ for (int i = size ; i > 0 ; i--) {
+ sb.append(c);
+ }
+ }
+ return sb.toString();
+ }
+}
diff --git a/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/utils/bean/BeanUtils.java b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/utils/bean/BeanUtils.java
new file mode 100644
index 0000000..d44b351
--- /dev/null
+++ b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/utils/bean/BeanUtils.java
@@ -0,0 +1,107 @@
+package com.muyu.common.core.utils.bean;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Bean 工具类
+ *
+ * @author muyu
+ */
+public class BeanUtils extends org.springframework.beans.BeanUtils {
+ /**
+ * Bean方法名中属性名开始的下标
+ */
+ private static final int BEAN_METHOD_PROP_INDEX = 3;
+
+ /**
+ * 匹配getter方法的正则表达式
+ */
+ private static final Pattern GET_PATTERN = Pattern.compile("get(\\p{javaUpperCase}\\w*)");
+
+ /**
+ * 匹配setter方法的正则表达式
+ */
+ private static final Pattern SET_PATTERN = Pattern.compile("set(\\p{javaUpperCase}\\w*)");
+
+ /**
+ * Bean属性复制工具方法。
+ *
+ * @param dest 目标对象
+ * @param src 源对象
+ */
+ public static void copyBeanProp (Object dest, Object src) {
+ try {
+ copyProperties(src, dest);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * 获取对象的setter方法。
+ *
+ * @param obj 对象
+ *
+ * @return 对象的setter方法列表
+ */
+ public static List getSetterMethods (Object obj) {
+ // setter方法列表
+ List setterMethods = new ArrayList();
+
+ // 获取所有方法
+ Method[] methods = obj.getClass().getMethods();
+
+ // 查找setter方法
+
+ for (Method method : methods) {
+ Matcher m = SET_PATTERN.matcher(method.getName());
+ if (m.matches() && (method.getParameterTypes().length == 1)) {
+ setterMethods.add(method);
+ }
+ }
+ // 返回setter方法列表
+ return setterMethods;
+ }
+
+ /**
+ * 获取对象的getter方法。
+ *
+ * @param obj 对象
+ *
+ * @return 对象的getter方法列表
+ */
+
+ public static List getGetterMethods (Object obj) {
+ // getter方法列表
+ List getterMethods = new ArrayList();
+ // 获取所有方法
+ Method[] methods = obj.getClass().getMethods();
+ // 查找getter方法
+ for (Method method : methods) {
+ Matcher m = GET_PATTERN.matcher(method.getName());
+ if (m.matches() && (method.getParameterTypes().length == 0)) {
+ getterMethods.add(method);
+ }
+ }
+ // 返回getter方法列表
+ return getterMethods;
+ }
+
+ /**
+ * 检查Bean方法名中的属性名是否相等。
+ * 如getName()和setName()属性名一样,getName()和setAge()属性名不一样。
+ *
+ * @param m1 方法名1
+ * @param m2 方法名2
+ *
+ * @return 属性名一样返回true,否则返回false
+ */
+
+ public static boolean isMethodPropEquals (String m1, String m2) {
+ return m1.substring(BEAN_METHOD_PROP_INDEX).equals(m2.substring(BEAN_METHOD_PROP_INDEX));
+ }
+}
diff --git a/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/utils/bean/BeanValidators.java b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/utils/bean/BeanValidators.java
new file mode 100644
index 0000000..abbb157
--- /dev/null
+++ b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/utils/bean/BeanValidators.java
@@ -0,0 +1,21 @@
+package com.muyu.common.core.utils.bean;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.ConstraintViolationException;
+import javax.validation.Validator;
+import java.util.Set;
+
+/**
+ * bean对象属性验证
+ *
+ * @author muyu
+ */
+public class BeanValidators {
+ public static void validateWithException (Validator validator, Object object, Class>... groups)
+ throws ConstraintViolationException {
+ Set> constraintViolations = validator.validate(object, groups);
+ if (!constraintViolations.isEmpty()) {
+ throw new ConstraintViolationException(constraintViolations);
+ }
+ }
+}
diff --git a/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/utils/file/FileTypeUtils.java b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/utils/file/FileTypeUtils.java
new file mode 100644
index 0000000..dde7e85
--- /dev/null
+++ b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/utils/file/FileTypeUtils.java
@@ -0,0 +1,85 @@
+package com.muyu.common.core.utils.file;
+
+import org.apache.commons.io.FilenameUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.File;
+import java.util.Objects;
+
+/**
+ * 文件类型工具类
+ *
+ * @author muyu
+ */
+public class FileTypeUtils {
+ /**
+ * 获取文件类型
+ *
+ * 例如: muyu.txt, 返回: txt
+ *
+ * @param file 文件名
+ *
+ * @return 后缀(不含".")
+ */
+ public static String getFileType (File file) {
+ if (null == file) {
+ return StringUtils.EMPTY;
+ }
+ return getFileType(file.getName());
+ }
+
+ /**
+ * 获取文件类型
+ *
+ * 例如: muyu.txt, 返回: txt
+ *
+ * @param fileName 文件名
+ *
+ * @return 后缀(不含".")
+ */
+ public static String getFileType (String fileName) {
+ int separatorIndex = fileName.lastIndexOf(".");
+ if (separatorIndex < 0) {
+ return "";
+ }
+ return fileName.substring(separatorIndex + 1).toLowerCase();
+ }
+
+ /**
+ * 获取文件名的后缀
+ *
+ * @param file 表单文件
+ *
+ * @return 后缀名
+ */
+ public static final String getExtension (MultipartFile file) {
+ String extension = FilenameUtils.getExtension(file.getOriginalFilename());
+ if (StringUtils.isEmpty(extension)) {
+ extension = MimeTypeUtils.getExtension(Objects.requireNonNull(file.getContentType()));
+ }
+ return extension;
+ }
+
+ /**
+ * 获取文件类型
+ *
+ * @param photoByte 文件字节码
+ *
+ * @return 后缀(不含".")
+ */
+ public static String getFileExtendName (byte[] photoByte) {
+ String strFileExtendName = "JPG";
+ if ((photoByte[0] == 71) && (photoByte[1] == 73) && (photoByte[2] == 70) && (photoByte[3] == 56)
+ && ((photoByte[4] == 55) || (photoByte[4] == 57)) && (photoByte[5] == 97)) {
+ strFileExtendName = "GIF";
+ } else if ((photoByte[6] == 74) && (photoByte[7] == 70) && (photoByte[8] == 73) && (photoByte[9] == 70)) {
+ strFileExtendName = "JPG";
+ } else if ((photoByte[0] == 66) && (photoByte[1] == 77)) {
+ strFileExtendName = "BMP";
+ } else if ((photoByte[1] == 80) && (photoByte[2] == 78) && (photoByte[3] == 71)) {
+ strFileExtendName = "PNG";
+ }
+ return strFileExtendName;
+ }
+}
diff --git a/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/utils/file/FileUtils.java b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/utils/file/FileUtils.java
new file mode 100644
index 0000000..30f01b8
--- /dev/null
+++ b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/utils/file/FileUtils.java
@@ -0,0 +1,223 @@
+package com.muyu.common.core.utils.file;
+
+import com.muyu.common.core.utils.StringUtils;
+import org.apache.commons.lang3.ArrayUtils;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.*;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * 文件处理工具类
+ *
+ * @author muyu
+ */
+public class FileUtils {
+ /**
+ * 字符常量:斜杠 {@code '/'}
+ */
+ public static final char SLASH = '/';
+
+ /**
+ * 字符常量:反斜杠 {@code '\\'}
+ */
+ public static final char BACKSLASH = '\\';
+
+ public static String FILENAME_PATTERN = "[a-zA-Z0-9_\\-\\|\\.\\u4e00-\\u9fa5]+";
+
+ /**
+ * 输出指定文件的byte数组
+ *
+ * @param filePath 文件路径
+ * @param os 输出流
+ *
+ * @return
+ */
+ public static void writeBytes (String filePath, OutputStream os) throws IOException {
+ FileInputStream fis = null;
+ try {
+ File file = new File(filePath);
+ if (!file.exists()) {
+ throw new FileNotFoundException(filePath);
+ }
+ fis = new FileInputStream(file);
+ byte[] b = new byte[1024];
+ int length;
+ while ((length = fis.read(b)) > 0) {
+ os.write(b, 0, length);
+ }
+ } catch (IOException e) {
+ throw e;
+ } finally {
+ if (os != null) {
+ try {
+ os.close();
+ } catch (IOException e1) {
+ e1.printStackTrace();
+ }
+ }
+ if (fis != null) {
+ try {
+ fis.close();
+ } catch (IOException e1) {
+ e1.printStackTrace();
+ }
+ }
+ }
+ }
+
+ /**
+ * 删除文件
+ *
+ * @param filePath 文件
+ *
+ * @return
+ */
+ public static boolean deleteFile (String filePath) {
+ boolean flag = false;
+ File file = new File(filePath);
+ // 路径为文件且不为空则进行删除
+ if (file.isFile() && file.exists()) {
+ flag = file.delete();
+ }
+ return flag;
+ }
+
+ /**
+ * 文件名称验证
+ *
+ * @param filename 文件名称
+ *
+ * @return true 正常 false 非法
+ */
+ public static boolean isValidFilename (String filename) {
+ return filename.matches(FILENAME_PATTERN);
+ }
+
+ /**
+ * 检查文件是否可下载
+ *
+ * @param resource 需要下载的文件
+ *
+ * @return true 正常 false 非法
+ */
+ public static boolean checkAllowDownload (String resource) {
+ // 禁止目录上跳级别
+ if (StringUtils.contains(resource, "..")) {
+ return false;
+ }
+ // 判断是否在允许下载的文件规则内
+ return ArrayUtils.contains(MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION, FileTypeUtils.getFileType(resource));
+ }
+
+ /**
+ * 下载文件名重新编码
+ *
+ * @param request 请求对象
+ * @param fileName 文件名
+ *
+ * @return 编码后的文件名
+ */
+ public static String setFileDownloadHeader (HttpServletRequest request, String fileName) throws UnsupportedEncodingException {
+ final String agent = request.getHeader("USER-AGENT");
+ String filename = fileName;
+ if (agent.contains("MSIE")) {
+ // IE浏览器
+ filename = URLEncoder.encode(filename, "utf-8");
+ filename = filename.replace("+", " ");
+ } else if (agent.contains("Firefox")) {
+ // 火狐浏览器
+ filename = new String(fileName.getBytes(), "ISO8859-1");
+ } else if (agent.contains("Chrome")) {
+ // google浏览器
+ filename = URLEncoder.encode(filename, "utf-8");
+ } else {
+ // 其它浏览器
+ filename = URLEncoder.encode(filename, "utf-8");
+ }
+ return filename;
+ }
+
+ /**
+ * 返回文件名
+ *
+ * @param filePath 文件
+ *
+ * @return 文件名
+ */
+ public static String getName (String filePath) {
+ if (null == filePath) {
+ return null;
+ }
+ int len = filePath.length();
+ if (0 == len) {
+ return filePath;
+ }
+ if (isFileSeparator(filePath.charAt(len - 1))) {
+ // 以分隔符结尾的去掉结尾分隔符
+ len--;
+ }
+
+ int begin = 0;
+ char c;
+ for (int i = len - 1 ; i > -1 ; i--) {
+ c = filePath.charAt(i);
+ if (isFileSeparator(c)) {
+ // 查找最后一个路径分隔符(/或者\)
+ begin = i + 1;
+ break;
+ }
+ }
+
+ return filePath.substring(begin, len);
+ }
+
+ /**
+ * 是否为Windows或者Linux(Unix)文件分隔符
+ * Windows平台下分隔符为\,Linux(Unix)为/
+ *
+ * @param c 字符
+ *
+ * @return 是否为Windows或者Linux(Unix)文件分隔符
+ */
+ public static boolean isFileSeparator (char c) {
+ return SLASH == c || BACKSLASH == c;
+ }
+
+ /**
+ * 下载文件名重新编码
+ *
+ * @param response 响应对象
+ * @param realFileName 真实文件名
+ *
+ * @return
+ */
+ public static void setAttachmentResponseHeader (HttpServletResponse response, String realFileName) throws UnsupportedEncodingException {
+ String percentEncodedFileName = percentEncode(realFileName);
+
+ StringBuilder contentDispositionValue = new StringBuilder();
+ contentDispositionValue.append("attachment; filename=")
+ .append(percentEncodedFileName)
+ .append(";")
+ .append("filename*=")
+ .append("utf-8''")
+ .append(percentEncodedFileName);
+
+ response.setHeader("Content-disposition", contentDispositionValue.toString());
+ response.setHeader("download-filename", percentEncodedFileName);
+ }
+
+ /**
+ * 百分号编码工具方法
+ *
+ * @param s 需要百分号编码的字符串
+ *
+ * @return 百分号编码后的字符串
+ */
+ public static String percentEncode (String s) throws UnsupportedEncodingException {
+ String encode = URLEncoder.encode(s, StandardCharsets.UTF_8.toString());
+ return encode.replaceAll("\\+", "%20");
+ }
+}
diff --git a/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/utils/file/ImageUtils.java b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/utils/file/ImageUtils.java
new file mode 100644
index 0000000..7e23345
--- /dev/null
+++ b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/utils/file/ImageUtils.java
@@ -0,0 +1,69 @@
+package com.muyu.common.core.utils.file;
+
+import org.apache.poi.util.IOUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.Arrays;
+
+/**
+ * 图片处理工具类
+ *
+ * @author muyu
+ */
+public class ImageUtils {
+ private static final Logger log = LoggerFactory.getLogger(ImageUtils.class);
+
+ public static byte[] getImage (String imagePath) {
+ InputStream is = getFile(imagePath);
+ try {
+ return IOUtils.toByteArray(is);
+ } catch (Exception e) {
+ log.error("图片加载异常 {}", e);
+ return null;
+ } finally {
+ IOUtils.closeQuietly(is);
+ }
+ }
+
+ public static InputStream getFile (String imagePath) {
+ try {
+ byte[] result = readFile(imagePath);
+ result = Arrays.copyOf(result, result.length);
+ return new ByteArrayInputStream(result);
+ } catch (Exception e) {
+ log.error("获取图片异常 {}", e);
+ }
+ return null;
+ }
+
+ /**
+ * 读取文件为字节数据
+ *
+ * @param url 地址
+ *
+ * @return 字节数据
+ */
+ public static byte[] readFile (String url) {
+ InputStream in = null;
+ try {
+ // 网络地址
+ URL urlObj = new URL(url);
+ URLConnection urlConnection = urlObj.openConnection();
+ urlConnection.setConnectTimeout(30 * 1000);
+ urlConnection.setReadTimeout(60 * 1000);
+ urlConnection.setDoInput(true);
+ in = urlConnection.getInputStream();
+ return IOUtils.toByteArray(in);
+ } catch (Exception e) {
+ log.error("访问文件异常 {}", e);
+ return null;
+ } finally {
+ IOUtils.closeQuietly(in);
+ }
+ }
+}
diff --git a/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/utils/file/MimeTypeUtils.java b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/utils/file/MimeTypeUtils.java
new file mode 100644
index 0000000..9eb1d84
--- /dev/null
+++ b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/utils/file/MimeTypeUtils.java
@@ -0,0 +1,56 @@
+package com.muyu.common.core.utils.file;
+
+/**
+ * 媒体类型工具类
+ *
+ * @author muyu
+ */
+public class MimeTypeUtils {
+ public static final String IMAGE_PNG = "image/png";
+
+ public static final String IMAGE_JPG = "image/jpg";
+
+ public static final String IMAGE_JPEG = "image/jpeg";
+
+ public static final String IMAGE_BMP = "image/bmp";
+
+ public static final String IMAGE_GIF = "image/gif";
+
+ public static final String[] IMAGE_EXTENSION = {"bmp", "gif", "jpg", "jpeg", "png"};
+
+ public static final String[] FLASH_EXTENSION = {"swf", "flv"};
+
+ public static final String[] MEDIA_EXTENSION = {"swf", "flv", "mp3", "wav", "wma", "wmv", "mid", "avi", "mpg",
+ "asf", "rm", "rmvb"};
+
+ public static final String[] VIDEO_EXTENSION = {"mp4", "avi", "rmvb"};
+
+ public static final String[] DEFAULT_ALLOWED_EXTENSION = {
+ // 图片
+ "bmp", "gif", "jpg", "jpeg", "png",
+ // word excel powerpoint
+ "doc", "docx", "xls", "xlsx", "ppt", "pptx", "html", "htm", "txt",
+ // 压缩文件
+ "rar", "zip", "gz", "bz2",
+ // 视频格式
+ "mp4", "avi", "rmvb",
+ // pdf
+ "pdf"};
+
+ public static String getExtension (String prefix) {
+ switch (prefix) {
+ case IMAGE_PNG:
+ return "png";
+ case IMAGE_JPG:
+ return "jpg";
+ case IMAGE_JPEG:
+ return "jpeg";
+ case IMAGE_BMP:
+ return "bmp";
+ case IMAGE_GIF:
+ return "gif";
+ default:
+ return "";
+ }
+ }
+}
diff --git a/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/utils/html/EscapeUtil.java b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/utils/html/EscapeUtil.java
new file mode 100644
index 0000000..7dba9be
--- /dev/null
+++ b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/utils/html/EscapeUtil.java
@@ -0,0 +1,145 @@
+package com.muyu.common.core.utils.html;
+
+import com.muyu.common.core.utils.StringUtils;
+
+/**
+ * 转义和反转义工具类
+ *
+ * @author muyu
+ */
+public class EscapeUtil {
+ public static final String RE_HTML_MARK = "(<[^<]*?>)|(<[\\s]*?/[^<]*?>)|(<[^<]*?/[\\s]*?>)";
+
+ private static final char[][] TEXT = new char[64][];
+
+ static {
+ for (int i = 0 ; i < 64 ; i++) {
+ TEXT[i] = new char[]{(char) i};
+ }
+
+ // special HTML characters
+ TEXT['\''] = "'".toCharArray(); // 单引号
+ TEXT['"'] = """.toCharArray(); // 双引号
+ TEXT['&'] = "&".toCharArray(); // &符
+ TEXT['<'] = "<".toCharArray(); // 小于号
+ TEXT['>'] = ">".toCharArray(); // 大于号
+ }
+
+ /**
+ * 转义文本中的HTML字符为安全的字符
+ *
+ * @param text 被转义的文本
+ *
+ * @return 转义后的文本
+ */
+ public static String escape (String text) {
+ return encode(text);
+ }
+
+ /**
+ * 还原被转义的HTML特殊字符
+ *
+ * @param content 包含转义符的HTML内容
+ *
+ * @return 转换后的字符串
+ */
+ public static String unescape (String content) {
+ return decode(content);
+ }
+
+ /**
+ * 清除所有HTML标签,但是不删除标签内的内容
+ *
+ * @param content 文本
+ *
+ * @return 清除标签后的文本
+ */
+ public static String clean (String content) {
+ return new HTMLFilter().filter(content);
+ }
+
+ /**
+ * Escape编码
+ *
+ * @param text 被编码的文本
+ *
+ * @return 编码后的字符
+ */
+ private static String encode (String text) {
+ if (StringUtils.isEmpty(text)) {
+ return StringUtils.EMPTY;
+ }
+
+ final StringBuilder tmp = new StringBuilder(text.length() * 6);
+ char c;
+ for (int i = 0 ; i < text.length() ; i++) {
+ c = text.charAt(i);
+ if (c < 256) {
+ tmp.append("%");
+ if (c < 16) {
+ tmp.append("0");
+ }
+ tmp.append(Integer.toString(c, 16));
+ } else {
+ tmp.append("%u");
+ if (c <= 0xfff) {
+ // issue#I49JU8@Gitee
+ tmp.append("0");
+ }
+ tmp.append(Integer.toString(c, 16));
+ }
+ }
+ return tmp.toString();
+ }
+
+ /**
+ * Escape解码
+ *
+ * @param content 被转义的内容
+ *
+ * @return 解码后的字符串
+ */
+ public static String decode (String content) {
+ if (StringUtils.isEmpty(content)) {
+ return content;
+ }
+
+ StringBuilder tmp = new StringBuilder(content.length());
+ int lastPos = 0, pos = 0;
+ char ch;
+ while (lastPos < content.length()) {
+ pos = content.indexOf("%", lastPos);
+ if (pos == lastPos) {
+ if (content.charAt(pos + 1) == 'u') {
+ ch = (char) Integer.parseInt(content.substring(pos + 2, pos + 6), 16);
+ tmp.append(ch);
+ lastPos = pos + 6;
+ } else {
+ ch = (char) Integer.parseInt(content.substring(pos + 1, pos + 3), 16);
+ tmp.append(ch);
+ lastPos = pos + 3;
+ }
+ } else {
+ if (pos == -1) {
+ tmp.append(content.substring(lastPos));
+ lastPos = content.length();
+ } else {
+ tmp.append(content.substring(lastPos, pos));
+ lastPos = pos;
+ }
+ }
+ }
+ return tmp.toString();
+ }
+
+ public static void main (String[] args) {
+ String html = "";
+ String escape = EscapeUtil.escape(html);
+ // String html = "ipt>alert(\"XSS\")ipt>";
+ // String html = "<123";
+ // String html = "123>";
+ System.out.println("clean: " + EscapeUtil.clean(html));
+ System.out.println("escape: " + escape);
+ System.out.println("unescape: " + EscapeUtil.unescape(escape));
+ }
+}
diff --git a/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/utils/html/HTMLFilter.java b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/utils/html/HTMLFilter.java
new file mode 100644
index 0000000..68221f3
--- /dev/null
+++ b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/utils/html/HTMLFilter.java
@@ -0,0 +1,498 @@
+package com.muyu.common.core.utils.html;
+
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * HTML过滤器,用于去除XSS漏洞隐患。
+ *
+ * @author muyu
+ */
+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("([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 P_REMOVE_PAIR_BLANKS = new ConcurrentHashMap<>();
+ private static final ConcurrentMap P_REMOVE_SELF_BLANKS = new ConcurrentHashMap<>();
+
+ /**
+ * set of allowed html elements, along with allowed attributes for each element
+ **/
+ private final Map> vAllowed;
+ /**
+ * counts of open tags for each (allowable) html element
+ **/
+ private final Map 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.
+ */
+ private final boolean alwaysMakeTags;
+
+ /**
+ * Default constructor.
+ */
+ public HTMLFilter () {
+ vAllowed = new HashMap<>();
+
+ final ArrayList a_atts = new ArrayList<>();
+ a_atts.add("href");
+ a_atts.add("target");
+ vAllowed.put("a", a_atts);
+
+ final ArrayList 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 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 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>) 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;
+ }
+
+ // ---------------------------------------------------------------
+ // 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;
+ }
+
+ 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 static boolean inArray (final String s, final String[] array) {
+ for (String item : array) {
+ if (item != null && item.equals(s)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void reset () {
+ vTagCounts.clear();
+ }
+
+ /**
+ * 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 isAlwaysMakeTags () {
+ return alwaysMakeTags;
+ }
+
+ 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 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 paramNames = new ArrayList<>();
+ final List 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).intValue();
+ 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).intValue();
+ 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).intValue();
+ 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 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));
+ }
+}
diff --git a/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/utils/ip/IpUtils.java b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/utils/ip/IpUtils.java
new file mode 100644
index 0000000..f7ad9bf
--- /dev/null
+++ b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/utils/ip/IpUtils.java
@@ -0,0 +1,331 @@
+package com.muyu.common.core.utils.ip;
+
+import com.muyu.common.core.utils.ServletUtils;
+import com.muyu.common.core.utils.StringUtils;
+
+import javax.servlet.http.HttpServletRequest;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+/**
+ * 获取IP方法
+ *
+ * @author muyu
+ */
+public class IpUtils {
+ 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_SEG = "(" + REGX_IP + "\\-" + REGX_IP + ")";
+ public final static String REGX_IP_WILDCARD = "(((\\*\\.){3}\\*)|(" + REGX_0_255 + "(\\.\\*){3})|(" + REGX_0_255 + "\\." + REGX_0_255 + ")(\\.\\*){2}" + "|((" + REGX_0_255 + "\\.){3}\\*))";
+
+ /**
+ * 获取客户端IP
+ *
+ * @return IP地址
+ */
+ public static String getIpAddr () {
+ return getIpAddr(ServletUtils.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 (StringUtils.isNull(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:
+ switch (b1) {
+ case 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 e) {
+ }
+ return "127.0.0.1";
+ }
+
+ /**
+ * 获取主机名
+ *
+ * @return 本地主机名
+ */
+ public static String getHostName () {
+ try {
+ return InetAddress.getLocalHost().getHostName();
+ } catch (UnknownHostException e) {
+ }
+ return "未知";
+ }
+
+ /**
+ * 从多级反向代理中获得第一个非unknown IP地址
+ *
+ * @param ip 获得的IP地址
+ *
+ * @return 第一个非unknown IP地址
+ */
+ public static String getMultistageReverseProxyIp (String ip) {
+ // 多级反向代理检测
+ if (ip != null && ip.indexOf(",") > 0) {
+ final String[] ips = ip.trim().split(",");
+ for (String subIp : ips) {
+ if (false == isUnknown(subIp)) {
+ ip = subIp;
+ break;
+ }
+ }
+ }
+ return StringUtils.substring(ip, 0, 255);
+ }
+
+ /**
+ * 检测给定字符串是否为未知,多用于检测HTTP请求相关
+ *
+ * @param checkString 被检测的字符串
+ *
+ * @return 是否未知
+ */
+ public static boolean isUnknown (String checkString) {
+ return StringUtils.isBlank(checkString) || "unknown".equalsIgnoreCase(checkString);
+ }
+
+ /**
+ * 是否为IP
+ */
+ public static boolean isIP (String ip) {
+ return StringUtils.isNotBlank(ip) && ip.matches(REGX_IP);
+ }
+
+ /**
+ * 是否为IP,或 *为间隔的通配符地址
+ */
+ public static boolean isIpWildCard (String ip) {
+ return StringUtils.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 StringUtils.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 (StringUtils.isEmpty(filter) || StringUtils.isEmpty(ip)) {
+ return false;
+ }
+ String[] ips = filter.split(";");
+ 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;
+ }
+}
diff --git a/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/utils/poi/ExcelHandlerAdapter.java b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/utils/poi/ExcelHandlerAdapter.java
new file mode 100644
index 0000000..e2041fc
--- /dev/null
+++ b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/utils/poi/ExcelHandlerAdapter.java
@@ -0,0 +1,23 @@
+package com.muyu.common.core.utils.poi;
+
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.Workbook;
+
+/**
+ * Excel数据格式处理适配器
+ *
+ * @author muyu
+ */
+public interface ExcelHandlerAdapter {
+ /**
+ * 格式化
+ *
+ * @param value 单元格数据值
+ * @param args excel注解args参数组
+ * @param cell 单元格对象
+ * @param wb 工作簿对象
+ *
+ * @return 处理后的值
+ */
+ Object format (Object value, String[] args, Cell cell, Workbook wb);
+}
diff --git a/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/utils/poi/ExcelUtil.java b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/utils/poi/ExcelUtil.java
new file mode 100644
index 0000000..69795db
--- /dev/null
+++ b/muyu-common/muyu-common-core/src/main/java/com/muyu/common/core/utils/poi/ExcelUtil.java
@@ -0,0 +1,1230 @@
+package com.muyu.common.core.utils.poi;
+
+import com.muyu.common.core.annotation.Excel;
+import com.muyu.common.core.annotation.Excel.ColumnType;
+import com.muyu.common.core.annotation.Excel.Type;
+import com.muyu.common.core.annotation.Excels;
+import com.muyu.common.core.exception.UtilException;
+import com.muyu.common.core.text.Convert;
+import com.muyu.common.core.utils.DateUtils;
+import com.muyu.common.core.utils.StringUtils;
+import com.muyu.common.core.utils.file.FileTypeUtils;
+import com.muyu.common.core.utils.file.ImageUtils;
+import com.muyu.common.core.utils.reflect.ReflectUtils;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.RegExUtils;
+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 org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.servlet.http.HttpServletResponse;
+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.*;
+import java.util.stream.Collectors;
+
+/**
+ * Excel相关处理
+ *
+ * @author muyu
+ */
+public class ExcelUtil {
+ public static final String FORMULA_REGEX_STR = "=|-|\\+|@";
+ public static final String[] FORMULA_STR = {"=", "-", "+", "@"};
+ /**
+ * Excel sheet最大行数,默认65536
+ */
+ public static final int sheetSize = 65536;
+ private static final Logger log = LoggerFactory.getLogger(ExcelUtil.class);
+ /**
+ * 数字格式
+ */
+ private static final DecimalFormat DOUBLE_FORMAT = new DecimalFormat("######0.00");
+ /**
+ * 实体对象
+ */
+ public Class clazz;
+ /**
+ * 需要排除列属性
+ */
+ public String[] excludeFields;
+ /**
+ * 工作表名称
+ */
+ private String sheetName;
+ /**
+ * 导出类型(EXPORT:导出数据;IMPORT:导入模板)
+ */
+ private Type type;
+ /**
+ * 工作薄对象
+ */
+ private Workbook wb;
+ /**
+ * 工作表对象
+ */
+ private Sheet sheet;
+ /**
+ * 样式列表
+ */
+ private Map styles;
+ /**
+ * 导入导出数据列表
+ */
+ private List list;
+ /**
+ * 注解列表
+ */
+ private List