初始化
commit
1b4628e52c
|
@ -0,0 +1,8 @@
|
||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="DiscordProjectSettings">
|
||||||
|
<option name="show" value="ASK" />
|
||||||
|
<option name="description" value="" />
|
||||||
|
</component>
|
||||||
|
</project>
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Encoding" defaultCharsetForPropertiesFiles="UTF-8">
|
||||||
|
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
|
||||||
|
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
|
||||||
|
<file url="PROJECT" charset="UTF-8" />
|
||||||
|
</component>
|
||||||
|
</project>
|
|
@ -0,0 +1,26 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||||
|
<component name="MavenProjectsManager">
|
||||||
|
<option name="originalFiles">
|
||||||
|
<list>
|
||||||
|
<option value="$PROJECT_DIR$/pom.xml" />
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
<component name="ProjectInspectionProfilesVisibleTreeState">
|
||||||
|
<entry key="Project Default">
|
||||||
|
<profile-state>
|
||||||
|
<expanded-state>
|
||||||
|
<State />
|
||||||
|
</expanded-state>
|
||||||
|
<selected-state>
|
||||||
|
<State>
|
||||||
|
<id>Ali-Check</id>
|
||||||
|
</State>
|
||||||
|
</selected-state>
|
||||||
|
</profile-state>
|
||||||
|
</entry>
|
||||||
|
</component>
|
||||||
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK" />
|
||||||
|
</project>
|
|
@ -0,0 +1,169 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<parent>
|
||||||
|
<groupId>com.muyu</groupId>
|
||||||
|
<artifactId>cloud-common</artifactId>
|
||||||
|
<version>3.6.3</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>cloud-common-core</artifactId>
|
||||||
|
|
||||||
|
<description>
|
||||||
|
cloud-common-core核心模块
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
|
||||||
|
<!-- SpringCloud Openfeign -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- SpringCloud Loadbalancer -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Spring Context Support -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-context-support</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Spring Web -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-web</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Transmittable ThreadLocal -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba</groupId>
|
||||||
|
<artifactId>transmittable-thread-local</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Pagehelper -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.pagehelper</groupId>
|
||||||
|
<artifactId>pagehelper-spring-boot-starter</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- mybatis - plus 依赖 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.baomidou</groupId>
|
||||||
|
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
|
||||||
|
<version>3.5.6</version>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>com.github.jsqlparser</groupId>
|
||||||
|
<artifactId>jsqlparser</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.mybatis</groupId>
|
||||||
|
<artifactId>mybatis</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mybatis</groupId>
|
||||||
|
<artifactId>mybatis</artifactId>
|
||||||
|
<version>3.5.16</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Hibernate Validator -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-validation</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Jackson -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-databind</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Alibaba Fastjson -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba.fastjson2</groupId>
|
||||||
|
<artifactId>fastjson2</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Jwt -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.jsonwebtoken</groupId>
|
||||||
|
<artifactId>jjwt</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Apache Lang3 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.commons</groupId>
|
||||||
|
<artifactId>commons-lang3</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Commons Io -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>commons-io</groupId>
|
||||||
|
<artifactId>commons-io</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- excel工具 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.poi</groupId>
|
||||||
|
<artifactId>poi-ooxml</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Java jakarta Servlet -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>jakarta.servlet</groupId>
|
||||||
|
<artifactId>jakarta.servlet-api</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Spring Aop 依赖 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-aop</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Java Specification Requests 标准库-->
|
||||||
|
<!-- <dependency>-->
|
||||||
|
<!-- <groupId>javax.annotation</groupId>-->
|
||||||
|
<!-- <artifactId>jsr250-api</artifactId>-->
|
||||||
|
<!-- </dependency>-->
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>javax.annotation</groupId>
|
||||||
|
<artifactId>javax.annotation-api</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- lombok -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- skywalking 日志收集 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.skywalking</groupId>
|
||||||
|
<artifactId>apm-toolkit-trace</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- skywalking 日志收集集成logback -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.skywalking</groupId>
|
||||||
|
<artifactId>apm-toolkit-logback-1.x</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.hutool</groupId>
|
||||||
|
<artifactId>hutool-all</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
|
@ -0,0 +1,176 @@
|
||||||
|
package com.muyu.common.core.annotation;
|
||||||
|
|
||||||
|
import com.muyu.common.core.utils.poi.ExcelHandlerAdapter;
|
||||||
|
import org.apache.poi.ss.usermodel.HorizontalAlignment;
|
||||||
|
import org.apache.poi.ss.usermodel.IndexedColors;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义导出Excel数据注解
|
||||||
|
*
|
||||||
|
* @author muyu
|
||||||
|
*/
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target(ElementType.FIELD)
|
||||||
|
public @interface Excel {
|
||||||
|
/**
|
||||||
|
* 导出时在excel中排序
|
||||||
|
*/
|
||||||
|
public int sort () default Integer.MAX_VALUE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出到Excel中的名字.
|
||||||
|
*/
|
||||||
|
public String name () default "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 日期格式, 如: yyyy-MM-dd
|
||||||
|
*/
|
||||||
|
public String dateFormat () default "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 读取内容转表达式 (如: 0=男,1=女,2=未知)
|
||||||
|
*/
|
||||||
|
public String readConverterExp () default "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分隔符,读取字符串组内容
|
||||||
|
*/
|
||||||
|
public String separator () default ",";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BigDecimal 精度 默认:-1(默认不开启BigDecimal格式化)
|
||||||
|
*/
|
||||||
|
public int scale () default -1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BigDecimal 舍入规则 默认:BigDecimal.ROUND_HALF_EVEN
|
||||||
|
*/
|
||||||
|
public int roundingMode () default BigDecimal.ROUND_HALF_EVEN;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出时在excel中每个列的高度
|
||||||
|
*/
|
||||||
|
public double height () default 14;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出时在excel中每个列的宽度
|
||||||
|
*/
|
||||||
|
public double width () default 16;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文字后缀,如% 90 变成90%
|
||||||
|
*/
|
||||||
|
public String suffix () default "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当值为空时,字段的默认值
|
||||||
|
*/
|
||||||
|
public String defaultValue () default "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提示信息
|
||||||
|
*/
|
||||||
|
public String prompt () default "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置只能选择不能输入的列内容.
|
||||||
|
*/
|
||||||
|
public String[] combo () default {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否需要纵向合并单元格,应对需求:含有list集合单元格)
|
||||||
|
*/
|
||||||
|
public boolean needMerge () default false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否导出数据,应对需求:有时我们需要导出一份模板,这是标题需要但内容需要用户手工填写.
|
||||||
|
*/
|
||||||
|
public boolean isExport () default true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 另一个类中的属性名称,支持多级获取,以小数点隔开
|
||||||
|
*/
|
||||||
|
public String targetAttr () default "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否自动统计数据,在最后追加一行统计数据总和
|
||||||
|
*/
|
||||||
|
public boolean isStatistics () default false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出类型(0数字 1字符串)
|
||||||
|
*/
|
||||||
|
public ColumnType cellType () default ColumnType.STRING;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出列头背景颜色
|
||||||
|
*/
|
||||||
|
public IndexedColors headerBackgroundColor () default IndexedColors.GREY_50_PERCENT;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出列头字体颜色
|
||||||
|
*/
|
||||||
|
public IndexedColors headerColor () default IndexedColors.WHITE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出单元格背景颜色
|
||||||
|
*/
|
||||||
|
public IndexedColors backgroundColor () default IndexedColors.WHITE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出单元格字体颜色
|
||||||
|
*/
|
||||||
|
public IndexedColors color () default IndexedColors.BLACK;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出字段对齐方式
|
||||||
|
*/
|
||||||
|
public HorizontalAlignment align () default HorizontalAlignment.CENTER;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义数据处理器
|
||||||
|
*/
|
||||||
|
public Class<?> handler () default ExcelHandlerAdapter.class;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义数据处理器参数
|
||||||
|
*/
|
||||||
|
public String[] args () default {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字段类型(0:导出导入;1:仅导出;2:仅导入)
|
||||||
|
*/
|
||||||
|
Type type () default Type.ALL;
|
||||||
|
|
||||||
|
public enum Type {
|
||||||
|
ALL(0), EXPORT(1), IMPORT(2);
|
||||||
|
private final int value;
|
||||||
|
|
||||||
|
Type (int value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int value () {
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ColumnType {
|
||||||
|
NUMERIC(0), STRING(1), IMAGE(2);
|
||||||
|
private final int value;
|
||||||
|
|
||||||
|
ColumnType (int value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int value () {
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package com.muyu.common.core.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Excel注解集
|
||||||
|
*
|
||||||
|
* @author muyu
|
||||||
|
*/
|
||||||
|
@Target(ElementType.FIELD)
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface Excels {
|
||||||
|
Excel[] value ();
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
package com.muyu.common.core.constant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 缓存常量信息
|
||||||
|
*
|
||||||
|
* @author muyu
|
||||||
|
*/
|
||||||
|
public class CacheConstants {
|
||||||
|
/**
|
||||||
|
* 缓存有效期,默认720(分钟)
|
||||||
|
*/
|
||||||
|
public final static long EXPIRATION = 720;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 缓存刷新时间,默认120(分钟)
|
||||||
|
*/
|
||||||
|
public final static long REFRESH_TIME = 120;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 密码最大错误次数
|
||||||
|
*/
|
||||||
|
public final static int PASSWORD_MAX_RETRY_COUNT = 5;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 密码锁定时间,默认10(分钟)
|
||||||
|
*/
|
||||||
|
public final static long PASSWORD_LOCK_TIME = 10;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 权限缓存前缀
|
||||||
|
*/
|
||||||
|
public final static String LOGIN_TOKEN_KEY = "login_tokens:";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证码 redis key
|
||||||
|
*/
|
||||||
|
public static final String CAPTCHA_CODE_KEY = "captcha_codes:";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 参数管理 cache key
|
||||||
|
*/
|
||||||
|
public static final String SYS_CONFIG_KEY = "sys_config:";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字典管理 cache key
|
||||||
|
*/
|
||||||
|
public static final String SYS_DICT_KEY = "sys_dict:";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录账户密码错误次数 redis key
|
||||||
|
*/
|
||||||
|
public static final String PWD_ERR_CNT_KEY = "pwd_err_cnt:";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录IP黑名单 cache key
|
||||||
|
*/
|
||||||
|
public static final String SYS_LOGIN_BLACKIPLIST = SYS_CONFIG_KEY + "sys.login.blackIPList";
|
||||||
|
}
|
|
@ -0,0 +1,134 @@
|
||||||
|
package com.muyu.common.core.constant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通用常量信息
|
||||||
|
*
|
||||||
|
* @author muyu
|
||||||
|
*/
|
||||||
|
public class Constants {
|
||||||
|
/**
|
||||||
|
* UTF-8 字符集
|
||||||
|
*/
|
||||||
|
public static final String UTF8 = "UTF-8";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GBK 字符集
|
||||||
|
*/
|
||||||
|
public static final String GBK = "GBK";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* www主域
|
||||||
|
*/
|
||||||
|
public static final String WWW = "www.";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RMI 远程方法调用
|
||||||
|
*/
|
||||||
|
public static final String LOOKUP_RMI = "rmi:";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LDAP 远程方法调用
|
||||||
|
*/
|
||||||
|
public static final String LOOKUP_LDAP = "ldap:";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LDAPS 远程方法调用
|
||||||
|
*/
|
||||||
|
public static final String LOOKUP_LDAPS = "ldaps:";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* http请求
|
||||||
|
*/
|
||||||
|
public static final String HTTP = "http://";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* https请求
|
||||||
|
*/
|
||||||
|
public static final String HTTPS = "https://";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 成功标记
|
||||||
|
*/
|
||||||
|
public static final Integer SUCCESS = 200;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 失败标记
|
||||||
|
*/
|
||||||
|
public static final Integer FAIL = 500;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录成功状态
|
||||||
|
*/
|
||||||
|
public static final String LOGIN_SUCCESS_STATUS = "0";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录失败状态
|
||||||
|
*/
|
||||||
|
public static final String LOGIN_FAIL_STATUS = "1";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录成功
|
||||||
|
*/
|
||||||
|
public static final String LOGIN_SUCCESS = "Success";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注销
|
||||||
|
*/
|
||||||
|
public static final String LOGOUT = "Logout";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注册
|
||||||
|
*/
|
||||||
|
public static final String REGISTER = "Register";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录失败
|
||||||
|
*/
|
||||||
|
public static final String LOGIN_FAIL = "Error";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当前记录起始索引
|
||||||
|
*/
|
||||||
|
public static final String PAGE_NUM = "pageNum";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 每页显示记录数
|
||||||
|
*/
|
||||||
|
public static final String PAGE_SIZE = "pageSize";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 排序列
|
||||||
|
*/
|
||||||
|
public static final String ORDER_BY_COLUMN = "orderByColumn";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 排序的方向 "desc" 或者 "asc".
|
||||||
|
*/
|
||||||
|
public static final String IS_ASC = "isAsc";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证码有效期(分钟)
|
||||||
|
*/
|
||||||
|
public static final long CAPTCHA_EXPIRATION = 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 资源映射路径 前缀
|
||||||
|
*/
|
||||||
|
public static final String RESOURCE_PREFIX = "/profile";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自动识别json对象白名单配置(仅允许解析的包名,范围越小越安全)
|
||||||
|
*/
|
||||||
|
public static final String[] JSON_WHITELIST_STR = {"org.springframework", "com.muyu"};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 定时任务白名单配置(仅允许访问的包名,如其他需要可以自行添加)
|
||||||
|
*/
|
||||||
|
public static final String[] JOB_WHITELIST_STR = {"com.muyu"};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 定时任务违规的字符
|
||||||
|
*/
|
||||||
|
public static final String[] JOB_ERROR_STR = {"java.net.URL", "javax.naming.InitialContext", "org.yaml.snakeyaml",
|
||||||
|
"org.springframework", "org.apache", "com.muyu.common.core.utils.file"};
|
||||||
|
}
|
|
@ -0,0 +1,186 @@
|
||||||
|
package com.muyu.common.core.constant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 代码生成通用常量
|
||||||
|
*
|
||||||
|
* @author muyu
|
||||||
|
*/
|
||||||
|
public class GenConstants {
|
||||||
|
/**
|
||||||
|
* 单表(增删改查)
|
||||||
|
*/
|
||||||
|
public static final String TPL_CRUD = "crud";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 树表(增删改查)
|
||||||
|
*/
|
||||||
|
public static final String TPL_TREE = "tree";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主子表(增删改查)
|
||||||
|
*/
|
||||||
|
public static final String TPL_SUB = "sub";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 树编码字段
|
||||||
|
*/
|
||||||
|
public static final String TREE_CODE = "treeCode";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 树父编码字段
|
||||||
|
*/
|
||||||
|
public static final String TREE_PARENT_CODE = "treeParentCode";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 树名称字段
|
||||||
|
*/
|
||||||
|
public static final String TREE_NAME = "treeName";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上级菜单ID字段
|
||||||
|
*/
|
||||||
|
public static final String PARENT_MENU_ID = "parentMenuId";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上级菜单名称字段
|
||||||
|
*/
|
||||||
|
public static final String PARENT_MENU_NAME = "parentMenuName";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据库字符串类型
|
||||||
|
*/
|
||||||
|
public static final String[] COLUMNTYPE_STR = {"char", "varchar", "nvarchar", "varchar2"};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据库文本类型
|
||||||
|
*/
|
||||||
|
public static final String[] COLUMNTYPE_TEXT = {"tinytext", "text", "mediumtext", "longtext"};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据库时间类型
|
||||||
|
*/
|
||||||
|
public static final String[] COLUMNTYPE_TIME = {"datetime", "time", "date", "timestamp"};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据库数字类型
|
||||||
|
*/
|
||||||
|
public static final String[] COLUMNTYPE_NUMBER = {"tinyint", "smallint", "mediumint", "int", "number", "integer",
|
||||||
|
"bigint", "float", "double", "decimal"};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 页面不需要编辑字段
|
||||||
|
*/
|
||||||
|
public static final String[] COLUMNNAME_NOT_EDIT = {"id", "create_by", "create_time", "del_flag"};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 页面不需要显示的列表字段
|
||||||
|
*/
|
||||||
|
public static final String[] COLUMNNAME_NOT_LIST = {"id", "create_by", "create_time", "del_flag", "update_by",
|
||||||
|
"update_time"};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 页面不需要查询字段
|
||||||
|
*/
|
||||||
|
public static final String[] COLUMNNAME_NOT_QUERY = {"id", "create_by", "create_time", "del_flag", "update_by",
|
||||||
|
"update_time", "remark"};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Entity基类字段
|
||||||
|
*/
|
||||||
|
public static final String[] BASE_ENTITY = {"createBy", "createTime", "updateBy", "updateTime", "remark"};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tree基类字段
|
||||||
|
*/
|
||||||
|
public static final String[] TREE_ENTITY = {"parentName", "parentId", "orderNum", "ancestors"};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文本框
|
||||||
|
*/
|
||||||
|
public static final String HTML_INPUT = "input";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文本域
|
||||||
|
*/
|
||||||
|
public static final String HTML_TEXTAREA = "textarea";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下拉框
|
||||||
|
*/
|
||||||
|
public static final String HTML_SELECT = "select";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 单选框
|
||||||
|
*/
|
||||||
|
public static final String HTML_RADIO = "radio";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 复选框
|
||||||
|
*/
|
||||||
|
public static final String HTML_CHECKBOX = "checkbox";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 日期控件
|
||||||
|
*/
|
||||||
|
public static final String HTML_DATETIME = "datetime";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 图片上传控件
|
||||||
|
*/
|
||||||
|
public static final String HTML_IMAGE_UPLOAD = "imageUpload";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件上传控件
|
||||||
|
*/
|
||||||
|
public static final String HTML_FILE_UPLOAD = "fileUpload";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 富文本控件
|
||||||
|
*/
|
||||||
|
public static final String HTML_EDITOR = "editor";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字符串类型
|
||||||
|
*/
|
||||||
|
public static final String TYPE_STRING = "String";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 整型
|
||||||
|
*/
|
||||||
|
public static final String TYPE_INTEGER = "Integer";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 长整型
|
||||||
|
*/
|
||||||
|
public static final String TYPE_LONG = "Long";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 浮点型
|
||||||
|
*/
|
||||||
|
public static final String TYPE_DOUBLE = "Double";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 高精度计算类型
|
||||||
|
*/
|
||||||
|
public static final String TYPE_BIGDECIMAL = "BigDecimal";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 时间类型
|
||||||
|
*/
|
||||||
|
public static final String TYPE_DATE = "Date";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 模糊查询
|
||||||
|
*/
|
||||||
|
public static final String QUERY_LIKE = "LIKE";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 相等查询
|
||||||
|
*/
|
||||||
|
public static final String QUERY_EQ = "EQ";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 需要
|
||||||
|
*/
|
||||||
|
public static final String REQUIRE = "1";
|
||||||
|
}
|
|
@ -0,0 +1,93 @@
|
||||||
|
package com.muyu.common.core.constant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回状态码
|
||||||
|
*
|
||||||
|
* @author muyu
|
||||||
|
*/
|
||||||
|
public class HttpStatus {
|
||||||
|
/**
|
||||||
|
* 操作成功
|
||||||
|
*/
|
||||||
|
public static final int SUCCESS = 200;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对象创建成功
|
||||||
|
*/
|
||||||
|
public static final int CREATED = 201;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求已经被接受
|
||||||
|
*/
|
||||||
|
public static final int ACCEPTED = 202;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 操作已经执行成功,但是没有返回数据
|
||||||
|
*/
|
||||||
|
public static final int NO_CONTENT = 204;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 资源已被移除
|
||||||
|
*/
|
||||||
|
public static final int MOVED_PERM = 301;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重定向
|
||||||
|
*/
|
||||||
|
public static final int SEE_OTHER = 303;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 资源没有被修改
|
||||||
|
*/
|
||||||
|
public static final int NOT_MODIFIED = 304;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 参数列表错误(缺少,格式不匹配)
|
||||||
|
*/
|
||||||
|
public static final int BAD_REQUEST = 400;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 未授权
|
||||||
|
*/
|
||||||
|
public static final int UNAUTHORIZED = 401;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 访问受限,授权过期
|
||||||
|
*/
|
||||||
|
public static final int FORBIDDEN = 403;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 资源,服务未找到
|
||||||
|
*/
|
||||||
|
public static final int NOT_FOUND = 404;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 不允许的http方法
|
||||||
|
*/
|
||||||
|
public static final int BAD_METHOD = 405;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 资源冲突,或者资源被锁
|
||||||
|
*/
|
||||||
|
public static final int CONFLICT = 409;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 不支持的数据,媒体类型
|
||||||
|
*/
|
||||||
|
public static final int UNSUPPORTED_TYPE = 415;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统内部错误
|
||||||
|
*/
|
||||||
|
public static final int ERROR = 500;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 接口未实现
|
||||||
|
*/
|
||||||
|
public static final int NOT_IMPLEMENTED = 501;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统警告消息
|
||||||
|
*/
|
||||||
|
public static final int WARN = 601;
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
package com.muyu.common.core.constant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务调度通用常量
|
||||||
|
*
|
||||||
|
* @author muyu
|
||||||
|
*/
|
||||||
|
public class ScheduleConstants {
|
||||||
|
public static final String TASK_CLASS_NAME = "TASK_CLASS_NAME";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行目标key
|
||||||
|
*/
|
||||||
|
public static final String TASK_PROPERTIES = "TASK_PROPERTIES";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认
|
||||||
|
*/
|
||||||
|
public static final String MISFIRE_DEFAULT = "0";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 立即触发执行
|
||||||
|
*/
|
||||||
|
public static final String MISFIRE_IGNORE_MISFIRES = "1";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 触发一次执行
|
||||||
|
*/
|
||||||
|
public static final String MISFIRE_FIRE_AND_PROCEED = "2";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 不触发立即执行
|
||||||
|
*/
|
||||||
|
public static final String MISFIRE_DO_NOTHING = "3";
|
||||||
|
|
||||||
|
public enum Status {
|
||||||
|
/**
|
||||||
|
* 正常
|
||||||
|
*/
|
||||||
|
NORMAL("0"),
|
||||||
|
/**
|
||||||
|
* 暂停
|
||||||
|
*/
|
||||||
|
PAUSE("1");
|
||||||
|
|
||||||
|
private String value;
|
||||||
|
|
||||||
|
private Status (String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getValue () {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
package com.muyu.common.core.constant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 权限相关通用常量
|
||||||
|
*
|
||||||
|
* @author muyu
|
||||||
|
*/
|
||||||
|
public class SecurityConstants {
|
||||||
|
/**
|
||||||
|
* 用户ID字段
|
||||||
|
*/
|
||||||
|
public static final String DETAILS_USER_ID = "user_id";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户名字段
|
||||||
|
*/
|
||||||
|
public static final String DETAILS_USERNAME = "username";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 授权信息字段
|
||||||
|
*/
|
||||||
|
public static final String AUTHORIZATION_HEADER = "authorization";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求来源
|
||||||
|
*/
|
||||||
|
public static final String FROM_SOURCE = "from-source";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 内部请求
|
||||||
|
*/
|
||||||
|
public static final String INNER = "inner";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户标识
|
||||||
|
*/
|
||||||
|
public static final String USER_KEY = "user_key";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录用户
|
||||||
|
*/
|
||||||
|
public static final String LOGIN_USER = "login_user";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 角色权限
|
||||||
|
*/
|
||||||
|
public static final String ROLE_PERMISSION = "role_permission";
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
package com.muyu.common.core.constant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务名称
|
||||||
|
*
|
||||||
|
* @author muyu
|
||||||
|
*/
|
||||||
|
public class ServiceNameConstants {
|
||||||
|
/**
|
||||||
|
* 认证服务的serviceid
|
||||||
|
*/
|
||||||
|
public static final String AUTH_SERVICE = "cloud-auth";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统模块的serviceid
|
||||||
|
*/
|
||||||
|
public static final String SYSTEM_SERVICE = "cloud-system";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件服务的serviceid
|
||||||
|
*/
|
||||||
|
public static final String FILE_SERVICE = "cloud-file";
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
package com.muyu.common.core.constant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Token的Key常量
|
||||||
|
*
|
||||||
|
* @author muyu
|
||||||
|
*/
|
||||||
|
public class TokenConstants {
|
||||||
|
/**
|
||||||
|
* 令牌自定义标识
|
||||||
|
*/
|
||||||
|
public static final String AUTHENTICATION = "Authorization";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 令牌前缀
|
||||||
|
*/
|
||||||
|
public static final String PREFIX = "Bearer ";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 令牌秘钥
|
||||||
|
*/
|
||||||
|
public final static String SECRET = "abcdefghijklmnsalieopadfaqawefwerstuvwxyz";
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,113 @@
|
||||||
|
package com.muyu.common.core.constant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户常量信息
|
||||||
|
*
|
||||||
|
* @author muyu
|
||||||
|
*/
|
||||||
|
public class UserConstants {
|
||||||
|
/**
|
||||||
|
* 平台内系统用户的唯一标志
|
||||||
|
*/
|
||||||
|
public static final String SYS_USER = "SYS_USER";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 正常状态
|
||||||
|
*/
|
||||||
|
public static final String NORMAL = "0";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 异常状态
|
||||||
|
*/
|
||||||
|
public static final String EXCEPTION = "1";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户封禁状态
|
||||||
|
*/
|
||||||
|
public static final String USER_DISABLE = "1";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 角色封禁状态
|
||||||
|
*/
|
||||||
|
public static final String ROLE_DISABLE = "1";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 部门正常状态
|
||||||
|
*/
|
||||||
|
public static final String DEPT_NORMAL = "0";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 部门停用状态
|
||||||
|
*/
|
||||||
|
public static final String DEPT_DISABLE = "1";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字典正常状态
|
||||||
|
*/
|
||||||
|
public static final String DICT_NORMAL = "0";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否为系统默认(是)
|
||||||
|
*/
|
||||||
|
public static final String YES = "Y";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否菜单外链(是)
|
||||||
|
*/
|
||||||
|
public static final String YES_FRAME = "0";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否菜单外链(否)
|
||||||
|
*/
|
||||||
|
public static final String NO_FRAME = "1";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 菜单类型(目录)
|
||||||
|
*/
|
||||||
|
public static final String TYPE_DIR = "M";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 菜单类型(菜单)
|
||||||
|
*/
|
||||||
|
public static final String TYPE_MENU = "C";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 菜单类型(按钮)
|
||||||
|
*/
|
||||||
|
public static final String TYPE_BUTTON = "F";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Layout组件标识
|
||||||
|
*/
|
||||||
|
public final static String LAYOUT = "Layout";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ParentView组件标识
|
||||||
|
*/
|
||||||
|
public final static String PARENT_VIEW = "ParentView";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* InnerLink组件标识
|
||||||
|
*/
|
||||||
|
public final static String INNER_LINK = "InnerLink";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验是否唯一的返回标识
|
||||||
|
*/
|
||||||
|
public final static boolean UNIQUE = true;
|
||||||
|
public final static boolean NOT_UNIQUE = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户名长度限制
|
||||||
|
*/
|
||||||
|
public static final int USERNAME_MIN_LENGTH = 2;
|
||||||
|
|
||||||
|
public static final int USERNAME_MAX_LENGTH = 20;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 密码长度限制
|
||||||
|
*/
|
||||||
|
public static final int PASSWORD_MIN_LENGTH = 5;
|
||||||
|
|
||||||
|
public static final int PASSWORD_MAX_LENGTH = 20;
|
||||||
|
}
|
|
@ -0,0 +1,83 @@
|
||||||
|
package com.muyu.common.core.context;
|
||||||
|
|
||||||
|
import com.alibaba.ttl.TransmittableThreadLocal;
|
||||||
|
import com.muyu.common.core.constant.SecurityConstants;
|
||||||
|
import com.muyu.common.core.text.Convert;
|
||||||
|
import com.muyu.common.core.utils.StringUtils;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前线程变量中的 用户id、用户名称、Token等信息
|
||||||
|
* 注意: 必须在网关通过请求头的方法传入,同时在HeaderInterceptor拦截器设置值。 否则这里无法获取
|
||||||
|
*
|
||||||
|
* @author muyu
|
||||||
|
*/
|
||||||
|
public class SecurityContextHolder {
|
||||||
|
private static final TransmittableThreadLocal<Map<String, Object>> THREAD_LOCAL = new TransmittableThreadLocal<>();
|
||||||
|
|
||||||
|
public static void set (String key, Object value) {
|
||||||
|
Map<String, Object> map = getLocalMap();
|
||||||
|
map.put(key, value == null ? StringUtils.EMPTY : value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String get (String key) {
|
||||||
|
Map<String, Object> map = getLocalMap();
|
||||||
|
return Convert.toStr(map.getOrDefault(key, StringUtils.EMPTY));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> T get (String key, Class<T> clazz) {
|
||||||
|
Map<String, Object> map = getLocalMap();
|
||||||
|
return StringUtils.cast(map.getOrDefault(key, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Map<String, Object> getLocalMap () {
|
||||||
|
Map<String, Object> map = THREAD_LOCAL.get();
|
||||||
|
if (map == null) {
|
||||||
|
map = new ConcurrentHashMap<String, Object>();
|
||||||
|
THREAD_LOCAL.set(map);
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setLocalMap (Map<String, Object> threadLocalMap) {
|
||||||
|
THREAD_LOCAL.set(threadLocalMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Long getUserId () {
|
||||||
|
return Convert.toLong(get(SecurityConstants.DETAILS_USER_ID), 0L);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setUserId (String account) {
|
||||||
|
set(SecurityConstants.DETAILS_USER_ID, account);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getUserName () {
|
||||||
|
return get(SecurityConstants.DETAILS_USERNAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setUserName (String username) {
|
||||||
|
set(SecurityConstants.DETAILS_USERNAME, username);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getUserKey () {
|
||||||
|
return get(SecurityConstants.USER_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setUserKey (String userKey) {
|
||||||
|
set(SecurityConstants.USER_KEY, userKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getPermission () {
|
||||||
|
return get(SecurityConstants.ROLE_PERMISSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setPermission (String permissions) {
|
||||||
|
set(SecurityConstants.ROLE_PERMISSION, permissions);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void remove () {
|
||||||
|
THREAD_LOCAL.remove();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,112 @@
|
||||||
|
package com.muyu.common.core.domain;
|
||||||
|
|
||||||
|
import com.muyu.common.core.constant.Constants;
|
||||||
|
import com.muyu.common.core.constant.HttpStatus;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 响应信息主体
|
||||||
|
*
|
||||||
|
* @author muyu
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class Result<T> implements Serializable {
|
||||||
|
/**
|
||||||
|
* 成功
|
||||||
|
*/
|
||||||
|
public static final int SUCCESS = Constants.SUCCESS;
|
||||||
|
/**
|
||||||
|
* 失败
|
||||||
|
*/
|
||||||
|
public static final int FAIL = Constants.FAIL;
|
||||||
|
/**
|
||||||
|
* 警告
|
||||||
|
*/
|
||||||
|
public static final int WARN = HttpStatus.WARN;
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
private int code;
|
||||||
|
|
||||||
|
private String msg;
|
||||||
|
|
||||||
|
private T data;
|
||||||
|
|
||||||
|
public static <T> Result<T> success () {
|
||||||
|
return restResult(null, SUCCESS, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Result<T> success (T data) {
|
||||||
|
return restResult(data, SUCCESS, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Result<T> success (T data, String msg) {
|
||||||
|
return restResult(data, SUCCESS, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Result<T> error () {
|
||||||
|
return restResult(null, FAIL, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Result<T> error (String msg) {
|
||||||
|
return restResult(null, FAIL, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Result<T> error (T data) {
|
||||||
|
return restResult(data, FAIL, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Result<T> error (T data, String msg) {
|
||||||
|
return restResult(data, FAIL, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Result<T> error (int code, String msg) {
|
||||||
|
return restResult(null, code, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static <T> Result<T> warn () {
|
||||||
|
return restResult(null, WARN, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Result<T> warn (String msg) {
|
||||||
|
return restResult(null, WARN, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Result<T> warn (T data) {
|
||||||
|
return restResult(data, WARN, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Result<T> warn (T data, String msg) {
|
||||||
|
return restResult(data, WARN, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Result<T> warn (int code, String msg) {
|
||||||
|
return restResult(null, code, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T> Result<T> restResult (T data, int code, String msg) {
|
||||||
|
return Result.<T>builder()
|
||||||
|
.code(code)
|
||||||
|
.data(data)
|
||||||
|
.msg(msg)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Boolean isError (Result<T> ret) {
|
||||||
|
return !isSuccess(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Boolean isSuccess (Result<T> ret) {
|
||||||
|
return Result.SUCCESS == ret.getCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 () {
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 错误明细,内部调试错误
|
||||||
|
* <p>
|
||||||
|
* 和 {@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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 () {
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 错误明细,内部调试错误
|
||||||
|
* <p>
|
||||||
|
* 和 {@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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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, ","));
|
||||||
|
}
|
||||||
|
}
|
|
@ -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, ","));
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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");
|
||||||
|
}
|
||||||
|
}
|
|
@ -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");
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
package com.muyu.common.core.feign;
|
||||||
|
|
||||||
|
import feign.Contract;
|
||||||
|
import org.springframework.cloud.openfeign.support.SpringMvcContract;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class FeginConfig {
|
||||||
|
// @Bean
|
||||||
|
// public Contract feignConfiguration() {
|
||||||
|
// return new feign.Contract.Default();
|
||||||
|
// }
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public Contract feignConfiguration() {
|
||||||
|
return new SpringMvcContract();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 {
|
||||||
|
/**
|
||||||
|
* 转换为字符串<br>
|
||||||
|
* 如果给定的值为null,或者转换失败,返回默认值<br>
|
||||||
|
* 转换失败不会报错
|
||||||
|
*
|
||||||
|
* @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();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转换为字符串<br>
|
||||||
|
* 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
|
||||||
|
* 转换失败不会报错
|
||||||
|
*
|
||||||
|
* @param value 被转换的值
|
||||||
|
*
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
public static String toStr (Object value) {
|
||||||
|
return toStr(value, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转换为字符<br>
|
||||||
|
* 如果给定的值为null,或者转换失败,返回默认值<br>
|
||||||
|
* 转换失败不会报错
|
||||||
|
*
|
||||||
|
* @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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转换为字符<br>
|
||||||
|
* 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
|
||||||
|
* 转换失败不会报错
|
||||||
|
*
|
||||||
|
* @param value 被转换的值
|
||||||
|
*
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
public static Character toChar (Object value) {
|
||||||
|
return toChar(value, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转换为byte<br>
|
||||||
|
* 如果给定的值为<code>null</code>,或者转换失败,返回默认值<br>
|
||||||
|
* 转换失败不会报错
|
||||||
|
*
|
||||||
|
* @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<br>
|
||||||
|
* 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
|
||||||
|
* 转换失败不会报错
|
||||||
|
*
|
||||||
|
* @param value 被转换的值
|
||||||
|
*
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
public static Byte toByte (Object value) {
|
||||||
|
return toByte(value, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转换为Short<br>
|
||||||
|
* 如果给定的值为<code>null</code>,或者转换失败,返回默认值<br>
|
||||||
|
* 转换失败不会报错
|
||||||
|
*
|
||||||
|
* @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<br>
|
||||||
|
* 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
|
||||||
|
* 转换失败不会报错
|
||||||
|
*
|
||||||
|
* @param value 被转换的值
|
||||||
|
*
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
public static Short toShort (Object value) {
|
||||||
|
return toShort(value, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转换为Number<br>
|
||||||
|
* 如果给定的值为空,或者转换失败,返回默认值<br>
|
||||||
|
* 转换失败不会报错
|
||||||
|
*
|
||||||
|
* @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<br>
|
||||||
|
* 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
|
||||||
|
* 转换失败不会报错
|
||||||
|
*
|
||||||
|
* @param value 被转换的值
|
||||||
|
*
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
public static Number toNumber (Object value) {
|
||||||
|
return toNumber(value, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转换为int<br>
|
||||||
|
* 如果给定的值为空,或者转换失败,返回默认值<br>
|
||||||
|
* 转换失败不会报错
|
||||||
|
*
|
||||||
|
* @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<br>
|
||||||
|
* 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
|
||||||
|
* 转换失败不会报错
|
||||||
|
*
|
||||||
|
* @param value 被转换的值
|
||||||
|
*
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
public static Integer toInt (Object value) {
|
||||||
|
return toInt(value, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转换为Integer数组<br>
|
||||||
|
*
|
||||||
|
* @param str 被转换的值
|
||||||
|
*
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
public static Integer[] toIntArray (String str) {
|
||||||
|
return toIntArray(",", str);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转换为Long数组<br>
|
||||||
|
*
|
||||||
|
* @param str 被转换的值
|
||||||
|
*
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
public static Long[] toLongArray (String str) {
|
||||||
|
return toLongArray(",", str);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转换为Integer数组<br>
|
||||||
|
*
|
||||||
|
* @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数组<br>
|
||||||
|
*
|
||||||
|
* @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数组<br>
|
||||||
|
*
|
||||||
|
* @param str 被转换的值
|
||||||
|
*
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
public static String[] toStrArray (String str) {
|
||||||
|
return toStrArray(",", str);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转换为String数组<br>
|
||||||
|
*
|
||||||
|
* @param split 分隔符
|
||||||
|
* @param str 被转换的值
|
||||||
|
*
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
public static String[] toStrArray (String split, String str) {
|
||||||
|
return str.split(split);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转换为long<br>
|
||||||
|
* 如果给定的值为空,或者转换失败,返回默认值<br>
|
||||||
|
* 转换失败不会报错
|
||||||
|
*
|
||||||
|
* @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<br>
|
||||||
|
* 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
|
||||||
|
* 转换失败不会报错
|
||||||
|
*
|
||||||
|
* @param value 被转换的值
|
||||||
|
*
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
public static Long toLong (Object value) {
|
||||||
|
return toLong(value, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转换为double<br>
|
||||||
|
* 如果给定的值为空,或者转换失败,返回默认值<br>
|
||||||
|
* 转换失败不会报错
|
||||||
|
*
|
||||||
|
* @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<br>
|
||||||
|
* 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
|
||||||
|
* 转换失败不会报错
|
||||||
|
*
|
||||||
|
* @param value 被转换的值
|
||||||
|
*
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
public static Double toDouble (Object value) {
|
||||||
|
return toDouble(value, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转换为Float<br>
|
||||||
|
* 如果给定的值为空,或者转换失败,返回默认值<br>
|
||||||
|
* 转换失败不会报错
|
||||||
|
*
|
||||||
|
* @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<br>
|
||||||
|
* 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
|
||||||
|
* 转换失败不会报错
|
||||||
|
*
|
||||||
|
* @param value 被转换的值
|
||||||
|
*
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
public static Float toFloat (Object value) {
|
||||||
|
return toFloat(value, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转换为boolean<br>
|
||||||
|
* String支持的值为:true、false、yes、ok、no,1,0 如果给定的值为空,或者转换失败,返回默认值<br>
|
||||||
|
* 转换失败不会报错
|
||||||
|
*
|
||||||
|
* @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<br>
|
||||||
|
* 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
|
||||||
|
* 转换失败不会报错
|
||||||
|
*
|
||||||
|
* @param value 被转换的值
|
||||||
|
*
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
public static Boolean toBool (Object value) {
|
||||||
|
return toBool(value, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转换为Enum对象<br>
|
||||||
|
* 如果给定的值为空,或者转换失败,返回默认值<br>
|
||||||
|
*
|
||||||
|
* @param clazz Enum的Class
|
||||||
|
* @param value 值
|
||||||
|
* @param defaultValue 默认值
|
||||||
|
*
|
||||||
|
* @return Enum
|
||||||
|
*/
|
||||||
|
public static <E extends Enum<E>> E toEnum (Class<E> 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对象<br>
|
||||||
|
* 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
|
||||||
|
*
|
||||||
|
* @param clazz Enum的Class
|
||||||
|
* @param value 值
|
||||||
|
*
|
||||||
|
* @return Enum
|
||||||
|
*/
|
||||||
|
public static <E extends Enum<E>> E toEnum (Class<E> clazz, Object value) {
|
||||||
|
return toEnum(clazz, value, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转换为BigInteger<br>
|
||||||
|
* 如果给定的值为空,或者转换失败,返回默认值<br>
|
||||||
|
* 转换失败不会报错
|
||||||
|
*
|
||||||
|
* @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<br>
|
||||||
|
* 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
|
||||||
|
* 转换失败不会报错
|
||||||
|
*
|
||||||
|
* @param value 被转换的值
|
||||||
|
*
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
public static BigInteger toBigInteger (Object value) {
|
||||||
|
return toBigInteger(value, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转换为BigDecimal<br>
|
||||||
|
* 如果给定的值为空,或者转换失败,返回默认值<br>
|
||||||
|
* 转换失败不会报错
|
||||||
|
*
|
||||||
|
* @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<br>
|
||||||
|
* 如果给定的值为空,或者转换失败,返回默认值<br>
|
||||||
|
* 转换失败不会报错
|
||||||
|
*
|
||||||
|
* @param value 被转换的值
|
||||||
|
*
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
public static BigDecimal toBigDecimal (Object value) {
|
||||||
|
return toBigDecimal(value, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将对象转为字符串<br>
|
||||||
|
* 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法
|
||||||
|
*
|
||||||
|
* @param obj 对象
|
||||||
|
*
|
||||||
|
* @return 字符串
|
||||||
|
*/
|
||||||
|
public static String utf8Str (Object obj) {
|
||||||
|
return str(obj, CharsetKit.CHARSET_UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将对象转为字符串<br>
|
||||||
|
* 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将对象转为字符串<br>
|
||||||
|
* 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<Character> 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<Character> 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("^整$", "零元整");
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 = '}';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化字符串<br>
|
||||||
|
* 此方法只是简单将占位符 {} 按照顺序替换为参数<br>
|
||||||
|
* 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可<br>
|
||||||
|
* 例:<br>
|
||||||
|
* 通常使用:format("this is {} for {}", "a", "b") -> this is a for b<br>
|
||||||
|
* 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a<br>
|
||||||
|
* 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b<br>
|
||||||
|
*
|
||||||
|
* @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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,165 @@
|
||||||
|
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.security.Keys;
|
||||||
|
import io.jsonwebtoken.security.SecureDigestAlgorithm;
|
||||||
|
|
||||||
|
import javax.crypto.SecretKey;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Jwt工具类
|
||||||
|
*
|
||||||
|
* @author muyu
|
||||||
|
*/
|
||||||
|
public class JwtUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加密算法
|
||||||
|
*/
|
||||||
|
private final static SecureDigestAlgorithm<SecretKey, SecretKey> ALGORITHM = Jwts.SIG.HS256;
|
||||||
|
/**
|
||||||
|
* 私钥 / 生成签名的时候使用的秘钥secret,一般可以从本地配置文件中读取,切记这个秘钥不能外露,只在服务端使用,在任何场景都不应该流露出去。
|
||||||
|
* 一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了。
|
||||||
|
* 应该大于等于 256位(长度32及以上的字符串),并且是随机的字符串
|
||||||
|
*/
|
||||||
|
private final static String secret = TokenConstants.SECRET;
|
||||||
|
/**
|
||||||
|
* 秘钥实例
|
||||||
|
*/
|
||||||
|
public static final SecretKey KEY = Keys.hmacShaKeyFor(secret.getBytes());
|
||||||
|
/**
|
||||||
|
* jwt签发者
|
||||||
|
*/
|
||||||
|
private final static String JWT_ISS = "MUYU";
|
||||||
|
/**
|
||||||
|
* jwt主题
|
||||||
|
*/
|
||||||
|
private final static String SUBJECT = "Peripherals";
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从数据声明生成令牌
|
||||||
|
*
|
||||||
|
* @param claims 数据声明
|
||||||
|
*
|
||||||
|
* @return 令牌
|
||||||
|
*/
|
||||||
|
public static String createToken (Map<String, Object> claims) {
|
||||||
|
return Jwts.builder()
|
||||||
|
// 设置头部信息header
|
||||||
|
.header().add("typ", "JWT").add("alg", "HS256").and()
|
||||||
|
// 设置自定义负载信息payload
|
||||||
|
.claims(claims)
|
||||||
|
// 签发时间
|
||||||
|
.issuedAt(new Date())
|
||||||
|
// 主题
|
||||||
|
.subject(SUBJECT)
|
||||||
|
// 签发者
|
||||||
|
.issuer(JWT_ISS)
|
||||||
|
// 签名
|
||||||
|
.signWith(KEY, ALGORITHM)
|
||||||
|
.compact();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从令牌中获取数据声明
|
||||||
|
*
|
||||||
|
* @param token 令牌
|
||||||
|
*
|
||||||
|
* @return 数据声明
|
||||||
|
*/
|
||||||
|
public static Claims parseToken (String token) {
|
||||||
|
return Jwts.parser()
|
||||||
|
.verifyWith(KEY)
|
||||||
|
.build()
|
||||||
|
.parseSignedClaims(token)
|
||||||
|
.getPayload();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据令牌获取用户标识
|
||||||
|
*
|
||||||
|
* @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), "");
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,289 @@
|
||||||
|
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 jakarta.servlet.ServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import jakarta.servlet.http.HttpSession;
|
||||||
|
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 java.io.IOException;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.net.URLDecoder;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 客户端工具类
|
||||||
|
*
|
||||||
|
* @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<String, String[]> getParams (ServletRequest request) {
|
||||||
|
final Map<String, String[]> map = request.getParameterMap();
|
||||||
|
return Collections.unmodifiableMap(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得所有请求参数
|
||||||
|
*
|
||||||
|
* @param request 请求对象{@link ServletRequest}
|
||||||
|
*
|
||||||
|
* @return Map
|
||||||
|
*/
|
||||||
|
public static Map<String, String> getParamMap (ServletRequest request) {
|
||||||
|
Map<String, String> params = new HashMap<>();
|
||||||
|
for (Map.Entry<String, String[]> entry : getParams(request).entrySet()) {
|
||||||
|
params.put(entry.getKey(), StringUtils.join(entry.getValue(), ","));
|
||||||
|
}
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取request
|
||||||
|
*/
|
||||||
|
public static HttpServletRequest getRequest () {
|
||||||
|
try {
|
||||||
|
return Objects.requireNonNull(getRequestAttributes()).getRequest();
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取response
|
||||||
|
*/
|
||||||
|
public static HttpServletResponse getResponse () {
|
||||||
|
try {
|
||||||
|
return Objects.requireNonNull(getRequestAttributes()).getResponse();
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取session
|
||||||
|
*/
|
||||||
|
public static HttpSession getSession () {
|
||||||
|
return Objects.requireNonNull(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<String, String> getHeaders (HttpServletRequest request) {
|
||||||
|
Map<String, String> map = new LinkedCaseInsensitiveMap<>();
|
||||||
|
Enumeration<String> enumeration = request.getHeaderNames();
|
||||||
|
if (enumeration != null) {
|
||||||
|
while (enumeration.hasMoreElements()) {
|
||||||
|
String key = enumeration.nextElement();
|
||||||
|
String value = request.getHeader(key);
|
||||||
|
map.put(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将字符串渲染到客户端
|
||||||
|
*
|
||||||
|
* @param response 渲染对象
|
||||||
|
* @param string 待渲染的字符串
|
||||||
|
*/
|
||||||
|
public static void renderString (HttpServletResponse response, String string) {
|
||||||
|
try {
|
||||||
|
response.setStatus(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) {
|
||||||
|
return URLDecoder.decode(str, StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置webflux模型响应
|
||||||
|
*
|
||||||
|
* @param response ServerHttpResponse
|
||||||
|
* @param value 响应内容
|
||||||
|
*
|
||||||
|
* @return Mono<Void>
|
||||||
|
*/
|
||||||
|
public static Mono<Void> webFluxResponseWriter (ServerHttpResponse response, Object value) {
|
||||||
|
return webFluxResponseWriter(response, HttpStatus.OK, value, Result.FAIL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置webflux模型响应
|
||||||
|
*
|
||||||
|
* @param response ServerHttpResponse
|
||||||
|
* @param code 响应状态码
|
||||||
|
* @param value 响应内容
|
||||||
|
*
|
||||||
|
* @return Mono<Void>
|
||||||
|
*/
|
||||||
|
public static Mono<Void> webFluxResponseWriter (ServerHttpResponse response, Object value, int code) {
|
||||||
|
return webFluxResponseWriter(response, HttpStatus.OK, value, code);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置webflux模型响应
|
||||||
|
*
|
||||||
|
* @param response ServerHttpResponse
|
||||||
|
* @param status http状态码
|
||||||
|
* @param code 响应状态码
|
||||||
|
* @param value 响应内容
|
||||||
|
*
|
||||||
|
* @return Mono<Void>
|
||||||
|
*/
|
||||||
|
public static Mono<Void> webFluxResponseWriter (ServerHttpResponse response, HttpStatus status, Object value, int code) {
|
||||||
|
return webFluxResponseWriter(response, MediaType.APPLICATION_JSON_VALUE, status, value, code);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置webflux模型响应
|
||||||
|
*
|
||||||
|
* @param response ServerHttpResponse
|
||||||
|
* @param contentType content-type
|
||||||
|
* @param status http状态码
|
||||||
|
* @param code 响应状态码
|
||||||
|
* @param value 响应内容
|
||||||
|
*
|
||||||
|
* @return Mono<Void>
|
||||||
|
*/
|
||||||
|
public static Mono<Void> webFluxResponseWriter (ServerHttpResponse response, String contentType, HttpStatus status, Object value, int code) {
|
||||||
|
response.setStatusCode(status);
|
||||||
|
response.getHeaders().add(HttpHeaders.CONTENT_TYPE, contentType);
|
||||||
|
Result<?> result = Result.error(code, value.toString());
|
||||||
|
DataBuffer dataBuffer = response.bufferFactory().wrap(JSON.toJSONString(result).getBytes());
|
||||||
|
return response.writeWith(Mono.just(dataBuffer));
|
||||||
|
}
|
||||||
|
}
|
|
@ -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> T getBean (String name) throws BeansException {
|
||||||
|
return (T) beanFactory.getBean(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取类型为requiredType的对象
|
||||||
|
*
|
||||||
|
* @param clz
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*
|
||||||
|
* @throws org.springframework.beans.BeansException
|
||||||
|
*/
|
||||||
|
public static <T> T getBean (Class<T> 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> T getAopProxy (T invoker) {
|
||||||
|
return (T) AopContext.currentProxy();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void postProcessBeanFactory (ConfigurableListableBeanFactory beanFactory) throws BeansException {
|
||||||
|
SpringUtils.beanFactory = beanFactory;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化文本, {} 表示占位符<br>
|
||||||
|
* 此方法只是简单将占位符 {} 按照顺序替换为参数<br>
|
||||||
|
* 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可<br>
|
||||||
|
* 例:<br>
|
||||||
|
* 通常使用:format("this is {} for {}", "a", "b") -> this is a for b<br>
|
||||||
|
* 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a<br>
|
||||||
|
* 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b<br>
|
||||||
|
*
|
||||||
|
* @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<String> 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<String> 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> 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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<Method> getSetterMethods (Object obj) {
|
||||||
|
// setter方法列表
|
||||||
|
List<Method> setterMethods = new ArrayList<Method>();
|
||||||
|
|
||||||
|
// 获取所有方法
|
||||||
|
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<Method> getGetterMethods (Object obj) {
|
||||||
|
// getter方法列表
|
||||||
|
List<Method> getterMethods = new ArrayList<Method>();
|
||||||
|
// 获取所有方法
|
||||||
|
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方法名中的属性名是否相等。<br>
|
||||||
|
* 如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));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
package com.muyu.common.core.utils.bean;
|
||||||
|
|
||||||
|
import jakarta.validation.ConstraintViolation;
|
||||||
|
import jakarta.validation.ConstraintViolationException;
|
||||||
|
import jakarta.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<ConstraintViolation<Object>> constraintViolations = validator.validate(object, groups);
|
||||||
|
if (!constraintViolations.isEmpty()) {
|
||||||
|
throw new ConstraintViolationException(constraintViolations);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 {
|
||||||
|
/**
|
||||||
|
* 获取文件类型
|
||||||
|
* <p>
|
||||||
|
* 例如: muyu.txt, 返回: txt
|
||||||
|
*
|
||||||
|
* @param file 文件名
|
||||||
|
*
|
||||||
|
* @return 后缀(不含".")
|
||||||
|
*/
|
||||||
|
public static String getFileType (File file) {
|
||||||
|
if (null == file) {
|
||||||
|
return StringUtils.EMPTY;
|
||||||
|
}
|
||||||
|
return getFileType(file.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取文件类型
|
||||||
|
* <p>
|
||||||
|
* 例如: 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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.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)文件分隔符<br>
|
||||||
|
* 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");
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 = "<script>alert(1);</script>";
|
||||||
|
String escape = EscapeUtil.escape(html);
|
||||||
|
// String html = "<scr<script>ipt>alert(\"XSS\")</scr<script>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));
|
||||||
|
}
|
||||||
|
}
|
|
@ -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("&#x([0-9a-f]+);?");
|
||||||
|
private static final Pattern P_ENCODE = Pattern.compile("%([0-9a-f]{2});?");
|
||||||
|
private static final Pattern P_VALID_ENTITIES = Pattern.compile("&([^&;]*)(?=(;|&|$))");
|
||||||
|
private static final Pattern P_VALID_QUOTES = Pattern.compile("(>|^)([^<]+?)(<|$)", Pattern.DOTALL);
|
||||||
|
private static final Pattern P_END_ARROW = Pattern.compile("^>");
|
||||||
|
private static final Pattern P_BODY_TO_END = Pattern.compile("<([^>]*?)(?=<|$)");
|
||||||
|
private static final Pattern P_XML_CONTENT = Pattern.compile("(^|>)([^<]*?)(?=>)");
|
||||||
|
private static final Pattern P_STRAY_LEFT_ARROW = Pattern.compile("<([^>]*?)(?=<|$)");
|
||||||
|
private static final Pattern P_STRAY_RIGHT_ARROW = Pattern.compile("(^|>)([^<]*?)(?=>)");
|
||||||
|
private static final Pattern P_AMP = Pattern.compile("&");
|
||||||
|
private static final Pattern P_QUOTE = Pattern.compile("\"");
|
||||||
|
private static final Pattern P_LEFT_ARROW = Pattern.compile("<");
|
||||||
|
private static final Pattern P_RIGHT_ARROW = Pattern.compile(">");
|
||||||
|
private static final Pattern P_BOTH_ARROWS = Pattern.compile("<>");
|
||||||
|
|
||||||
|
// @xxx could grow large... maybe use sesat's ReferenceMap
|
||||||
|
private static final ConcurrentMap<String, Pattern> P_REMOVE_PAIR_BLANKS = new ConcurrentHashMap<>();
|
||||||
|
private static final ConcurrentMap<String, Pattern> P_REMOVE_SELF_BLANKS = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set of allowed html elements, along with allowed attributes for each element
|
||||||
|
**/
|
||||||
|
private final Map<String, List<String>> vAllowed;
|
||||||
|
/**
|
||||||
|
* counts of open tags for each (allowable) html element
|
||||||
|
**/
|
||||||
|
private final Map<String, Integer> vTagCounts = new HashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* html elements which must always be self-closing (e.g. "<img />")
|
||||||
|
**/
|
||||||
|
private final String[] vSelfClosingTags;
|
||||||
|
/**
|
||||||
|
* html elements which must always have separate opening and closing tags (e.g. "<b></b>")
|
||||||
|
**/
|
||||||
|
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. "<b></b>" or "<b />")
|
||||||
|
**/
|
||||||
|
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. "<b text </b>"
|
||||||
|
* becomes "<b> text </b>"). If set to false, unbalanced angle brackets will be html escaped.
|
||||||
|
*/
|
||||||
|
private final boolean alwaysMakeTags;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default constructor.
|
||||||
|
*/
|
||||||
|
public HTMLFilter () {
|
||||||
|
vAllowed = new HashMap<>();
|
||||||
|
|
||||||
|
final ArrayList<String> a_atts = new ArrayList<>();
|
||||||
|
a_atts.add("href");
|
||||||
|
a_atts.add("target");
|
||||||
|
vAllowed.put("a", a_atts);
|
||||||
|
|
||||||
|
final ArrayList<String> img_atts = new ArrayList<>();
|
||||||
|
img_atts.add("src");
|
||||||
|
img_atts.add("width");
|
||||||
|
img_atts.add("height");
|
||||||
|
img_atts.add("alt");
|
||||||
|
vAllowed.put("img", img_atts);
|
||||||
|
|
||||||
|
final ArrayList<String> no_atts = new ArrayList<>();
|
||||||
|
vAllowed.put("b", no_atts);
|
||||||
|
vAllowed.put("strong", no_atts);
|
||||||
|
vAllowed.put("i", no_atts);
|
||||||
|
vAllowed.put("em", no_atts);
|
||||||
|
|
||||||
|
vSelfClosingTags = new String[]{"img"};
|
||||||
|
vNeedClosingTags = new String[]{"a", "b", "strong", "i", "em"};
|
||||||
|
vDisallowed = new String[]{};
|
||||||
|
vAllowedProtocols = new String[]{"http", "mailto", "https"}; // no ftp.
|
||||||
|
vProtocolAtts = new String[]{"src", "href"};
|
||||||
|
vRemoveBlanks = new String[]{"a", "b", "strong", "i", "em"};
|
||||||
|
vAllowedEntities = new String[]{"amp", "gt", "lt", "quot"};
|
||||||
|
stripComment = true;
|
||||||
|
encodeQuotes = true;
|
||||||
|
alwaysMakeTags = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map-parameter configurable constructor.
|
||||||
|
*
|
||||||
|
* @param conf map containing configuration. keys match field names.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public HTMLFilter (final Map<String, Object> conf) {
|
||||||
|
|
||||||
|
assert conf.containsKey("vAllowed") : "configuration requires vAllowed";
|
||||||
|
assert conf.containsKey("vSelfClosingTags") : "configuration requires vSelfClosingTags";
|
||||||
|
assert conf.containsKey("vNeedClosingTags") : "configuration requires vNeedClosingTags";
|
||||||
|
assert conf.containsKey("vDisallowed") : "configuration requires vDisallowed";
|
||||||
|
assert conf.containsKey("vAllowedProtocols") : "configuration requires vAllowedProtocols";
|
||||||
|
assert conf.containsKey("vProtocolAtts") : "configuration requires vProtocolAtts";
|
||||||
|
assert conf.containsKey("vRemoveBlanks") : "configuration requires vRemoveBlanks";
|
||||||
|
assert conf.containsKey("vAllowedEntities") : "configuration requires vAllowedEntities";
|
||||||
|
|
||||||
|
vAllowed = Collections.unmodifiableMap((HashMap<String, List<String>>) conf.get("vAllowed"));
|
||||||
|
vSelfClosingTags = (String[]) conf.get("vSelfClosingTags");
|
||||||
|
vNeedClosingTags = (String[]) conf.get("vNeedClosingTags");
|
||||||
|
vDisallowed = (String[]) conf.get("vDisallowed");
|
||||||
|
vAllowedProtocols = (String[]) conf.get("vAllowedProtocols");
|
||||||
|
vProtocolAtts = (String[]) conf.get("vProtocolAtts");
|
||||||
|
vRemoveBlanks = (String[]) conf.get("vRemoveBlanks");
|
||||||
|
vAllowedEntities = (String[]) conf.get("vAllowedEntities");
|
||||||
|
stripComment = conf.containsKey("stripComment") ? (Boolean) conf.get("stripComment") : true;
|
||||||
|
encodeQuotes = conf.containsKey("encodeQuotes") ? (Boolean) conf.get("encodeQuotes") : true;
|
||||||
|
alwaysMakeTags = conf.containsKey("alwaysMakeTags") ? (Boolean) conf.get("alwaysMakeTags") : true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// 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("<!--" + htmlSpecialChars(match) + "-->"));
|
||||||
|
}
|
||||||
|
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<String> paramNames = new ArrayList<>();
|
||||||
|
final List<String> paramValues = new ArrayList<>();
|
||||||
|
while (m2.find()) {
|
||||||
|
paramNames.add(m2.group(1)); // ([a-z0-9]+)
|
||||||
|
paramValues.add(m2.group(3)); // (.*?)
|
||||||
|
}
|
||||||
|
while (m3.find()) {
|
||||||
|
paramNames.add(m3.group(1)); // ([a-z0-9]+)
|
||||||
|
paramValues.add(m3.group(3)); // ([^\"\\s']+)
|
||||||
|
}
|
||||||
|
|
||||||
|
String paramName, paramValue;
|
||||||
|
for (int ii = 0 ; ii < paramNames.size() ; ii++) {
|
||||||
|
paramName = paramNames.get(ii).toLowerCase();
|
||||||
|
paramValue = paramValues.get(ii);
|
||||||
|
|
||||||
|
// debug( "paramName='" + paramName + "'" );
|
||||||
|
// debug( "paramValue='" + paramValue + "'" );
|
||||||
|
// debug( "allowed? " + vAllowed.get( name ).contains( paramName ) );
|
||||||
|
|
||||||
|
if (allowedAttribute(name, paramName)) {
|
||||||
|
if (inArray(paramName, vProtocolAtts)) {
|
||||||
|
paramValue = processParamProtocol(paramValue);
|
||||||
|
}
|
||||||
|
params.append(' ').append(paramName).append("=\\\"").append(paramValue).append("\\\"");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inArray(name, vSelfClosingTags)) {
|
||||||
|
ending = " /";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inArray(name, vNeedClosingTags)) {
|
||||||
|
ending = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ending == null || ending.length() < 1) {
|
||||||
|
if (vTagCounts.containsKey(name)) {
|
||||||
|
vTagCounts.put(name, vTagCounts.get(name) + 1);
|
||||||
|
} else {
|
||||||
|
vTagCounts.put(name, 1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ending = " /";
|
||||||
|
}
|
||||||
|
return "<" + name + params + ending + ">";
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// comments
|
||||||
|
m = P_COMMENT.matcher(s);
|
||||||
|
if (!stripComment && m.find()) {
|
||||||
|
return "<" + m.group() + ">";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private String processParamProtocol (String s) {
|
||||||
|
s = decodeEntities(s);
|
||||||
|
final Matcher m = P_PROTOCOL.matcher(s);
|
||||||
|
if (m.find()) {
|
||||||
|
final String protocol = m.group(1);
|
||||||
|
if (!inArray(protocol, vAllowedProtocols)) {
|
||||||
|
// bad protocol, turn into local anchor link instead
|
||||||
|
s = "#" + s.substring(protocol.length() + 1);
|
||||||
|
if (s.startsWith("#//")) {
|
||||||
|
s = "#" + s.substring(3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String decodeEntities (String s) {
|
||||||
|
StringBuffer buf = new StringBuffer();
|
||||||
|
|
||||||
|
Matcher m = P_ENTITY.matcher(s);
|
||||||
|
while (m.find()) {
|
||||||
|
final String match = m.group(1);
|
||||||
|
final int decimal = Integer.decode(match).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));
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 jakarta.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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,324 @@
|
||||||
|
package com.muyu.common.core.utils.reflect;
|
||||||
|
|
||||||
|
import com.muyu.common.core.text.Convert;
|
||||||
|
import com.muyu.common.core.utils.DateUtils;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
|
import org.apache.poi.ss.usermodel.DateUtil;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.lang.reflect.*;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 反射工具类. 提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class, 被AOP过的真实类等工具函数.
|
||||||
|
*
|
||||||
|
* @author muyu
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
public class ReflectUtils {
|
||||||
|
private static final String SETTER_PREFIX = "set";
|
||||||
|
|
||||||
|
private static final String GETTER_PREFIX = "get";
|
||||||
|
|
||||||
|
private static final String CGLIB_CLASS_SEPARATOR = "$$";
|
||||||
|
|
||||||
|
private static Logger logger = LoggerFactory.getLogger(ReflectUtils.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 调用Getter方法.
|
||||||
|
* 支持多级,如:对象名.对象名.方法
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static <E> E invokeGetter (Object obj, String propertyName) {
|
||||||
|
Object object = obj;
|
||||||
|
for (String name : StringUtils.split(propertyName, ".")) {
|
||||||
|
String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(name);
|
||||||
|
object = invokeMethod(object, getterMethodName, new Class[]{}, new Object[]{});
|
||||||
|
}
|
||||||
|
return (E) object;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 调用Setter方法, 仅匹配方法名。
|
||||||
|
* 支持多级,如:对象名.对象名.方法
|
||||||
|
*/
|
||||||
|
public static <E> void invokeSetter (Object obj, String propertyName, E value) {
|
||||||
|
Object object = obj;
|
||||||
|
String[] names = StringUtils.split(propertyName, ".");
|
||||||
|
for (int i = 0 ; i < names.length ; i++) {
|
||||||
|
if (i < names.length - 1) {
|
||||||
|
String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(names[i]);
|
||||||
|
object = invokeMethod(object, getterMethodName, new Class[]{}, new Object[]{});
|
||||||
|
} else {
|
||||||
|
String setterMethodName = SETTER_PREFIX + StringUtils.capitalize(names[i]);
|
||||||
|
invokeMethodByName(object, setterMethodName, new Object[]{value});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 直接读取对象属性值, 无视private/protected修饰符, 不经过getter函数.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static <E> E getFieldValue (final Object obj, final String fieldName) {
|
||||||
|
Field field = getAccessibleField(obj, fieldName);
|
||||||
|
if (field == null) {
|
||||||
|
logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 ");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
E result = null;
|
||||||
|
try {
|
||||||
|
result = (E) field.get(obj);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
logger.error("不可能抛出的异常{}", e.getMessage());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 直接设置对象属性值, 无视private/protected修饰符, 不经过setter函数.
|
||||||
|
*/
|
||||||
|
public static <E> void setFieldValue (final Object obj, final String fieldName, final E value) {
|
||||||
|
Field field = getAccessibleField(obj, fieldName);
|
||||||
|
if (field == null) {
|
||||||
|
// throw new IllegalArgumentException("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 ");
|
||||||
|
logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 ");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
field.set(obj, value);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
logger.error("不可能抛出的异常: {}", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 直接调用对象方法, 无视private/protected修饰符.
|
||||||
|
* 用于一次性调用的情况,否则应使用getAccessibleMethod()函数获得Method后反复调用.
|
||||||
|
* 同时匹配方法名+参数类型,
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static <E> E invokeMethod (final Object obj, final String methodName, final Class<?>[] parameterTypes,
|
||||||
|
final Object[] args) {
|
||||||
|
if (obj == null || methodName == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Method method = getAccessibleMethod(obj, methodName, parameterTypes);
|
||||||
|
if (method == null) {
|
||||||
|
logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 ");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return (E) method.invoke(obj, args);
|
||||||
|
} catch (Exception e) {
|
||||||
|
String msg = "method: " + method + ", obj: " + obj + ", args: " + args + "";
|
||||||
|
throw convertReflectionExceptionToUnchecked(msg, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 直接调用对象方法, 无视private/protected修饰符,
|
||||||
|
* 用于一次性调用的情况,否则应使用getAccessibleMethodByName()函数获得Method后反复调用.
|
||||||
|
* 只匹配函数名,如果有多个同名函数调用第一个。
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static <E> E invokeMethodByName (final Object obj, final String methodName, final Object[] args) {
|
||||||
|
Method method = getAccessibleMethodByName(obj, methodName, args.length);
|
||||||
|
if (method == null) {
|
||||||
|
// 如果为空不报错,直接返回空。
|
||||||
|
logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 ");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
// 类型转换(将参数数据类型转换为目标方法参数类型)
|
||||||
|
Class<?>[] cs = method.getParameterTypes();
|
||||||
|
for (int i = 0 ; i < cs.length ; i++) {
|
||||||
|
if (args[i] != null && !args[i].getClass().equals(cs[i])) {
|
||||||
|
if (cs[i] == String.class) {
|
||||||
|
args[i] = Convert.toStr(args[i]);
|
||||||
|
if (StringUtils.endsWith((String) args[i], ".0")) {
|
||||||
|
args[i] = StringUtils.substringBefore((String) args[i], ".0");
|
||||||
|
}
|
||||||
|
} else if (cs[i] == Integer.class) {
|
||||||
|
args[i] = Convert.toInt(args[i]);
|
||||||
|
} else if (cs[i] == Long.class) {
|
||||||
|
args[i] = Convert.toLong(args[i]);
|
||||||
|
} else if (cs[i] == Double.class) {
|
||||||
|
args[i] = Convert.toDouble(args[i]);
|
||||||
|
} else if (cs[i] == Float.class) {
|
||||||
|
args[i] = Convert.toFloat(args[i]);
|
||||||
|
} else if (cs[i] == Date.class) {
|
||||||
|
if (args[i] instanceof String) {
|
||||||
|
args[i] = DateUtils.parseDate(args[i]);
|
||||||
|
} else {
|
||||||
|
args[i] = DateUtil.getJavaDate((Double) args[i]);
|
||||||
|
}
|
||||||
|
} else if (cs[i] == boolean.class || cs[i] == Boolean.class) {
|
||||||
|
args[i] = Convert.toBool(args[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (E) method.invoke(obj, args);
|
||||||
|
} catch (Exception e) {
|
||||||
|
String msg = "method: " + method + ", obj: " + obj + ", args: " + args + "";
|
||||||
|
throw convertReflectionExceptionToUnchecked(msg, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 循环向上转型, 获取对象的DeclaredField, 并强制设置为可访问.
|
||||||
|
* 如向上转型到Object仍无法找到, 返回null.
|
||||||
|
*/
|
||||||
|
public static Field getAccessibleField (final Object obj, final String fieldName) {
|
||||||
|
// 为空不报错。直接返回 null
|
||||||
|
if (obj == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Validate.notBlank(fieldName, "fieldName can't be blank");
|
||||||
|
for (Class<?> superClass = obj.getClass() ; superClass != Object.class ; superClass = superClass.getSuperclass()) {
|
||||||
|
try {
|
||||||
|
Field field = superClass.getDeclaredField(fieldName);
|
||||||
|
makeAccessible(field);
|
||||||
|
return field;
|
||||||
|
} catch (NoSuchFieldException e) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问.
|
||||||
|
* 如向上转型到Object仍无法找到, 返回null.
|
||||||
|
* 匹配函数名+参数类型。
|
||||||
|
* 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args)
|
||||||
|
*/
|
||||||
|
public static Method getAccessibleMethod (final Object obj, final String methodName,
|
||||||
|
final Class<?>... parameterTypes) {
|
||||||
|
// 为空不报错。直接返回 null
|
||||||
|
if (obj == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Validate.notBlank(methodName, "methodName can't be blank");
|
||||||
|
for (Class<?> searchType = obj.getClass() ; searchType != Object.class ; searchType = searchType.getSuperclass()) {
|
||||||
|
try {
|
||||||
|
Method method = searchType.getDeclaredMethod(methodName, parameterTypes);
|
||||||
|
makeAccessible(method);
|
||||||
|
return method;
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问.
|
||||||
|
* 如向上转型到Object仍无法找到, 返回null.
|
||||||
|
* 只匹配函数名。
|
||||||
|
* 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args)
|
||||||
|
*/
|
||||||
|
public static Method getAccessibleMethodByName (final Object obj, final String methodName, int argsNum) {
|
||||||
|
// 为空不报错。直接返回 null
|
||||||
|
if (obj == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Validate.notBlank(methodName, "methodName can't be blank");
|
||||||
|
for (Class<?> searchType = obj.getClass() ; searchType != Object.class ; searchType = searchType.getSuperclass()) {
|
||||||
|
Method[] methods = searchType.getDeclaredMethods();
|
||||||
|
for (Method method : methods) {
|
||||||
|
if (method.getName().equals(methodName) && method.getParameterTypes().length == argsNum) {
|
||||||
|
makeAccessible(method);
|
||||||
|
return method;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 改变private/protected的方法为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。
|
||||||
|
*/
|
||||||
|
public static void makeAccessible (Method method) {
|
||||||
|
if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers()))
|
||||||
|
&& !method.isAccessible()) {
|
||||||
|
method.setAccessible(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 改变private/protected的成员变量为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。
|
||||||
|
*/
|
||||||
|
public static void makeAccessible (Field field) {
|
||||||
|
if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers())
|
||||||
|
|| Modifier.isFinal(field.getModifiers())) && !field.isAccessible()) {
|
||||||
|
field.setAccessible(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过反射, 获得Class定义中声明的泛型参数的类型, 注意泛型必须定义在父类处
|
||||||
|
* 如无法找到, 返回Object.class.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static <T> Class<T> getClassGenricType (final Class clazz) {
|
||||||
|
return getClassGenricType(clazz, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过反射, 获得Class定义中声明的父类的泛型参数的类型.
|
||||||
|
* 如无法找到, 返回Object.class.
|
||||||
|
*/
|
||||||
|
public static Class getClassGenricType (final Class clazz, final int index) {
|
||||||
|
Type genType = clazz.getGenericSuperclass();
|
||||||
|
|
||||||
|
if (!(genType instanceof ParameterizedType)) {
|
||||||
|
logger.debug(clazz.getSimpleName() + "'s superclass not ParameterizedType");
|
||||||
|
return Object.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
|
||||||
|
|
||||||
|
if (index >= params.length || index < 0) {
|
||||||
|
logger.debug("Index: " + index + ", Size of " + clazz.getSimpleName() + "'s Parameterized Type: "
|
||||||
|
+ params.length);
|
||||||
|
return Object.class;
|
||||||
|
}
|
||||||
|
if (!(params[index] instanceof Class)) {
|
||||||
|
logger.debug(clazz.getSimpleName() + " not set the actual class on superclass generic parameter");
|
||||||
|
return Object.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (Class) params[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Class<?> getUserClass (Object instance) {
|
||||||
|
if (instance == null) {
|
||||||
|
throw new RuntimeException("Instance must not be null");
|
||||||
|
}
|
||||||
|
Class clazz = instance.getClass();
|
||||||
|
if (clazz != null && clazz.getName().contains(CGLIB_CLASS_SEPARATOR)) {
|
||||||
|
Class<?> superClass = clazz.getSuperclass();
|
||||||
|
if (superClass != null && !Object.class.equals(superClass)) {
|
||||||
|
return superClass;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return clazz;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将反射时的checked exception转换为unchecked exception.
|
||||||
|
*/
|
||||||
|
public static RuntimeException convertReflectionExceptionToUnchecked (String msg, Exception e) {
|
||||||
|
if (e instanceof IllegalAccessException || e instanceof IllegalArgumentException
|
||||||
|
|| e instanceof NoSuchMethodException) {
|
||||||
|
return new IllegalArgumentException(msg, e);
|
||||||
|
} else if (e instanceof InvocationTargetException) {
|
||||||
|
return new RuntimeException(msg, ((InvocationTargetException) e).getTargetException());
|
||||||
|
}
|
||||||
|
return new RuntimeException(msg, e);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,256 @@
|
||||||
|
package com.muyu.common.core.utils.sign;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base64工具类
|
||||||
|
*
|
||||||
|
* @author muyu
|
||||||
|
*/
|
||||||
|
public final class Base64 {
|
||||||
|
static private final int BASELENGTH = 128;
|
||||||
|
static private final int LOOKUPLENGTH = 64;
|
||||||
|
static private final int TWENTYFOURBITGROUP = 24;
|
||||||
|
static private final int EIGHTBIT = 8;
|
||||||
|
static private final int SIXTEENBIT = 16;
|
||||||
|
static private final int FOURBYTE = 4;
|
||||||
|
static private final int SIGN = -128;
|
||||||
|
static private final char PAD = '=';
|
||||||
|
static final private byte[] base64Alphabet = new byte[BASELENGTH];
|
||||||
|
static final private char[] lookUpBase64Alphabet = new char[LOOKUPLENGTH];
|
||||||
|
|
||||||
|
static {
|
||||||
|
for (int i = 0 ; i < BASELENGTH ; ++i) {
|
||||||
|
base64Alphabet[i] = -1;
|
||||||
|
}
|
||||||
|
for (int i = 'Z' ; i >= 'A' ; i--) {
|
||||||
|
base64Alphabet[i] = (byte) (i - 'A');
|
||||||
|
}
|
||||||
|
for (int i = 'z' ; i >= 'a' ; i--) {
|
||||||
|
base64Alphabet[i] = (byte) (i - 'a' + 26);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = '9' ; i >= '0' ; i--) {
|
||||||
|
base64Alphabet[i] = (byte) (i - '0' + 52);
|
||||||
|
}
|
||||||
|
|
||||||
|
base64Alphabet['+'] = 62;
|
||||||
|
base64Alphabet['/'] = 63;
|
||||||
|
|
||||||
|
for (int i = 0 ; i <= 25 ; i++) {
|
||||||
|
lookUpBase64Alphabet[i] = (char) ('A' + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 26, j = 0 ; i <= 51 ; i++, j++) {
|
||||||
|
lookUpBase64Alphabet[i] = (char) ('a' + j);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 52, j = 0 ; i <= 61 ; i++, j++) {
|
||||||
|
lookUpBase64Alphabet[i] = (char) ('0' + j);
|
||||||
|
}
|
||||||
|
lookUpBase64Alphabet[62] = (char) '+';
|
||||||
|
lookUpBase64Alphabet[63] = (char) '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isWhiteSpace (char octect) {
|
||||||
|
return (octect == 0x20 || octect == 0xd || octect == 0xa || octect == 0x9);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isPad (char octect) {
|
||||||
|
return (octect == PAD);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isData (char octect) {
|
||||||
|
return (octect < BASELENGTH && base64Alphabet[octect] != -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encodes hex octects into Base64
|
||||||
|
*
|
||||||
|
* @param binaryData Array containing binaryData
|
||||||
|
*
|
||||||
|
* @return Encoded Base64 array
|
||||||
|
*/
|
||||||
|
public static String encode (byte[] binaryData) {
|
||||||
|
if (binaryData == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lengthDataBits = binaryData.length * EIGHTBIT;
|
||||||
|
if (lengthDataBits == 0) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP;
|
||||||
|
int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP;
|
||||||
|
int numberQuartet = fewerThan24bits != 0 ? numberTriplets + 1 : numberTriplets;
|
||||||
|
char encodedData[] = null;
|
||||||
|
|
||||||
|
encodedData = new char[numberQuartet * 4];
|
||||||
|
|
||||||
|
byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0;
|
||||||
|
|
||||||
|
int encodedIndex = 0;
|
||||||
|
int dataIndex = 0;
|
||||||
|
|
||||||
|
for (int i = 0 ; i < numberTriplets ; i++) {
|
||||||
|
b1 = binaryData[dataIndex++];
|
||||||
|
b2 = binaryData[dataIndex++];
|
||||||
|
b3 = binaryData[dataIndex++];
|
||||||
|
|
||||||
|
l = (byte) (b2 & 0x0f);
|
||||||
|
k = (byte) (b1 & 0x03);
|
||||||
|
|
||||||
|
byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
|
||||||
|
byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0);
|
||||||
|
byte val3 = ((b3 & SIGN) == 0) ? (byte) (b3 >> 6) : (byte) ((b3) >> 6 ^ 0xfc);
|
||||||
|
|
||||||
|
encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];
|
||||||
|
encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)];
|
||||||
|
encodedData[encodedIndex++] = lookUpBase64Alphabet[(l << 2) | val3];
|
||||||
|
encodedData[encodedIndex++] = lookUpBase64Alphabet[b3 & 0x3f];
|
||||||
|
}
|
||||||
|
|
||||||
|
// form integral number of 6-bit groups
|
||||||
|
if (fewerThan24bits == EIGHTBIT) {
|
||||||
|
b1 = binaryData[dataIndex];
|
||||||
|
k = (byte) (b1 & 0x03);
|
||||||
|
byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
|
||||||
|
encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];
|
||||||
|
encodedData[encodedIndex++] = lookUpBase64Alphabet[k << 4];
|
||||||
|
encodedData[encodedIndex++] = PAD;
|
||||||
|
encodedData[encodedIndex++] = PAD;
|
||||||
|
} else if (fewerThan24bits == SIXTEENBIT) {
|
||||||
|
b1 = binaryData[dataIndex];
|
||||||
|
b2 = binaryData[dataIndex + 1];
|
||||||
|
l = (byte) (b2 & 0x0f);
|
||||||
|
k = (byte) (b1 & 0x03);
|
||||||
|
|
||||||
|
byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
|
||||||
|
byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0);
|
||||||
|
|
||||||
|
encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];
|
||||||
|
encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)];
|
||||||
|
encodedData[encodedIndex++] = lookUpBase64Alphabet[l << 2];
|
||||||
|
encodedData[encodedIndex++] = PAD;
|
||||||
|
}
|
||||||
|
return new String(encodedData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decodes Base64 data into octects
|
||||||
|
*
|
||||||
|
* @param encoded string containing Base64 data
|
||||||
|
*
|
||||||
|
* @return Array containind decoded data.
|
||||||
|
*/
|
||||||
|
public static byte[] decode (String encoded) {
|
||||||
|
if (encoded == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
char[] base64Data = encoded.toCharArray();
|
||||||
|
// remove white spaces
|
||||||
|
int len = removeWhiteSpace(base64Data);
|
||||||
|
|
||||||
|
if (len % FOURBYTE != 0) {
|
||||||
|
return null;// should be divisible by four
|
||||||
|
}
|
||||||
|
|
||||||
|
int numberQuadruple = (len / FOURBYTE);
|
||||||
|
|
||||||
|
if (numberQuadruple == 0) {
|
||||||
|
return new byte[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
byte decodedData[] = null;
|
||||||
|
byte b1 = 0, b2 = 0, b3 = 0, b4 = 0;
|
||||||
|
char d1 = 0, d2 = 0, d3 = 0, d4 = 0;
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
int encodedIndex = 0;
|
||||||
|
int dataIndex = 0;
|
||||||
|
decodedData = new byte[(numberQuadruple) * 3];
|
||||||
|
|
||||||
|
for ( ; i < numberQuadruple - 1 ; i++) {
|
||||||
|
|
||||||
|
if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++]))
|
||||||
|
|| !isData((d3 = base64Data[dataIndex++])) || !isData((d4 = base64Data[dataIndex++]))) {
|
||||||
|
return null;
|
||||||
|
} // if found "no data" just return null
|
||||||
|
|
||||||
|
b1 = base64Alphabet[d1];
|
||||||
|
b2 = base64Alphabet[d2];
|
||||||
|
b3 = base64Alphabet[d3];
|
||||||
|
b4 = base64Alphabet[d4];
|
||||||
|
|
||||||
|
decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
|
||||||
|
decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
|
||||||
|
decodedData[encodedIndex++] = (byte) (b3 << 6 | b4);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++]))) {
|
||||||
|
return null;// if found "no data" just return null
|
||||||
|
}
|
||||||
|
|
||||||
|
b1 = base64Alphabet[d1];
|
||||||
|
b2 = base64Alphabet[d2];
|
||||||
|
|
||||||
|
d3 = base64Data[dataIndex++];
|
||||||
|
d4 = base64Data[dataIndex++];
|
||||||
|
if (!isData((d3)) || !isData((d4))) {// Check if they are PAD characters
|
||||||
|
if (isPad(d3) && isPad(d4)) {
|
||||||
|
if ((b2 & 0xf) != 0)// last 4 bits should be zero
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
byte[] tmp = new byte[i * 3 + 1];
|
||||||
|
System.arraycopy(decodedData, 0, tmp, 0, i * 3);
|
||||||
|
tmp[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
|
||||||
|
return tmp;
|
||||||
|
} else if (!isPad(d3) && isPad(d4)) {
|
||||||
|
b3 = base64Alphabet[d3];
|
||||||
|
if ((b3 & 0x3) != 0)// last 2 bits should be zero
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
byte[] tmp = new byte[i * 3 + 2];
|
||||||
|
System.arraycopy(decodedData, 0, tmp, 0, i * 3);
|
||||||
|
tmp[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
|
||||||
|
tmp[encodedIndex] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
|
||||||
|
return tmp;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} else { // No PAD e.g 3cQl
|
||||||
|
b3 = base64Alphabet[d3];
|
||||||
|
b4 = base64Alphabet[d4];
|
||||||
|
decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
|
||||||
|
decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
|
||||||
|
decodedData[encodedIndex++] = (byte) (b3 << 6 | b4);
|
||||||
|
|
||||||
|
}
|
||||||
|
return decodedData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* remove WhiteSpace from MIME containing encoded Base64 data.
|
||||||
|
*
|
||||||
|
* @param data the byte array of base64 data (with WS)
|
||||||
|
*
|
||||||
|
* @return the new length
|
||||||
|
*/
|
||||||
|
private static int removeWhiteSpace (char[] data) {
|
||||||
|
if (data == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// count characters that's not whitespace
|
||||||
|
int newSize = 0;
|
||||||
|
int len = data.length;
|
||||||
|
for (int i = 0 ; i < len ; i++) {
|
||||||
|
if (!isWhiteSpace(data[i])) {
|
||||||
|
data[newSize++] = data[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newSize;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
package com.muyu.common.core.utils.sql;
|
||||||
|
|
||||||
|
import com.muyu.common.core.exception.UtilException;
|
||||||
|
import com.muyu.common.core.utils.StringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sql操作工具类
|
||||||
|
*
|
||||||
|
* @author muyu
|
||||||
|
*/
|
||||||
|
public class SqlUtil {
|
||||||
|
/**
|
||||||
|
* 限制orderBy最大长度
|
||||||
|
*/
|
||||||
|
private static final int ORDER_BY_MAX_LENGTH = 500;
|
||||||
|
/**
|
||||||
|
* 定义常用的 sql关键字
|
||||||
|
*/
|
||||||
|
public static String SQL_REGEX = "and |extractvalue|updatexml|exec |insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |or |+|user()";
|
||||||
|
/**
|
||||||
|
* 仅支持字母、数字、下划线、空格、逗号、小数点(支持多个字段排序)
|
||||||
|
*/
|
||||||
|
public static String SQL_PATTERN = "[a-zA-Z0-9_\\ \\,\\.]+";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查字符,防止注入绕过
|
||||||
|
*/
|
||||||
|
public static String escapeOrderBySql (String value) {
|
||||||
|
if (StringUtils.isNotEmpty(value) && !isValidOrderBySql(value)) {
|
||||||
|
throw new UtilException("参数不符合规范,不能进行查询");
|
||||||
|
}
|
||||||
|
if (StringUtils.length(value) > ORDER_BY_MAX_LENGTH) {
|
||||||
|
throw new UtilException("参数已超过最大限制,不能进行查询");
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证 order by 语法是否符合规范
|
||||||
|
*/
|
||||||
|
public static boolean isValidOrderBySql (String value) {
|
||||||
|
return value.matches(SQL_PATTERN);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SQL关键字检查
|
||||||
|
*/
|
||||||
|
public static void filterKeyword (String value) {
|
||||||
|
if (StringUtils.isEmpty(value)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String[] sqlKeywords = StringUtils.split(SQL_REGEX, "\\|");
|
||||||
|
for (String sqlKeyword : sqlKeywords) {
|
||||||
|
if (StringUtils.indexOfIgnoreCase(value, sqlKeyword) > -1) {
|
||||||
|
throw new UtilException("参数存在SQL注入风险");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
package com.muyu.common.core.utils.uuid;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ID生成器工具类
|
||||||
|
*
|
||||||
|
* @author muyu
|
||||||
|
*/
|
||||||
|
public class IdUtils {
|
||||||
|
/**
|
||||||
|
* 获取随机UUID
|
||||||
|
*
|
||||||
|
* @return 随机UUID
|
||||||
|
*/
|
||||||
|
public static String randomUUID () {
|
||||||
|
return UUID.randomUUID().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 简化的UUID,去掉了横线
|
||||||
|
*
|
||||||
|
* @return 简化的UUID,去掉了横线
|
||||||
|
*/
|
||||||
|
public static String simpleUUID () {
|
||||||
|
return UUID.randomUUID().toString(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取随机UUID,使用性能更好的ThreadLocalRandom生成UUID
|
||||||
|
*
|
||||||
|
* @return 随机UUID
|
||||||
|
*/
|
||||||
|
public static String fastUUID () {
|
||||||
|
return UUID.fastUUID().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 简化的UUID,去掉了横线,使用性能更好的ThreadLocalRandom生成UUID
|
||||||
|
*
|
||||||
|
* @return 简化的UUID,去掉了横线
|
||||||
|
*/
|
||||||
|
public static String fastSimpleUUID () {
|
||||||
|
return UUID.fastUUID().toString(true);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,78 @@
|
||||||
|
package com.muyu.common.core.utils.uuid;
|
||||||
|
|
||||||
|
import com.muyu.common.core.utils.DateUtils;
|
||||||
|
import com.muyu.common.core.utils.StringUtils;
|
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author muyu 序列生成类
|
||||||
|
*/
|
||||||
|
public class Seq {
|
||||||
|
// 通用序列类型
|
||||||
|
public static final String commSeqType = "COMMON";
|
||||||
|
|
||||||
|
// 上传序列类型
|
||||||
|
public static final String uploadSeqType = "UPLOAD";
|
||||||
|
// 机器标识
|
||||||
|
private static final String machineCode = "A";
|
||||||
|
// 通用接口序列数
|
||||||
|
private static AtomicInteger commSeq = new AtomicInteger(1);
|
||||||
|
// 上传接口序列数
|
||||||
|
private static AtomicInteger uploadSeq = new AtomicInteger(1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取通用序列号
|
||||||
|
*
|
||||||
|
* @return 序列值
|
||||||
|
*/
|
||||||
|
public static String getId () {
|
||||||
|
return getId(commSeqType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认16位序列号 yyMMddHHmmss + 一位机器标识 + 3长度循环递增字符串
|
||||||
|
*
|
||||||
|
* @return 序列值
|
||||||
|
*/
|
||||||
|
public static String getId (String type) {
|
||||||
|
AtomicInteger atomicInt = commSeq;
|
||||||
|
if (uploadSeqType.equals(type)) {
|
||||||
|
atomicInt = uploadSeq;
|
||||||
|
}
|
||||||
|
return getId(atomicInt, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通用接口序列号 yyMMddHHmmss + 一位机器标识 + length长度循环递增字符串
|
||||||
|
*
|
||||||
|
* @param atomicInt 序列数
|
||||||
|
* @param length 数值长度
|
||||||
|
*
|
||||||
|
* @return 序列值
|
||||||
|
*/
|
||||||
|
public static String getId (AtomicInteger atomicInt, int length) {
|
||||||
|
String result = DateUtils.dateTimeNow();
|
||||||
|
result += machineCode;
|
||||||
|
result += getSeq(atomicInt, length);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 序列循环递增字符串[1, 10 的 (length)幂次方), 用0左补齐length位数
|
||||||
|
*
|
||||||
|
* @return 序列值
|
||||||
|
*/
|
||||||
|
private synchronized static String getSeq (AtomicInteger atomicInt, int length) {
|
||||||
|
// 先取值再+1
|
||||||
|
int value = atomicInt.getAndIncrement();
|
||||||
|
|
||||||
|
// 如果更新后值>=10 的 (length)幂次方则重置为1
|
||||||
|
int maxSeq = (int) Math.pow(10, length);
|
||||||
|
if (atomicInt.get() >= maxSeq) {
|
||||||
|
atomicInt.set(1);
|
||||||
|
}
|
||||||
|
// 转字符串,用0左补齐
|
||||||
|
return StringUtils.padl(value, length);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,450 @@
|
||||||
|
package com.muyu.common.core.utils.uuid;
|
||||||
|
|
||||||
|
import com.muyu.common.core.exception.UtilException;
|
||||||
|
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.concurrent.ThreadLocalRandom;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提供通用唯一识别码(universally unique identifier)(UUID)实现
|
||||||
|
*
|
||||||
|
* @author muyu
|
||||||
|
*/
|
||||||
|
public final class UUID implements java.io.Serializable, Comparable<UUID> {
|
||||||
|
private static final long serialVersionUID = -1185015143654744140L;
|
||||||
|
/**
|
||||||
|
* 此UUID的最高64有效位
|
||||||
|
*/
|
||||||
|
private final long mostSigBits;
|
||||||
|
/**
|
||||||
|
* 此UUID的最低64有效位
|
||||||
|
*/
|
||||||
|
private final long leastSigBits;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 私有构造
|
||||||
|
*
|
||||||
|
* @param data 数据
|
||||||
|
*/
|
||||||
|
private UUID (byte[] data) {
|
||||||
|
long msb = 0;
|
||||||
|
long lsb = 0;
|
||||||
|
assert data.length == 16 : "data must be 16 bytes in length";
|
||||||
|
for (int i = 0 ; i < 8 ; i++) {
|
||||||
|
msb = (msb << 8) | (data[i] & 0xff);
|
||||||
|
}
|
||||||
|
for (int i = 8 ; i < 16 ; i++) {
|
||||||
|
lsb = (lsb << 8) | (data[i] & 0xff);
|
||||||
|
}
|
||||||
|
this.mostSigBits = msb;
|
||||||
|
this.leastSigBits = lsb;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用指定的数据构造新的 UUID。
|
||||||
|
*
|
||||||
|
* @param mostSigBits 用于 {@code UUID} 的最高有效 64 位
|
||||||
|
* @param leastSigBits 用于 {@code UUID} 的最低有效 64 位
|
||||||
|
*/
|
||||||
|
public UUID (long mostSigBits, long leastSigBits) {
|
||||||
|
this.mostSigBits = mostSigBits;
|
||||||
|
this.leastSigBits = leastSigBits;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取类型 4(伪随机生成的)UUID 的静态工厂。
|
||||||
|
*
|
||||||
|
* @return 随机生成的 {@code UUID}
|
||||||
|
*/
|
||||||
|
public static UUID fastUUID () {
|
||||||
|
return randomUUID(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。
|
||||||
|
*
|
||||||
|
* @return 随机生成的 {@code UUID}
|
||||||
|
*/
|
||||||
|
public static UUID randomUUID () {
|
||||||
|
return randomUUID(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。
|
||||||
|
*
|
||||||
|
* @param isSecure 是否使用{@link SecureRandom}如果是可以获得更安全的随机码,否则可以得到更好的性能
|
||||||
|
*
|
||||||
|
* @return 随机生成的 {@code UUID}
|
||||||
|
*/
|
||||||
|
public static UUID randomUUID (boolean isSecure) {
|
||||||
|
final Random ng = isSecure ? Holder.numberGenerator : getRandom();
|
||||||
|
|
||||||
|
byte[] randomBytes = new byte[16];
|
||||||
|
ng.nextBytes(randomBytes);
|
||||||
|
randomBytes[6] &= 0x0f; /* clear version */
|
||||||
|
randomBytes[6] |= 0x40; /* set to version 4 */
|
||||||
|
randomBytes[8] &= 0x3f; /* clear variant */
|
||||||
|
randomBytes[8] |= 0x80; /* set to IETF variant */
|
||||||
|
return new UUID(randomBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据指定的字节数组获取类型 3(基于名称的)UUID 的静态工厂。
|
||||||
|
*
|
||||||
|
* @param name 用于构造 UUID 的字节数组。
|
||||||
|
*
|
||||||
|
* @return 根据指定数组生成的 {@code UUID}
|
||||||
|
*/
|
||||||
|
public static UUID nameUUIDFromBytes (byte[] name) {
|
||||||
|
MessageDigest md;
|
||||||
|
try {
|
||||||
|
md = MessageDigest.getInstance("MD5");
|
||||||
|
} catch (NoSuchAlgorithmException nsae) {
|
||||||
|
throw new InternalError("MD5 not supported");
|
||||||
|
}
|
||||||
|
byte[] md5Bytes = md.digest(name);
|
||||||
|
md5Bytes[6] &= 0x0f; /* clear version */
|
||||||
|
md5Bytes[6] |= 0x30; /* set to version 3 */
|
||||||
|
md5Bytes[8] &= 0x3f; /* clear variant */
|
||||||
|
md5Bytes[8] |= 0x80; /* set to IETF variant */
|
||||||
|
return new UUID(md5Bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据 {@link #toString()} 方法中描述的字符串标准表示形式创建{@code UUID}。
|
||||||
|
*
|
||||||
|
* @param name 指定 {@code UUID} 字符串
|
||||||
|
*
|
||||||
|
* @return 具有指定值的 {@code UUID}
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException 如果 name 与 {@link #toString} 中描述的字符串表示形式不符抛出此异常
|
||||||
|
*/
|
||||||
|
public static UUID fromString (String name) {
|
||||||
|
String[] components = name.split("-");
|
||||||
|
if (components.length != 5) {
|
||||||
|
throw new IllegalArgumentException("Invalid UUID string: " + name);
|
||||||
|
}
|
||||||
|
for (int i = 0 ; i < 5 ; i++) {
|
||||||
|
components[i] = "0x" + components[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
long mostSigBits = Long.decode(components[0]).longValue();
|
||||||
|
mostSigBits <<= 16;
|
||||||
|
mostSigBits |= Long.decode(components[1]).longValue();
|
||||||
|
mostSigBits <<= 16;
|
||||||
|
mostSigBits |= Long.decode(components[2]).longValue();
|
||||||
|
|
||||||
|
long leastSigBits = Long.decode(components[3]).longValue();
|
||||||
|
leastSigBits <<= 48;
|
||||||
|
leastSigBits |= Long.decode(components[4]).longValue();
|
||||||
|
|
||||||
|
return new UUID(mostSigBits, leastSigBits);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回指定数字对应的hex值
|
||||||
|
*
|
||||||
|
* @param val 值
|
||||||
|
* @param digits 位
|
||||||
|
*
|
||||||
|
* @return 值
|
||||||
|
*/
|
||||||
|
private static String digits (long val, int digits) {
|
||||||
|
long hi = 1L << (digits * 4);
|
||||||
|
return Long.toHexString(hi | (val & (hi - 1))).substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取{@link SecureRandom},类提供加密的强随机数生成器 (RNG)
|
||||||
|
*
|
||||||
|
* @return {@link SecureRandom}
|
||||||
|
*/
|
||||||
|
public static SecureRandom getSecureRandom () {
|
||||||
|
try {
|
||||||
|
return SecureRandom.getInstance("SHA1PRNG");
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
throw new UtilException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取随机数生成器对象<br>
|
||||||
|
* ThreadLocalRandom是JDK 7之后提供并发产生随机数,能够解决多个线程发生的竞争争夺。
|
||||||
|
*
|
||||||
|
* @return {@link ThreadLocalRandom}
|
||||||
|
*/
|
||||||
|
public static ThreadLocalRandom getRandom () {
|
||||||
|
return ThreadLocalRandom.current();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回此 UUID 的 128 位值中的最低有效 64 位。
|
||||||
|
*
|
||||||
|
* @return 此 UUID 的 128 位值中的最低有效 64 位。
|
||||||
|
*/
|
||||||
|
public long getLeastSignificantBits () {
|
||||||
|
return leastSigBits;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回此 UUID 的 128 位值中的最高有效 64 位。
|
||||||
|
*
|
||||||
|
* @return 此 UUID 的 128 位值中最高有效 64 位。
|
||||||
|
*/
|
||||||
|
public long getMostSignificantBits () {
|
||||||
|
return mostSigBits;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 与此 {@code UUID} 相关联的版本号. 版本号描述此 {@code UUID} 是如何生成的。
|
||||||
|
* <p>
|
||||||
|
* 版本号具有以下含意:
|
||||||
|
* <ul>
|
||||||
|
* <li>1 基于时间的 UUID
|
||||||
|
* <li>2 DCE 安全 UUID
|
||||||
|
* <li>3 基于名称的 UUID
|
||||||
|
* <li>4 随机生成的 UUID
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @return 此 {@code UUID} 的版本号
|
||||||
|
*/
|
||||||
|
public int version () {
|
||||||
|
// Version is bits masked by 0x000000000000F000 in MS long
|
||||||
|
return (int) ((mostSigBits >> 12) & 0x0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 与此 {@code UUID} 相关联的变体号。变体号描述 {@code UUID} 的布局。
|
||||||
|
* <p>
|
||||||
|
* 变体号具有以下含意:
|
||||||
|
* <ul>
|
||||||
|
* <li>0 为 NCS 向后兼容保留
|
||||||
|
* <li>2 <a href="http://www.ietf.org/rfc/rfc4122.txt">IETF RFC 4122</a>(Leach-Salz), 用于此类
|
||||||
|
* <li>6 保留,微软向后兼容
|
||||||
|
* <li>7 保留供以后定义使用
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @return 此 {@code UUID} 相关联的变体号
|
||||||
|
*/
|
||||||
|
public int variant () {
|
||||||
|
// This field is composed of a varying number of bits.
|
||||||
|
// 0 - - Reserved for NCS backward compatibility
|
||||||
|
// 1 0 - The IETF aka Leach-Salz variant (used by this class)
|
||||||
|
// 1 1 0 Reserved, Microsoft backward compatibility
|
||||||
|
// 1 1 1 Reserved for future definition.
|
||||||
|
return (int) ((leastSigBits >>> (64 - (leastSigBits >>> 62))) & (leastSigBits >> 63));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 与此 UUID 相关联的时间戳值。
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* 60 位的时间戳值根据此 {@code UUID} 的 time_low、time_mid 和 time_hi 字段构造。<br>
|
||||||
|
* 所得到的时间戳以 100 毫微秒为单位,从 UTC(通用协调时间) 1582 年 10 月 15 日零时开始。
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* 时间戳值仅在在基于时间的 UUID(其 version 类型为 1)中才有意义。<br>
|
||||||
|
* 如果此 {@code UUID} 不是基于时间的 UUID,则此方法抛出 UnsupportedOperationException。
|
||||||
|
*
|
||||||
|
* @throws UnsupportedOperationException 如果此 {@code UUID} 不是 version 为 1 的 UUID。
|
||||||
|
*/
|
||||||
|
public long timestamp () throws UnsupportedOperationException {
|
||||||
|
checkTimeBase();
|
||||||
|
return (mostSigBits & 0x0FFFL) << 48//
|
||||||
|
| ((mostSigBits >> 16) & 0x0FFFFL) << 32//
|
||||||
|
| mostSigBits >>> 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 与此 UUID 相关联的时钟序列值。
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* 14 位的时钟序列值根据此 UUID 的 clock_seq 字段构造。clock_seq 字段用于保证在基于时间的 UUID 中的时间唯一性。
|
||||||
|
* <p>
|
||||||
|
* {@code clockSequence} 值仅在基于时间的 UUID(其 version 类型为 1)中才有意义。 如果此 UUID 不是基于时间的 UUID,则此方法抛出
|
||||||
|
* UnsupportedOperationException。
|
||||||
|
*
|
||||||
|
* @return 此 {@code UUID} 的时钟序列
|
||||||
|
*
|
||||||
|
* @throws UnsupportedOperationException 如果此 UUID 的 version 不为 1
|
||||||
|
*/
|
||||||
|
public int clockSequence () throws UnsupportedOperationException {
|
||||||
|
checkTimeBase();
|
||||||
|
return (int) ((leastSigBits & 0x3FFF000000000000L) >>> 48);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 与此 UUID 相关的节点值。
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* 48 位的节点值根据此 UUID 的 node 字段构造。此字段旨在用于保存机器的 IEEE 802 地址,该地址用于生成此 UUID 以保证空间唯一性。
|
||||||
|
* <p>
|
||||||
|
* 节点值仅在基于时间的 UUID(其 version 类型为 1)中才有意义。<br>
|
||||||
|
* 如果此 UUID 不是基于时间的 UUID,则此方法抛出 UnsupportedOperationException。
|
||||||
|
*
|
||||||
|
* @return 此 {@code UUID} 的节点值
|
||||||
|
*
|
||||||
|
* @throws UnsupportedOperationException 如果此 UUID 的 version 不为 1
|
||||||
|
*/
|
||||||
|
public long node () throws UnsupportedOperationException {
|
||||||
|
checkTimeBase();
|
||||||
|
return leastSigBits & 0x0000FFFFFFFFFFFFL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回此{@code UUID} 的字符串表现形式。
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* UUID 的字符串表示形式由此 BNF 描述:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* {@code
|
||||||
|
* UUID = <time_low>-<time_mid>-<time_high_and_version>-<variant_and_sequence>-<node>
|
||||||
|
* time_low = 4*<hexOctet>
|
||||||
|
* time_mid = 2*<hexOctet>
|
||||||
|
* time_high_and_version = 2*<hexOctet>
|
||||||
|
* variant_and_sequence = 2*<hexOctet>
|
||||||
|
* node = 6*<hexOctet>
|
||||||
|
* hexOctet = <hexDigit><hexDigit>
|
||||||
|
* hexDigit = [0-9a-fA-F]
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* </blockquote>
|
||||||
|
*
|
||||||
|
* @return 此{@code UUID} 的字符串表现形式
|
||||||
|
*
|
||||||
|
* @see #toString(boolean)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString () {
|
||||||
|
return toString(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回此{@code UUID} 的字符串表现形式。
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* UUID 的字符串表示形式由此 BNF 描述:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* {@code
|
||||||
|
* UUID = <time_low>-<time_mid>-<time_high_and_version>-<variant_and_sequence>-<node>
|
||||||
|
* time_low = 4*<hexOctet>
|
||||||
|
* time_mid = 2*<hexOctet>
|
||||||
|
* time_high_and_version = 2*<hexOctet>
|
||||||
|
* variant_and_sequence = 2*<hexOctet>
|
||||||
|
* node = 6*<hexOctet>
|
||||||
|
* hexOctet = <hexDigit><hexDigit>
|
||||||
|
* hexDigit = [0-9a-fA-F]
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* </blockquote>
|
||||||
|
*
|
||||||
|
* @param isSimple 是否简单模式,简单模式为不带'-'的UUID字符串
|
||||||
|
*
|
||||||
|
* @return 此{@code UUID} 的字符串表现形式
|
||||||
|
*/
|
||||||
|
public String toString (boolean isSimple) {
|
||||||
|
final StringBuilder builder = new StringBuilder(isSimple ? 32 : 36);
|
||||||
|
// time_low
|
||||||
|
builder.append(digits(mostSigBits >> 32, 8));
|
||||||
|
if (false == isSimple) {
|
||||||
|
builder.append('-');
|
||||||
|
}
|
||||||
|
// time_mid
|
||||||
|
builder.append(digits(mostSigBits >> 16, 4));
|
||||||
|
if (false == isSimple) {
|
||||||
|
builder.append('-');
|
||||||
|
}
|
||||||
|
// time_high_and_version
|
||||||
|
builder.append(digits(mostSigBits, 4));
|
||||||
|
if (false == isSimple) {
|
||||||
|
builder.append('-');
|
||||||
|
}
|
||||||
|
// variant_and_sequence
|
||||||
|
builder.append(digits(leastSigBits >> 48, 4));
|
||||||
|
if (false == isSimple) {
|
||||||
|
builder.append('-');
|
||||||
|
}
|
||||||
|
// node
|
||||||
|
builder.append(digits(leastSigBits, 12));
|
||||||
|
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Comparison Operations
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回此 UUID 的哈希码。
|
||||||
|
*
|
||||||
|
* @return UUID 的哈希码值。
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public int hashCode () {
|
||||||
|
long hilo = mostSigBits ^ leastSigBits;
|
||||||
|
return ((int) (hilo >> 32)) ^ (int) hilo;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------------------------------------------------
|
||||||
|
// Private method start
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将此对象与指定对象比较。
|
||||||
|
* <p>
|
||||||
|
* 当且仅当参数不为 {@code null}、而是一个 UUID 对象、具有与此 UUID 相同的 varriant、包含相同的值(每一位均相同)时,结果才为 {@code true}。
|
||||||
|
*
|
||||||
|
* @param obj 要与之比较的对象
|
||||||
|
*
|
||||||
|
* @return 如果对象相同,则返回 {@code true};否则返回 {@code false}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean equals (Object obj) {
|
||||||
|
if ((null == obj) || (obj.getClass() != UUID.class)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
UUID id = (UUID) obj;
|
||||||
|
return (mostSigBits == id.mostSigBits && leastSigBits == id.leastSigBits);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将此 UUID 与指定的 UUID 比较。
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* 如果两个 UUID 不同,且第一个 UUID 的最高有效字段大于第二个 UUID 的对应字段,则第一个 UUID 大于第二个 UUID。
|
||||||
|
*
|
||||||
|
* @param val 与此 UUID 比较的 UUID
|
||||||
|
*
|
||||||
|
* @return 在此 UUID 小于、等于或大于 val 时,分别返回 -1、0 或 1。
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public int compareTo (UUID val) {
|
||||||
|
// The ordering is intentionally set up so that the UUIDs
|
||||||
|
// can simply be numerically compared as two numbers
|
||||||
|
return (this.mostSigBits < val.mostSigBits ? -1 : //
|
||||||
|
(this.mostSigBits > val.mostSigBits ? 1 : //
|
||||||
|
(this.leastSigBits < val.leastSigBits ? -1 : //
|
||||||
|
(this.leastSigBits > val.leastSigBits ? 1 : //
|
||||||
|
0))));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查是否为time-based版本UUID
|
||||||
|
*/
|
||||||
|
private void checkTimeBase () {
|
||||||
|
if (version() != 1) {
|
||||||
|
throw new UnsupportedOperationException("Not a time-based UUID");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SecureRandom 的单例
|
||||||
|
*/
|
||||||
|
private static class Holder {
|
||||||
|
static final SecureRandom numberGenerator = getSecureRandom();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package com.muyu.common.core.validation;
|
||||||
|
|
||||||
|
import jakarta.validation.Validation;
|
||||||
|
import jakarta.validation.Validator;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class ValidationConfig {
|
||||||
|
@Bean
|
||||||
|
public Validator validator() {
|
||||||
|
return Validation.byDefaultProvider().configure().buildValidatorFactory().getValidator();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,129 @@
|
||||||
|
package com.muyu.common.core.web.controller;
|
||||||
|
|
||||||
|
import com.github.pagehelper.PageInfo;
|
||||||
|
import com.muyu.common.core.utils.DateUtils;
|
||||||
|
import com.muyu.common.core.utils.PageUtils;
|
||||||
|
import com.muyu.common.core.domain.Result;
|
||||||
|
import com.muyu.common.core.web.page.TableDataInfo;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.web.bind.WebDataBinder;
|
||||||
|
import org.springframework.web.bind.annotation.InitBinder;
|
||||||
|
|
||||||
|
import java.beans.PropertyEditorSupport;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* web层通用数据处理
|
||||||
|
*
|
||||||
|
* @author muyu
|
||||||
|
*/
|
||||||
|
public class BaseController {
|
||||||
|
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将前台传递过来的日期格式的字符串,自动转化为Date类型
|
||||||
|
*/
|
||||||
|
@InitBinder
|
||||||
|
public void initBinder (WebDataBinder binder) {
|
||||||
|
// Date 类型转换
|
||||||
|
binder.registerCustomEditor(Date.class, new PropertyEditorSupport() {
|
||||||
|
@Override
|
||||||
|
public void setAsText (String text) {
|
||||||
|
setValue(DateUtils.parseDate(text));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置请求分页数据
|
||||||
|
*/
|
||||||
|
protected void startPage () {
|
||||||
|
PageUtils.startPage();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清理分页的线程变量
|
||||||
|
*/
|
||||||
|
protected void clearPage () {
|
||||||
|
PageUtils.clearPage();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 响应请求分页数据
|
||||||
|
*/
|
||||||
|
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||||
|
protected <T> Result<TableDataInfo<T>> getDataTable (List<T> list) {
|
||||||
|
return Result.success(
|
||||||
|
TableDataInfo.<T>builder()
|
||||||
|
.total(new PageInfo(list).getTotal())
|
||||||
|
.rows(list)
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回成功
|
||||||
|
*/
|
||||||
|
public Result success () {
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回成功消息
|
||||||
|
*/
|
||||||
|
public Result success (String message) {
|
||||||
|
return Result.success(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回成功消息
|
||||||
|
*/
|
||||||
|
public Result success (Object data) {
|
||||||
|
return Result.success(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回失败消息
|
||||||
|
*/
|
||||||
|
public Result error () {
|
||||||
|
return Result.error();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回失败消息
|
||||||
|
*/
|
||||||
|
public Result error (String message) {
|
||||||
|
return Result.error(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回警告消息
|
||||||
|
*/
|
||||||
|
public Result warn (String message) {
|
||||||
|
return Result.warn(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 响应返回结果
|
||||||
|
*
|
||||||
|
* @param rows 影响行数
|
||||||
|
*
|
||||||
|
* @return 操作结果
|
||||||
|
*/
|
||||||
|
protected Result toAjax (int rows) {
|
||||||
|
return rows > 0 ? Result.success() : Result.error();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 响应返回结果
|
||||||
|
*
|
||||||
|
* @param result 结果
|
||||||
|
*
|
||||||
|
* @return 操作结果
|
||||||
|
*/
|
||||||
|
protected Result toAjax (boolean result) {
|
||||||
|
return result ? success() : error();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,80 @@
|
||||||
|
package com.muyu.common.core.web.domain;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.experimental.SuperBuilder;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Entity基类
|
||||||
|
*
|
||||||
|
* @author muyu
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@SuperBuilder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class BaseEntity implements Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 搜索值
|
||||||
|
*/
|
||||||
|
@JsonIgnore
|
||||||
|
@TableField(exist = false)
|
||||||
|
private String searchValue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建者
|
||||||
|
*/
|
||||||
|
private String createBy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
private Date createTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新者
|
||||||
|
*/
|
||||||
|
private String updateBy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
private Date updateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 备注
|
||||||
|
*/
|
||||||
|
private String remark;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求参数
|
||||||
|
*/
|
||||||
|
@JsonInclude(JsonInclude.Include.NON_EMPTY)
|
||||||
|
@TableField(exist = false)
|
||||||
|
private Map<String, Object> params;
|
||||||
|
|
||||||
|
public Map<String, Object> getParams () {
|
||||||
|
if (params == null) {
|
||||||
|
params = new HashMap<>();
|
||||||
|
}
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setParams (Map<String, Object> params) {
|
||||||
|
this.params = params;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
package com.muyu.common.core.web.domain;
|
||||||
|
|
||||||
|
import lombok.*;
|
||||||
|
import lombok.experimental.SuperBuilder;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tree基类
|
||||||
|
*
|
||||||
|
* @author muyu
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@SuperBuilder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class TreeEntity extends BaseEntity {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 父菜单名称
|
||||||
|
*/
|
||||||
|
private String parentName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 父菜单ID
|
||||||
|
*/
|
||||||
|
private Long parentId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 显示顺序
|
||||||
|
*/
|
||||||
|
private Integer orderNum;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 祖级列表
|
||||||
|
*/
|
||||||
|
private String ancestors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 子部门
|
||||||
|
*/
|
||||||
|
private List<?> children = new ArrayList<>();
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,93 @@
|
||||||
|
package com.muyu.common.core.web.page;
|
||||||
|
|
||||||
|
import com.muyu.common.core.utils.StringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页数据
|
||||||
|
*
|
||||||
|
* @author muyu
|
||||||
|
*/
|
||||||
|
public class PageDomain {
|
||||||
|
/**
|
||||||
|
* 当前记录起始索引
|
||||||
|
*/
|
||||||
|
private Integer pageNum;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 每页显示记录数
|
||||||
|
*/
|
||||||
|
private Integer pageSize;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 排序列
|
||||||
|
*/
|
||||||
|
private String orderByColumn;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 排序的方向desc或者asc
|
||||||
|
*/
|
||||||
|
private String isAsc = "asc";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页参数合理化
|
||||||
|
*/
|
||||||
|
private Boolean reasonable = true;
|
||||||
|
|
||||||
|
public String getOrderBy () {
|
||||||
|
if (StringUtils.isEmpty(orderByColumn)) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return StringUtils.toUnderScoreCase(orderByColumn) + " " + isAsc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getPageNum () {
|
||||||
|
return pageNum;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPageNum (Integer pageNum) {
|
||||||
|
this.pageNum = pageNum;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getPageSize () {
|
||||||
|
return pageSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPageSize (Integer pageSize) {
|
||||||
|
this.pageSize = pageSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOrderByColumn () {
|
||||||
|
return orderByColumn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOrderByColumn (String orderByColumn) {
|
||||||
|
this.orderByColumn = orderByColumn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getIsAsc () {
|
||||||
|
return isAsc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIsAsc (String isAsc) {
|
||||||
|
if (StringUtils.isNotEmpty(isAsc)) {
|
||||||
|
// 兼容前端排序类型
|
||||||
|
if ("ascending".equals(isAsc)) {
|
||||||
|
isAsc = "asc";
|
||||||
|
} else if ("descending".equals(isAsc)) {
|
||||||
|
isAsc = "desc";
|
||||||
|
}
|
||||||
|
this.isAsc = isAsc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean getReasonable () {
|
||||||
|
if (StringUtils.isNull(reasonable)) {
|
||||||
|
return Boolean.TRUE;
|
||||||
|
}
|
||||||
|
return reasonable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setReasonable (Boolean reasonable) {
|
||||||
|
this.reasonable = reasonable;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
package com.muyu.common.core.web.page;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import org.apache.poi.ss.formula.functions.T;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表格分页数据对象
|
||||||
|
*
|
||||||
|
* @author muyu
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class TableDataInfo<T> implements Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 总记录数
|
||||||
|
*/
|
||||||
|
private long total;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 列表数据
|
||||||
|
*/
|
||||||
|
private List<T> rows;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 消息状态码
|
||||||
|
*/
|
||||||
|
private int code;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 消息内容
|
||||||
|
*/
|
||||||
|
private String msg;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
package com.muyu.common.core.web.page;
|
||||||
|
|
||||||
|
import com.muyu.common.core.text.Convert;
|
||||||
|
import com.muyu.common.core.utils.ServletUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表格数据处理
|
||||||
|
*
|
||||||
|
* @author muyu
|
||||||
|
*/
|
||||||
|
public class TableSupport {
|
||||||
|
/**
|
||||||
|
* 当前记录起始索引
|
||||||
|
*/
|
||||||
|
public static final String PAGE_NUM = "pageNum";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 每页显示记录数
|
||||||
|
*/
|
||||||
|
public static final String PAGE_SIZE = "pageSize";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 排序列
|
||||||
|
*/
|
||||||
|
public static final String ORDER_BY_COLUMN = "orderByColumn";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 排序的方向 "desc" 或者 "asc".
|
||||||
|
*/
|
||||||
|
public static final String IS_ASC = "isAsc";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页参数合理化
|
||||||
|
*/
|
||||||
|
public static final String REASONABLE = "reasonable";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 封装分页对象
|
||||||
|
*/
|
||||||
|
public static PageDomain getPageDomain () {
|
||||||
|
PageDomain pageDomain = new PageDomain();
|
||||||
|
pageDomain.setPageNum(Convert.toInt(ServletUtils.getParameter(PAGE_NUM), 1));
|
||||||
|
pageDomain.setPageSize(Convert.toInt(ServletUtils.getParameter(PAGE_SIZE), 10));
|
||||||
|
pageDomain.setOrderByColumn(ServletUtils.getParameter(ORDER_BY_COLUMN));
|
||||||
|
pageDomain.setIsAsc(ServletUtils.getParameter(IS_ASC));
|
||||||
|
pageDomain.setReasonable(ServletUtils.getParameterToBool(REASONABLE));
|
||||||
|
return pageDomain;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PageDomain buildPageRequest () {
|
||||||
|
return getPageDomain();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
package com.muyu.common.core.xss;
|
||||||
|
|
||||||
|
import jakarta.validation.Constraint;
|
||||||
|
import jakarta.validation.Payload;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义xss校验注解
|
||||||
|
*
|
||||||
|
* @author muyu
|
||||||
|
*/
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target(value = {ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.PARAMETER})
|
||||||
|
@Constraint(validatedBy = {XssValidator.class})
|
||||||
|
public @interface Xss {
|
||||||
|
String message ()
|
||||||
|
|
||||||
|
default "不允许任何脚本运行";
|
||||||
|
|
||||||
|
Class<?>[] groups () default {};
|
||||||
|
|
||||||
|
Class<? extends Payload>[] payload () default {};
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
package com.muyu.common.core.xss;
|
||||||
|
|
||||||
|
import com.muyu.common.core.utils.StringUtils;
|
||||||
|
import jakarta.validation.ConstraintValidator;
|
||||||
|
import jakarta.validation.ConstraintValidatorContext;
|
||||||
|
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义xss校验注解实现
|
||||||
|
*
|
||||||
|
* @author muyu
|
||||||
|
*/
|
||||||
|
public class XssValidator implements ConstraintValidator<Xss, String> {
|
||||||
|
private static final String HTML_PATTERN = "<(\\S*?)[^>]*>.*?|<.*? />";
|
||||||
|
|
||||||
|
public static boolean containsHtml (String value) {
|
||||||
|
Pattern pattern = Pattern.compile(HTML_PATTERN);
|
||||||
|
Matcher matcher = pattern.matcher(value);
|
||||||
|
return matcher.matches();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isValid (String value, ConstraintValidatorContext constraintValidatorContext) {
|
||||||
|
if (StringUtils.isBlank(value)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return !containsHtml(value);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
com.muyu.common.core.utils.SpringUtils
|
||||||
|
com.muyu.common.core.feign.FeginConfig
|
|
@ -0,0 +1,2 @@
|
||||||
|
com.muyu.common.core.utils.SpringUtils
|
||||||
|
com.muyu.common.core.feign.FeginConfig
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue