zhousq
1 year ago
217 changed files with 29455 additions and 0 deletions
@ -0,0 +1,141 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" |
|||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
|||
<parent> |
|||
<artifactId>lzbi</artifactId> |
|||
<groupId>com.lzbi</groupId> |
|||
<version>3.8.6</version> |
|||
</parent> |
|||
<modelVersion>4.0.0</modelVersion> |
|||
|
|||
<artifactId>lzbi-common</artifactId> |
|||
|
|||
<description> |
|||
common通用工具 |
|||
</description> |
|||
|
|||
<dependencies> |
|||
|
|||
<!-- Spring框架基本的核心工具 --> |
|||
<dependency> |
|||
<groupId>org.springframework</groupId> |
|||
<artifactId>spring-context-support</artifactId> |
|||
</dependency> |
|||
|
|||
<!-- SpringWeb模块 --> |
|||
<dependency> |
|||
<groupId>org.springframework</groupId> |
|||
<artifactId>spring-web</artifactId> |
|||
</dependency> |
|||
|
|||
<!-- spring security 安全认证 --> |
|||
<dependency> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-starter-security</artifactId> |
|||
</dependency> |
|||
|
|||
<!-- pagehelper 分页插件 --> |
|||
<dependency> |
|||
<groupId>com.github.pagehelper</groupId> |
|||
<artifactId>pagehelper-spring-boot-starter</artifactId> |
|||
</dependency> |
|||
|
|||
<!-- 自定义验证注解 --> |
|||
<dependency> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-starter-validation</artifactId> |
|||
</dependency> |
|||
|
|||
<!--常用工具类 --> |
|||
<dependency> |
|||
<groupId>org.apache.commons</groupId> |
|||
<artifactId>commons-lang3</artifactId> |
|||
</dependency> |
|||
|
|||
<!-- JSON工具类 --> |
|||
<dependency> |
|||
<groupId>com.fasterxml.jackson.core</groupId> |
|||
<artifactId>jackson-databind</artifactId> |
|||
</dependency> |
|||
|
|||
<!-- <!– 动态数据源 –>--> |
|||
<!-- <dependency>--> |
|||
<!-- <groupId>com.baomidou</groupId>--> |
|||
<!-- <artifactId>dynamic-datasource-spring-boot-starter</artifactId>--> |
|||
<!-- <version>3.5.2</version>--> |
|||
<!-- </dependency>--> |
|||
|
|||
<!-- 阿里JSON解析器 --> |
|||
<dependency> |
|||
<groupId>com.alibaba.fastjson2</groupId> |
|||
<artifactId>fastjson2</artifactId> |
|||
</dependency> |
|||
|
|||
<!-- io常用工具类 --> |
|||
<dependency> |
|||
<groupId>commons-io</groupId> |
|||
<artifactId>commons-io</artifactId> |
|||
</dependency> |
|||
|
|||
<!-- excel工具 --> |
|||
<dependency> |
|||
<groupId>org.apache.poi</groupId> |
|||
<artifactId>poi-ooxml</artifactId> |
|||
</dependency> |
|||
|
|||
<!-- yml解析器 --> |
|||
<dependency> |
|||
<groupId>org.yaml</groupId> |
|||
<artifactId>snakeyaml</artifactId> |
|||
</dependency> |
|||
|
|||
<!-- Token生成与解析--> |
|||
<dependency> |
|||
<groupId>io.jsonwebtoken</groupId> |
|||
<artifactId>jjwt</artifactId> |
|||
</dependency> |
|||
|
|||
<!-- Jaxb --> |
|||
<dependency> |
|||
<groupId>javax.xml.bind</groupId> |
|||
<artifactId>jaxb-api</artifactId> |
|||
</dependency> |
|||
|
|||
<!-- redis 缓存操作 --> |
|||
<dependency> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-starter-data-redis</artifactId> |
|||
</dependency> |
|||
|
|||
<!-- pool 对象池 --> |
|||
<dependency> |
|||
<groupId>org.apache.commons</groupId> |
|||
<artifactId>commons-pool2</artifactId> |
|||
</dependency> |
|||
|
|||
<!-- 解析客户端操作系统、浏览器等 --> |
|||
<dependency> |
|||
<groupId>eu.bitwalker</groupId> |
|||
<artifactId>UserAgentUtils</artifactId> |
|||
</dependency> |
|||
|
|||
<!-- servlet包 --> |
|||
<dependency> |
|||
<groupId>javax.servlet</groupId> |
|||
<artifactId>javax.servlet-api</artifactId> |
|||
</dependency> |
|||
<!-- mybatis-plus 增强CRUD --> |
|||
<dependency> |
|||
<groupId>com.baomidou</groupId> |
|||
<artifactId>mybatis-plus-boot-starter</artifactId> |
|||
<version>3.5.1</version> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>cn.hutool</groupId> |
|||
<artifactId>hutool-all</artifactId> |
|||
<version>5.8.16</version> |
|||
</dependency> |
|||
</dependencies> |
|||
|
|||
</project> |
@ -0,0 +1,114 @@ |
|||
package com.lzbi.common.utils; |
|||
|
|||
import java.math.BigDecimal; |
|||
import java.math.RoundingMode; |
|||
|
|||
/** |
|||
* 精确的浮点数运算 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
public class Arith |
|||
{ |
|||
|
|||
/** 默认除法运算精度 */ |
|||
private static final int DEF_DIV_SCALE = 10; |
|||
|
|||
/** 这个类不能实例化 */ |
|||
private Arith() |
|||
{ |
|||
} |
|||
|
|||
/** |
|||
* 提供精确的加法运算。 |
|||
* @param v1 被加数 |
|||
* @param v2 加数 |
|||
* @return 两个参数的和 |
|||
*/ |
|||
public static double add(double v1, double v2) |
|||
{ |
|||
BigDecimal b1 = new BigDecimal(Double.toString(v1)); |
|||
BigDecimal b2 = new BigDecimal(Double.toString(v2)); |
|||
return b1.add(b2).doubleValue(); |
|||
} |
|||
|
|||
/** |
|||
* 提供精确的减法运算。 |
|||
* @param v1 被减数 |
|||
* @param v2 减数 |
|||
* @return 两个参数的差 |
|||
*/ |
|||
public static double sub(double v1, double v2) |
|||
{ |
|||
BigDecimal b1 = new BigDecimal(Double.toString(v1)); |
|||
BigDecimal b2 = new BigDecimal(Double.toString(v2)); |
|||
return b1.subtract(b2).doubleValue(); |
|||
} |
|||
|
|||
/** |
|||
* 提供精确的乘法运算。 |
|||
* @param v1 被乘数 |
|||
* @param v2 乘数 |
|||
* @return 两个参数的积 |
|||
*/ |
|||
public static double mul(double v1, double v2) |
|||
{ |
|||
BigDecimal b1 = new BigDecimal(Double.toString(v1)); |
|||
BigDecimal b2 = new BigDecimal(Double.toString(v2)); |
|||
return b1.multiply(b2).doubleValue(); |
|||
} |
|||
|
|||
/** |
|||
* 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到 |
|||
* 小数点以后10位,以后的数字四舍五入。 |
|||
* @param v1 被除数 |
|||
* @param v2 除数 |
|||
* @return 两个参数的商 |
|||
*/ |
|||
public static double div(double v1, double v2) |
|||
{ |
|||
return div(v1, v2, DEF_DIV_SCALE); |
|||
} |
|||
|
|||
/** |
|||
* 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指 |
|||
* 定精度,以后的数字四舍五入。 |
|||
* @param v1 被除数 |
|||
* @param v2 除数 |
|||
* @param scale 表示表示需要精确到小数点以后几位。 |
|||
* @return 两个参数的商 |
|||
*/ |
|||
public static double div(double v1, double v2, int scale) |
|||
{ |
|||
if (scale < 0) |
|||
{ |
|||
throw new IllegalArgumentException( |
|||
"The scale must be a positive integer or zero"); |
|||
} |
|||
BigDecimal b1 = new BigDecimal(Double.toString(v1)); |
|||
BigDecimal b2 = new BigDecimal(Double.toString(v2)); |
|||
if (b1.compareTo(BigDecimal.ZERO) == 0) |
|||
{ |
|||
return BigDecimal.ZERO.doubleValue(); |
|||
} |
|||
return b1.divide(b2, scale, RoundingMode.HALF_UP).doubleValue(); |
|||
} |
|||
|
|||
/** |
|||
* 提供精确的小数位四舍五入处理。 |
|||
* @param v 需要四舍五入的数字 |
|||
* @param scale 小数点后保留几位 |
|||
* @return 四舍五入后的结果 |
|||
*/ |
|||
public static double round(double v, int scale) |
|||
{ |
|||
if (scale < 0) |
|||
{ |
|||
throw new IllegalArgumentException( |
|||
"The scale must be a positive integer or zero"); |
|||
} |
|||
BigDecimal b = new BigDecimal(Double.toString(v)); |
|||
BigDecimal one = BigDecimal.ONE; |
|||
return b.divide(one, scale, RoundingMode.HALF_UP).doubleValue(); |
|||
} |
|||
} |
@ -0,0 +1,191 @@ |
|||
package com.lzbi.common.utils; |
|||
|
|||
import java.lang.management.ManagementFactory; |
|||
import java.text.ParseException; |
|||
import java.text.SimpleDateFormat; |
|||
import java.time.LocalDate; |
|||
import java.time.LocalDateTime; |
|||
import java.time.LocalTime; |
|||
import java.time.ZoneId; |
|||
import java.time.ZonedDateTime; |
|||
import java.util.Date; |
|||
import org.apache.commons.lang3.time.DateFormatUtils; |
|||
|
|||
/** |
|||
* 时间工具类 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
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); |
|||
} |
|||
|
|||
/** |
|||
* 计算相差天数 |
|||
*/ |
|||
public static int differentDaysByMillisecond(Date date1, Date date2) |
|||
{ |
|||
return Math.abs((int) ((date2.getTime() - date1.getTime()) / (1000 * 3600 * 24))); |
|||
} |
|||
|
|||
/** |
|||
* 计算时间差 |
|||
* |
|||
* @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,186 @@ |
|||
package com.lzbi.common.utils; |
|||
|
|||
import java.util.Collection; |
|||
import java.util.List; |
|||
import com.alibaba.fastjson2.JSONArray; |
|||
import com.lzbi.common.constant.CacheConstants; |
|||
import com.lzbi.common.core.domain.entity.SysDictData; |
|||
import com.lzbi.common.core.redis.RedisCache; |
|||
import com.lzbi.common.utils.spring.SpringUtils; |
|||
|
|||
/** |
|||
* 字典工具类 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
public class DictUtils |
|||
{ |
|||
/** |
|||
* 分隔符 |
|||
*/ |
|||
public static final String SEPARATOR = ","; |
|||
|
|||
/** |
|||
* 设置字典缓存 |
|||
* |
|||
* @param key 参数键 |
|||
* @param dictDatas 字典数据列表 |
|||
*/ |
|||
public static void setDictCache(String key, List<SysDictData> dictDatas) |
|||
{ |
|||
SpringUtils.getBean(RedisCache.class).setCacheObject(getCacheKey(key), dictDatas); |
|||
} |
|||
|
|||
/** |
|||
* 获取字典缓存 |
|||
* |
|||
* @param key 参数键 |
|||
* @return dictDatas 字典数据列表 |
|||
*/ |
|||
public static List<SysDictData> getDictCache(String key) |
|||
{ |
|||
JSONArray arrayCache = SpringUtils.getBean(RedisCache.class).getCacheObject(getCacheKey(key)); |
|||
if (StringUtils.isNotNull(arrayCache)) |
|||
{ |
|||
return arrayCache.toList(SysDictData.class); |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
/** |
|||
* 根据字典类型和字典值获取字典标签 |
|||
* |
|||
* @param dictType 字典类型 |
|||
* @param dictValue 字典值 |
|||
* @return 字典标签 |
|||
*/ |
|||
public static String getDictLabel(String dictType, String dictValue) |
|||
{ |
|||
return getDictLabel(dictType, dictValue, SEPARATOR); |
|||
} |
|||
|
|||
/** |
|||
* 根据字典类型和字典标签获取字典值 |
|||
* |
|||
* @param dictType 字典类型 |
|||
* @param dictLabel 字典标签 |
|||
* @return 字典值 |
|||
*/ |
|||
public static String getDictValue(String dictType, String dictLabel) |
|||
{ |
|||
return getDictValue(dictType, dictLabel, SEPARATOR); |
|||
} |
|||
|
|||
/** |
|||
* 根据字典类型和字典值获取字典标签 |
|||
* |
|||
* @param dictType 字典类型 |
|||
* @param dictValue 字典值 |
|||
* @param separator 分隔符 |
|||
* @return 字典标签 |
|||
*/ |
|||
public static String getDictLabel(String dictType, String dictValue, String separator) |
|||
{ |
|||
StringBuilder propertyString = new StringBuilder(); |
|||
List<SysDictData> datas = getDictCache(dictType); |
|||
|
|||
if (StringUtils.isNotNull(datas)) |
|||
{ |
|||
if (StringUtils.containsAny(separator, dictValue)) |
|||
{ |
|||
for (SysDictData dict : datas) |
|||
{ |
|||
for (String value : dictValue.split(separator)) |
|||
{ |
|||
if (value.equals(dict.getDictValue())) |
|||
{ |
|||
propertyString.append(dict.getDictLabel()).append(separator); |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
for (SysDictData dict : datas) |
|||
{ |
|||
if (dictValue.equals(dict.getDictValue())) |
|||
{ |
|||
return dict.getDictLabel(); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
return StringUtils.stripEnd(propertyString.toString(), separator); |
|||
} |
|||
|
|||
/** |
|||
* 根据字典类型和字典标签获取字典值 |
|||
* |
|||
* @param dictType 字典类型 |
|||
* @param dictLabel 字典标签 |
|||
* @param separator 分隔符 |
|||
* @return 字典值 |
|||
*/ |
|||
public static String getDictValue(String dictType, String dictLabel, String separator) |
|||
{ |
|||
StringBuilder propertyString = new StringBuilder(); |
|||
List<SysDictData> datas = getDictCache(dictType); |
|||
|
|||
if (StringUtils.containsAny(separator, dictLabel) && StringUtils.isNotEmpty(datas)) |
|||
{ |
|||
for (SysDictData dict : datas) |
|||
{ |
|||
for (String label : dictLabel.split(separator)) |
|||
{ |
|||
if (label.equals(dict.getDictLabel())) |
|||
{ |
|||
propertyString.append(dict.getDictValue()).append(separator); |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
for (SysDictData dict : datas) |
|||
{ |
|||
if (dictLabel.equals(dict.getDictLabel())) |
|||
{ |
|||
return dict.getDictValue(); |
|||
} |
|||
} |
|||
} |
|||
return StringUtils.stripEnd(propertyString.toString(), separator); |
|||
} |
|||
|
|||
/** |
|||
* 删除指定字典缓存 |
|||
* |
|||
* @param key 字典键 |
|||
*/ |
|||
public static void removeDictCache(String key) |
|||
{ |
|||
SpringUtils.getBean(RedisCache.class).deleteObject(getCacheKey(key)); |
|||
} |
|||
|
|||
/** |
|||
* 清空字典缓存 |
|||
*/ |
|||
public static void clearDictCache() |
|||
{ |
|||
Collection<String> keys = SpringUtils.getBean(RedisCache.class).keys(CacheConstants.SYS_DICT_KEY + "*"); |
|||
SpringUtils.getBean(RedisCache.class).deleteObject(keys); |
|||
} |
|||
|
|||
/** |
|||
* 设置cache key |
|||
* |
|||
* @param configKey 参数键 |
|||
* @return 缓存键key |
|||
*/ |
|||
public static String getCacheKey(String configKey) |
|||
{ |
|||
return CacheConstants.SYS_DICT_KEY + configKey; |
|||
} |
|||
} |
@ -0,0 +1,39 @@ |
|||
package com.lzbi.common.utils; |
|||
|
|||
import java.io.PrintWriter; |
|||
import java.io.StringWriter; |
|||
import org.apache.commons.lang3.exception.ExceptionUtils; |
|||
|
|||
/** |
|||
* 错误信息处理类。 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
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,18 @@ |
|||
package com.lzbi.common.utils; |
|||
|
|||
/** |
|||
* 处理并记录日志文件 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
public class LogUtils |
|||
{ |
|||
public static String getBlock(Object msg) |
|||
{ |
|||
if (msg == null) |
|||
{ |
|||
msg = ""; |
|||
} |
|||
return "[" + msg.toString() + "]"; |
|||
} |
|||
} |
@ -0,0 +1,26 @@ |
|||
package com.lzbi.common.utils; |
|||
|
|||
import org.springframework.context.MessageSource; |
|||
import org.springframework.context.i18n.LocaleContextHolder; |
|||
import com.lzbi.common.utils.spring.SpringUtils; |
|||
|
|||
/** |
|||
* 获取i18n资源文件 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
public class MessageUtils |
|||
{ |
|||
/** |
|||
* 根据消息键和参数 获取消息 委托给spring messageSource |
|||
* |
|||
* @param code 消息键 |
|||
* @param args 参数 |
|||
* @return 获取国际化翻译值 |
|||
*/ |
|||
public static String message(String code, Object... args) |
|||
{ |
|||
MessageSource messageSource = SpringUtils.getBean(MessageSource.class); |
|||
return messageSource.getMessage(code, args, LocaleContextHolder.getLocale()); |
|||
} |
|||
} |
@ -0,0 +1,35 @@ |
|||
package com.lzbi.common.utils; |
|||
|
|||
import com.github.pagehelper.PageHelper; |
|||
import com.lzbi.common.core.page.PageDomain; |
|||
import com.lzbi.common.core.page.TableSupport; |
|||
import com.lzbi.common.utils.sql.SqlUtil; |
|||
|
|||
/** |
|||
* 分页工具类 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
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,178 @@ |
|||
package com.lzbi.common.utils; |
|||
|
|||
import java.util.Collection; |
|||
import java.util.List; |
|||
import java.util.stream.Collectors; |
|||
import org.springframework.security.core.Authentication; |
|||
import org.springframework.security.core.context.SecurityContextHolder; |
|||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; |
|||
import org.springframework.util.PatternMatchUtils; |
|||
import com.lzbi.common.constant.Constants; |
|||
import com.lzbi.common.constant.HttpStatus; |
|||
import com.lzbi.common.core.domain.entity.SysRole; |
|||
import com.lzbi.common.core.domain.model.LoginUser; |
|||
import com.lzbi.common.exception.ServiceException; |
|||
|
|||
/** |
|||
* 安全服务工具类 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
public class SecurityUtils |
|||
{ |
|||
|
|||
/** |
|||
* 用户ID |
|||
**/ |
|||
public static Long getUserId() |
|||
{ |
|||
try |
|||
{ |
|||
return getLoginUser().getUserId(); |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
throw new ServiceException("获取用户ID异常", HttpStatus.UNAUTHORIZED); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 获取部门ID |
|||
**/ |
|||
public static Long getDeptId() |
|||
{ |
|||
try |
|||
{ |
|||
return getLoginUser().getDeptId(); |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
throw new ServiceException("获取部门ID异常", HttpStatus.UNAUTHORIZED); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 获取用户账户 |
|||
**/ |
|||
public static String getUsername() |
|||
{ |
|||
try |
|||
{ |
|||
return getLoginUser().getUsername(); |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
throw new ServiceException("获取用户账户异常", HttpStatus.UNAUTHORIZED); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 获取用户 |
|||
**/ |
|||
public static LoginUser getLoginUser() |
|||
{ |
|||
try |
|||
{ |
|||
return (LoginUser) getAuthentication().getPrincipal(); |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
throw new ServiceException("获取用户信息异常", HttpStatus.UNAUTHORIZED); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 获取Authentication |
|||
*/ |
|||
public static Authentication getAuthentication() |
|||
{ |
|||
return SecurityContextHolder.getContext().getAuthentication(); |
|||
} |
|||
|
|||
/** |
|||
* 生成BCryptPasswordEncoder密码 |
|||
* |
|||
* @param password 密码 |
|||
* @return 加密字符串 |
|||
*/ |
|||
public static String encryptPassword(String password) |
|||
{ |
|||
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); |
|||
return passwordEncoder.encode(password); |
|||
} |
|||
|
|||
/** |
|||
* 判断密码是否相同 |
|||
* |
|||
* @param rawPassword 真实密码 |
|||
* @param encodedPassword 加密后字符 |
|||
* @return 结果 |
|||
*/ |
|||
public static boolean matchesPassword(String rawPassword, String encodedPassword) |
|||
{ |
|||
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); |
|||
return passwordEncoder.matches(rawPassword, encodedPassword); |
|||
} |
|||
|
|||
/** |
|||
* 是否为管理员 |
|||
* |
|||
* @param userId 用户ID |
|||
* @return 结果 |
|||
*/ |
|||
public static boolean isAdmin(Long userId) |
|||
{ |
|||
return userId != null && 1L == userId; |
|||
} |
|||
|
|||
/** |
|||
* 验证用户是否具备某权限 |
|||
* |
|||
* @param permission 权限字符串 |
|||
* @return 用户是否具备某权限 |
|||
*/ |
|||
public static boolean hasPermi(String permission) |
|||
{ |
|||
return hasPermi(getLoginUser().getPermissions(), permission); |
|||
} |
|||
|
|||
/** |
|||
* 判断是否包含权限 |
|||
* |
|||
* @param authorities 权限列表 |
|||
* @param permission 权限字符串 |
|||
* @return 用户是否具备某权限 |
|||
*/ |
|||
public static boolean hasPermi(Collection<String> authorities, String permission) |
|||
{ |
|||
return authorities.stream().filter(StringUtils::hasText) |
|||
.anyMatch(x -> Constants.ALL_PERMISSION.equals(x) || PatternMatchUtils.simpleMatch(x, permission)); |
|||
} |
|||
|
|||
/** |
|||
* 验证用户是否拥有某个角色 |
|||
* |
|||
* @param role 角色标识 |
|||
* @return 用户是否具备某角色 |
|||
*/ |
|||
public static boolean hasRole(String role) |
|||
{ |
|||
List<SysRole> roleList = getLoginUser().getUser().getRoles(); |
|||
Collection<String> roles = roleList.stream().map(SysRole::getRoleKey).collect(Collectors.toSet()); |
|||
return hasRole(roles, role); |
|||
} |
|||
|
|||
/** |
|||
* 判断是否包含角色 |
|||
* |
|||
* @param roles 角色列表 |
|||
* @param role 角色 |
|||
* @return 用户是否具备某角色权限 |
|||
*/ |
|||
public static boolean hasRole(Collection<String> roles, String role) |
|||
{ |
|||
return roles.stream().filter(StringUtils::hasText) |
|||
.anyMatch(x -> Constants.SUPER_ADMIN.equals(x) || PatternMatchUtils.simpleMatch(x, role)); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,218 @@ |
|||
package com.lzbi.common.utils; |
|||
|
|||
import java.io.IOException; |
|||
import java.io.UnsupportedEncodingException; |
|||
import java.net.URLDecoder; |
|||
import java.net.URLEncoder; |
|||
import java.util.Collections; |
|||
import java.util.HashMap; |
|||
import java.util.Map; |
|||
import javax.servlet.ServletRequest; |
|||
import javax.servlet.http.HttpServletRequest; |
|||
import javax.servlet.http.HttpServletResponse; |
|||
import javax.servlet.http.HttpSession; |
|||
import org.springframework.web.context.request.RequestAttributes; |
|||
import org.springframework.web.context.request.RequestContextHolder; |
|||
import org.springframework.web.context.request.ServletRequestAttributes; |
|||
import com.lzbi.common.constant.Constants; |
|||
import com.lzbi.common.core.text.Convert; |
|||
|
|||
/** |
|||
* 客户端工具类 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
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() |
|||
{ |
|||
return getRequestAttributes().getRequest(); |
|||
} |
|||
|
|||
/** |
|||
* 获取response |
|||
*/ |
|||
public static HttpServletResponse getResponse() |
|||
{ |
|||
return getRequestAttributes().getResponse(); |
|||
} |
|||
|
|||
/** |
|||
* 获取session |
|||
*/ |
|||
public static HttpSession getSession() |
|||
{ |
|||
return getRequest().getSession(); |
|||
} |
|||
|
|||
public static ServletRequestAttributes getRequestAttributes() |
|||
{ |
|||
RequestAttributes attributes = RequestContextHolder.getRequestAttributes(); |
|||
return (ServletRequestAttributes) attributes; |
|||
} |
|||
|
|||
/** |
|||
* 将字符串渲染到客户端 |
|||
* |
|||
* @param response 渲染对象 |
|||
* @param string 待渲染的字符串 |
|||
*/ |
|||
public static void renderString(HttpServletResponse response, String string) |
|||
{ |
|||
try |
|||
{ |
|||
response.setStatus(200); |
|||
response.setContentType("application/json"); |
|||
response.setCharacterEncoding("utf-8"); |
|||
response.getWriter().print(string); |
|||
} |
|||
catch (IOException e) |
|||
{ |
|||
e.printStackTrace(); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 是否是Ajax异步请求 |
|||
* |
|||
* @param request |
|||
*/ |
|||
public static boolean isAjaxRequest(HttpServletRequest request) |
|||
{ |
|||
String accept = request.getHeader("accept"); |
|||
if (accept != null && accept.contains("application/json")) |
|||
{ |
|||
return true; |
|||
} |
|||
|
|||
String xRequestedWith = request.getHeader("X-Requested-With"); |
|||
if (xRequestedWith != null && xRequestedWith.contains("XMLHttpRequest")) |
|||
{ |
|||
return true; |
|||
} |
|||
|
|||
String uri = request.getRequestURI(); |
|||
if (StringUtils.inStringIgnoreCase(uri, ".json", ".xml")) |
|||
{ |
|||
return true; |
|||
} |
|||
|
|||
String ajax = request.getParameter("__ajax"); |
|||
return StringUtils.inStringIgnoreCase(ajax, "json", "xml"); |
|||
} |
|||
|
|||
/** |
|||
* 内容编码 |
|||
* |
|||
* @param str 内容 |
|||
* @return 编码后的内容 |
|||
*/ |
|||
public static String urlEncode(String str) |
|||
{ |
|||
try |
|||
{ |
|||
return URLEncoder.encode(str, Constants.UTF8); |
|||
} |
|||
catch (UnsupportedEncodingException e) |
|||
{ |
|||
return StringUtils.EMPTY; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 内容解码 |
|||
* |
|||
* @param str 内容 |
|||
* @return 解码后的内容 |
|||
*/ |
|||
public static String urlDecode(String str) |
|||
{ |
|||
try |
|||
{ |
|||
return URLDecoder.decode(str, Constants.UTF8); |
|||
} |
|||
catch (UnsupportedEncodingException e) |
|||
{ |
|||
return StringUtils.EMPTY; |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,715 @@ |
|||
package com.lzbi.common.utils; |
|||
|
|||
import java.util.ArrayList; |
|||
import java.util.Collection; |
|||
import java.util.HashSet; |
|||
import java.util.List; |
|||
import java.util.Map; |
|||
import java.util.Set; |
|||
import org.springframework.util.AntPathMatcher; |
|||
import com.lzbi.common.constant.Constants; |
|||
import com.lzbi.common.core.text.StrFormatter; |
|||
|
|||
/** |
|||
* 字符串工具类 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
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); |
|||
} |
|||
|
|||
/** |
|||
* 字符串转set |
|||
* |
|||
* @param str 字符串 |
|||
* @param sep 分隔符 |
|||
* @return set集合 |
|||
*/ |
|||
public static final Set<String> str2Set(String str, String sep) |
|||
{ |
|||
return new HashSet<String>(str2List(str, sep, true, false)); |
|||
} |
|||
|
|||
/** |
|||
* 字符串转list |
|||
* |
|||
* @param str 字符串 |
|||
* @param sep 分隔符 |
|||
* @param filterBlank 过滤纯空白 |
|||
* @param trim 去掉首尾空白 |
|||
* @return list集合 |
|||
*/ |
|||
public static final List<String> str2List(String str, String sep, boolean filterBlank, boolean trim) |
|||
{ |
|||
List<String> list = new ArrayList<String>(); |
|||
if (StringUtils.isEmpty(str)) |
|||
{ |
|||
return list; |
|||
} |
|||
|
|||
// 过滤空白字符串
|
|||
if (filterBlank && StringUtils.isBlank(str)) |
|||
{ |
|||
return list; |
|||
} |
|||
String[] split = str.split(sep); |
|||
for (String string : split) |
|||
{ |
|||
if (filterBlank && StringUtils.isBlank(string)) |
|||
{ |
|||
continue; |
|||
} |
|||
if (trim) |
|||
{ |
|||
string = string.trim(); |
|||
} |
|||
list.add(string); |
|||
} |
|||
|
|||
return list; |
|||
} |
|||
|
|||
/** |
|||
* 判断给定的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; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 查找指定字符串是否包含指定字符串列表中的任意一个字符串同时串忽略大小写 |
|||
* |
|||
* @param cs 指定字符串 |
|||
* @param searchCharSequences 需要检查的字符串数组 |
|||
* @return 是否包含任意一个字符串 |
|||
*/ |
|||
public static boolean containsAnyIgnoreCase(CharSequence cs, CharSequence... searchCharSequences) |
|||
{ |
|||
if (isEmpty(cs) || isEmpty(searchCharSequences)) |
|||
{ |
|||
return false; |
|||
} |
|||
for (CharSequence testStr : searchCharSequences) |
|||
{ |
|||
if (containsIgnoreCase(cs, testStr)) |
|||
{ |
|||
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(); |
|||
} |
|||
/* |
|||
* add by zhousq 2023-08-15 |
|||
* 获取数据类型 |
|||
* */ |
|||
public static Object getVarType(String code) { |
|||
if ("3".equals(code)) { |
|||
return Double.class; |
|||
}else if ("2".equals(code)) { |
|||
return String.class; |
|||
}else if ("1".equals(code)) { |
|||
return Integer.class; |
|||
}else if ("4".equals(code)) { |
|||
return Boolean.class; |
|||
}else{ |
|||
return String.class;} |
|||
} |
|||
public static String getVarTypeStr(String val){ |
|||
if(isDouble(val)){ |
|||
return "3"; |
|||
}else if(isNumeric(val)){ |
|||
return "1"; |
|||
}else if(isBooean(val)){ |
|||
return "4"; |
|||
}else{ |
|||
return "2"; |
|||
} |
|||
} |
|||
public static double getfomatDouble(double val,String format){ |
|||
String temp = String.format(format,val); |
|||
try{ return Double.parseDouble(temp); |
|||
}catch(Exception e){ |
|||
return val; |
|||
} |
|||
|
|||
} |
|||
|
|||
public static double getfomatDouble(double val){ |
|||
return getfomatDouble(val, "%.2f") ; |
|||
} |
|||
|
|||
/** |
|||
* 判断字符串是不是double型 |
|||
* @param str |
|||
* @return |
|||
*/ |
|||
public static boolean isNumeric(String str) { |
|||
return str.matches("-?\\d+") ; |
|||
|
|||
} |
|||
/** |
|||
* 判断字符串是不是科学计数法 |
|||
* @param number |
|||
* @return |
|||
*/ |
|||
public static boolean isScientificNotation(String number) { |
|||
//-?\d+(\.\d+)?[eE]\d+
|
|||
return number.toLowerCase().matches("^((-?\\d+.?\\d*)[Ee]{1}(-?\\d+))$"); |
|||
} |
|||
public static boolean isDouble(String str){ |
|||
return str.matches("-?\\d+(\\.\\d+)?"); |
|||
} |
|||
public static boolean isBooean(String str){ |
|||
if(str.equalsIgnoreCase("true") || str.equalsIgnoreCase("false")){ |
|||
return true; |
|||
}else{ |
|||
return false; |
|||
} |
|||
} |
|||
public static String getStandDouble(String value){ |
|||
String tmp=value.substring(value.indexOf(".")+1); |
|||
if(StringUtils.isNotNull(tmp) && tmp.length()>7) { |
|||
tmp=value.substring(0,value.indexOf(".")+6); |
|||
}else{ |
|||
tmp=value; |
|||
} |
|||
return tmp; |
|||
} |
|||
} |
@ -0,0 +1,99 @@ |
|||
package com.lzbi.common.utils; |
|||
|
|||
import java.util.concurrent.CancellationException; |
|||
import java.util.concurrent.ExecutionException; |
|||
import java.util.concurrent.ExecutorService; |
|||
import java.util.concurrent.Future; |
|||
import java.util.concurrent.TimeUnit; |
|||
import org.slf4j.Logger; |
|||
import org.slf4j.LoggerFactory; |
|||
|
|||
/** |
|||
* 线程相关工具类. |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
public class Threads |
|||
{ |
|||
private static final Logger logger = LoggerFactory.getLogger(Threads.class); |
|||
|
|||
/** |
|||
* sleep等待,单位为毫秒 |
|||
*/ |
|||
public static void sleep(long milliseconds) |
|||
{ |
|||
try |
|||
{ |
|||
Thread.sleep(milliseconds); |
|||
} |
|||
catch (InterruptedException e) |
|||
{ |
|||
return; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 停止线程池 |
|||
* 先使用shutdown, 停止接收新任务并尝试完成所有已存在任务. |
|||
* 如果超时, 则调用shutdownNow, 取消在workQueue中Pending的任务,并中断所有阻塞函数. |
|||
* 如果仍然超時,則強制退出. |
|||
* 另对在shutdown时线程本身被调用中断做了处理. |
|||
*/ |
|||
public static void shutdownAndAwaitTermination(ExecutorService pool) |
|||
{ |
|||
if (pool != null && !pool.isShutdown()) |
|||
{ |
|||
pool.shutdown(); |
|||
try |
|||
{ |
|||
if (!pool.awaitTermination(120, TimeUnit.SECONDS)) |
|||
{ |
|||
pool.shutdownNow(); |
|||
if (!pool.awaitTermination(120, TimeUnit.SECONDS)) |
|||
{ |
|||
logger.info("Pool did not terminate"); |
|||
} |
|||
} |
|||
} |
|||
catch (InterruptedException ie) |
|||
{ |
|||
pool.shutdownNow(); |
|||
Thread.currentThread().interrupt(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 打印线程异常信息 |
|||
*/ |
|||
public static void printException(Runnable r, Throwable t) |
|||
{ |
|||
if (t == null && r instanceof Future<?>) |
|||
{ |
|||
try |
|||
{ |
|||
Future<?> future = (Future<?>) r; |
|||
if (future.isDone()) |
|||
{ |
|||
future.get(); |
|||
} |
|||
} |
|||
catch (CancellationException ce) |
|||
{ |
|||
t = ce; |
|||
} |
|||
catch (ExecutionException ee) |
|||
{ |
|||
t = ee.getCause(); |
|||
} |
|||
catch (InterruptedException ie) |
|||
{ |
|||
Thread.currentThread().interrupt(); |
|||
} |
|||
} |
|||
if (t != null) |
|||
{ |
|||
logger.error(t.getMessage(), t); |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,76 @@ |
|||
package com.lzbi.common.utils.file; |
|||
|
|||
import java.io.File; |
|||
import org.apache.commons.lang3.StringUtils; |
|||
|
|||
/** |
|||
* 文件类型工具类 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
public class FileTypeUtils |
|||
{ |
|||
/** |
|||
* 获取文件类型 |
|||
* <p> |
|||
* 例如: ruoyi.txt, 返回: txt |
|||
* |
|||
* @param file 文件名 |
|||
* @return 后缀(不含".") |
|||
*/ |
|||
public static String getFileType(File file) |
|||
{ |
|||
if (null == file) |
|||
{ |
|||
return StringUtils.EMPTY; |
|||
} |
|||
return getFileType(file.getName()); |
|||
} |
|||
|
|||
/** |
|||
* 获取文件类型 |
|||
* <p> |
|||
* 例如: ruoyi.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 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,232 @@ |
|||
package com.lzbi.common.utils.file; |
|||
|
|||
import java.io.File; |
|||
import java.io.IOException; |
|||
import java.nio.file.Paths; |
|||
import java.util.Objects; |
|||
import org.apache.commons.io.FilenameUtils; |
|||
import org.springframework.web.multipart.MultipartFile; |
|||
import com.lzbi.common.config.RuoYiConfig; |
|||
import com.lzbi.common.constant.Constants; |
|||
import com.lzbi.common.exception.file.FileNameLengthLimitExceededException; |
|||
import com.lzbi.common.exception.file.FileSizeLimitExceededException; |
|||
import com.lzbi.common.exception.file.InvalidExtensionException; |
|||
import com.lzbi.common.utils.DateUtils; |
|||
import com.lzbi.common.utils.StringUtils; |
|||
import com.lzbi.common.utils.uuid.Seq; |
|||
|
|||
/** |
|||
* 文件上传工具类 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
public class FileUploadUtils |
|||
{ |
|||
/** |
|||
* 默认大小 50M |
|||
*/ |
|||
public static final long DEFAULT_MAX_SIZE = 50 * 1024 * 1024; |
|||
|
|||
/** |
|||
* 默认的文件名最大长度 100 |
|||
*/ |
|||
public static final int DEFAULT_FILE_NAME_LENGTH = 100; |
|||
|
|||
/** |
|||
* 默认上传的地址 |
|||
*/ |
|||
private static String defaultBaseDir = RuoYiConfig.getProfile(); |
|||
|
|||
public static void setDefaultBaseDir(String defaultBaseDir) |
|||
{ |
|||
FileUploadUtils.defaultBaseDir = defaultBaseDir; |
|||
} |
|||
|
|||
public static String getDefaultBaseDir() |
|||
{ |
|||
return defaultBaseDir; |
|||
} |
|||
|
|||
/** |
|||
* 以默认配置进行文件上传 |
|||
* |
|||
* @param file 上传的文件 |
|||
* @return 文件名称 |
|||
* @throws Exception |
|||
*/ |
|||
public static final String upload(MultipartFile file) throws IOException |
|||
{ |
|||
try |
|||
{ |
|||
return upload(getDefaultBaseDir(), file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION); |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
throw new IOException(e.getMessage(), e); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 根据文件路径上传 |
|||
* |
|||
* @param baseDir 相对应用的基目录 |
|||
* @param file 上传的文件 |
|||
* @return 文件名称 |
|||
* @throws IOException |
|||
*/ |
|||
public static final String upload(String baseDir, MultipartFile file) throws IOException |
|||
{ |
|||
try |
|||
{ |
|||
return upload(baseDir, file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION); |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
throw new IOException(e.getMessage(), e); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 文件上传 |
|||
* |
|||
* @param baseDir 相对应用的基目录 |
|||
* @param file 上传的文件 |
|||
* @param allowedExtension 上传文件类型 |
|||
* @return 返回上传成功的文件名 |
|||
* @throws FileSizeLimitExceededException 如果超出最大大小 |
|||
* @throws FileNameLengthLimitExceededException 文件名太长 |
|||
* @throws IOException 比如读写文件出错时 |
|||
* @throws InvalidExtensionException 文件校验异常 |
|||
*/ |
|||
public static final String upload(String baseDir, MultipartFile file, String[] allowedExtension) |
|||
throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException, |
|||
InvalidExtensionException |
|||
{ |
|||
int fileNamelength = Objects.requireNonNull(file.getOriginalFilename()).length(); |
|||
if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH) |
|||
{ |
|||
throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH); |
|||
} |
|||
|
|||
assertAllowed(file, allowedExtension); |
|||
|
|||
String fileName = extractFilename(file); |
|||
|
|||
String absPath = getAbsoluteFile(baseDir, fileName).getAbsolutePath(); |
|||
file.transferTo(Paths.get(absPath)); |
|||
return getPathFileName(baseDir, fileName); |
|||
} |
|||
|
|||
/** |
|||
* 编码文件名 |
|||
*/ |
|||
public static final String extractFilename(MultipartFile file) |
|||
{ |
|||
return StringUtils.format("{}/{}_{}.{}", DateUtils.datePath(), |
|||
FilenameUtils.getBaseName(file.getOriginalFilename()), Seq.getId(Seq.uploadSeqType), getExtension(file)); |
|||
} |
|||
|
|||
public static final File getAbsoluteFile(String uploadDir, String fileName) throws IOException |
|||
{ |
|||
File desc = new File(uploadDir + File.separator + fileName); |
|||
|
|||
if (!desc.exists()) |
|||
{ |
|||
if (!desc.getParentFile().exists()) |
|||
{ |
|||
desc.getParentFile().mkdirs(); |
|||
} |
|||
} |
|||
return desc; |
|||
} |
|||
|
|||
public static final String getPathFileName(String uploadDir, String fileName) throws IOException |
|||
{ |
|||
int dirLastIndex = RuoYiConfig.getProfile().length() + 1; |
|||
String currentDir = StringUtils.substring(uploadDir, dirLastIndex); |
|||
return Constants.RESOURCE_PREFIX + "/" + currentDir + "/" + fileName; |
|||
} |
|||
|
|||
/** |
|||
* 文件大小校验 |
|||
* |
|||
* @param file 上传的文件 |
|||
* @return |
|||
* @throws FileSizeLimitExceededException 如果超出最大大小 |
|||
* @throws InvalidExtensionException |
|||
*/ |
|||
public static final void assertAllowed(MultipartFile file, String[] allowedExtension) |
|||
throws FileSizeLimitExceededException, InvalidExtensionException |
|||
{ |
|||
long size = file.getSize(); |
|||
if (size > DEFAULT_MAX_SIZE) |
|||
{ |
|||
throw new FileSizeLimitExceededException(DEFAULT_MAX_SIZE / 1024 / 1024); |
|||
} |
|||
|
|||
String fileName = file.getOriginalFilename(); |
|||
String extension = getExtension(file); |
|||
if (allowedExtension != null && !isAllowedExtension(extension, allowedExtension)) |
|||
{ |
|||
if (allowedExtension == MimeTypeUtils.IMAGE_EXTENSION) |
|||
{ |
|||
throw new InvalidExtensionException.InvalidImageExtensionException(allowedExtension, extension, |
|||
fileName); |
|||
} |
|||
else if (allowedExtension == MimeTypeUtils.FLASH_EXTENSION) |
|||
{ |
|||
throw new InvalidExtensionException.InvalidFlashExtensionException(allowedExtension, extension, |
|||
fileName); |
|||
} |
|||
else if (allowedExtension == MimeTypeUtils.MEDIA_EXTENSION) |
|||
{ |
|||
throw new InvalidExtensionException.InvalidMediaExtensionException(allowedExtension, extension, |
|||
fileName); |
|||
} |
|||
else if (allowedExtension == MimeTypeUtils.VIDEO_EXTENSION) |
|||
{ |
|||
throw new InvalidExtensionException.InvalidVideoExtensionException(allowedExtension, extension, |
|||
fileName); |
|||
} |
|||
else |
|||
{ |
|||
throw new InvalidExtensionException(allowedExtension, extension, fileName); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 判断MIME类型是否是允许的MIME类型 |
|||
* |
|||
* @param extension |
|||
* @param allowedExtension |
|||
* @return |
|||
*/ |
|||
public static final boolean isAllowedExtension(String extension, String[] allowedExtension) |
|||
{ |
|||
for (String str : allowedExtension) |
|||
{ |
|||
if (str.equalsIgnoreCase(extension)) |
|||
{ |
|||
return true; |
|||
} |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
/** |
|||
* 获取文件名的后缀 |
|||
* |
|||
* @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; |
|||
} |
|||
} |
@ -0,0 +1,291 @@ |
|||
package com.lzbi.common.utils.file; |
|||
|
|||
import java.io.File; |
|||
import java.io.FileInputStream; |
|||
import java.io.FileNotFoundException; |
|||
import java.io.FileOutputStream; |
|||
import java.io.IOException; |
|||
import java.io.OutputStream; |
|||
import java.io.UnsupportedEncodingException; |
|||
import java.net.URLEncoder; |
|||
import java.nio.charset.StandardCharsets; |
|||
import javax.servlet.http.HttpServletRequest; |
|||
import javax.servlet.http.HttpServletResponse; |
|||
import org.apache.commons.io.IOUtils; |
|||
import org.apache.commons.lang3.ArrayUtils; |
|||
import com.lzbi.common.config.RuoYiConfig; |
|||
import com.lzbi.common.utils.DateUtils; |
|||
import com.lzbi.common.utils.StringUtils; |
|||
import com.lzbi.common.utils.uuid.IdUtils; |
|||
import org.apache.commons.io.FilenameUtils; |
|||
|
|||
/** |
|||
* 文件处理工具类 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
public class FileUtils |
|||
{ |
|||
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 |
|||
{ |
|||
IOUtils.close(os); |
|||
IOUtils.close(fis); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 写数据到文件中 |
|||
* |
|||
* @param data 数据 |
|||
* @return 目标文件 |
|||
* @throws IOException IO异常 |
|||
*/ |
|||
public static String writeImportBytes(byte[] data) throws IOException |
|||
{ |
|||
return writeBytes(data, RuoYiConfig.getImportPath()); |
|||
} |
|||
|
|||
/** |
|||
* 写数据到文件中 |
|||
* |
|||
* @param data 数据 |
|||
* @param uploadDir 目标文件 |
|||
* @return 目标文件 |
|||
* @throws IOException IO异常 |
|||
*/ |
|||
public static String writeBytes(byte[] data, String uploadDir) throws IOException |
|||
{ |
|||
FileOutputStream fos = null; |
|||
String pathName = ""; |
|||
try |
|||
{ |
|||
String extension = getFileExtendName(data); |
|||
pathName = DateUtils.datePath() + "/" + IdUtils.fastUUID() + "." + extension; |
|||
File file = FileUploadUtils.getAbsoluteFile(uploadDir, pathName); |
|||
fos = new FileOutputStream(file); |
|||
fos.write(data); |
|||
} |
|||
finally |
|||
{ |
|||
IOUtils.close(fos); |
|||
} |
|||
return FileUploadUtils.getPathFileName(uploadDir, pathName); |
|||
} |
|||
|
|||
/** |
|||
* 删除文件 |
|||
* |
|||
* @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; |
|||
} |
|||
|
|||
// 检查允许下载的文件规则
|
|||
if (ArrayUtils.contains(MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION, FileTypeUtils.getFileType(resource))) |
|||
{ |
|||
return true; |
|||
} |
|||
|
|||
// 不在允许下载的文件规则
|
|||
return false; |
|||
} |
|||
|
|||
/** |
|||
* 下载文件名重新编码 |
|||
* |
|||
* @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 response 响应对象 |
|||
* @param realFileName 真实文件名 |
|||
*/ |
|||
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.addHeader("Access-Control-Expose-Headers", "Content-Disposition,download-filename"); |
|||
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"); |
|||
} |
|||
|
|||
/** |
|||
* 获取图像后缀 |
|||
* |
|||
* @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; |
|||
} |
|||
|
|||
/** |
|||
* 获取文件名称 /profile/upload/2022/04/16/ruoyi.png -- ruoyi.png |
|||
* |
|||
* @param fileName 路径名称 |
|||
* @return 没有文件路径的名称 |
|||
*/ |
|||
public static String getName(String fileName) |
|||
{ |
|||
if (fileName == null) |
|||
{ |
|||
return null; |
|||
} |
|||
int lastUnixPos = fileName.lastIndexOf('/'); |
|||
int lastWindowsPos = fileName.lastIndexOf('\\'); |
|||
int index = Math.max(lastUnixPos, lastWindowsPos); |
|||
return fileName.substring(index + 1); |
|||
} |
|||
|
|||
/** |
|||
* 获取不带后缀文件名称 /profile/upload/2022/04/16/ruoyi.png -- ruoyi |
|||
* |
|||
* @param fileName 路径名称 |
|||
* @return 没有文件路径和后缀的名称 |
|||
*/ |
|||
public static String getNameNotSuffix(String fileName) |
|||
{ |
|||
if (fileName == null) |
|||
{ |
|||
return null; |
|||
} |
|||
String baseName = FilenameUtils.getBaseName(fileName); |
|||
return baseName; |
|||
} |
|||
} |
@ -0,0 +1,98 @@ |
|||
package com.lzbi.common.utils.file; |
|||
|
|||
import java.io.ByteArrayInputStream; |
|||
import java.io.FileInputStream; |
|||
import java.io.InputStream; |
|||
import java.net.URL; |
|||
import java.net.URLConnection; |
|||
import java.util.Arrays; |
|||
import org.apache.poi.util.IOUtils; |
|||
import org.slf4j.Logger; |
|||
import org.slf4j.LoggerFactory; |
|||
import com.lzbi.common.config.RuoYiConfig; |
|||
import com.lzbi.common.constant.Constants; |
|||
import com.lzbi.common.utils.StringUtils; |
|||
|
|||
/** |
|||
* 图片处理工具类 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
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 |
|||
{ |
|||
if (url.startsWith("http")) |
|||
{ |
|||
// 网络地址
|
|||
URL urlObj = new URL(url); |
|||
URLConnection urlConnection = urlObj.openConnection(); |
|||
urlConnection.setConnectTimeout(30 * 1000); |
|||
urlConnection.setReadTimeout(60 * 1000); |
|||
urlConnection.setDoInput(true); |
|||
in = urlConnection.getInputStream(); |
|||
} |
|||
else |
|||
{ |
|||
// 本机地址
|
|||
String localPath = RuoYiConfig.getProfile(); |
|||
String downloadPath = localPath + StringUtils.substringAfter(url, Constants.RESOURCE_PREFIX); |
|||
in = new FileInputStream(downloadPath); |
|||
} |
|||
return IOUtils.toByteArray(in); |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
log.error("获取文件路径异常 {}", e); |
|||
return null; |
|||
} |
|||
finally |
|||
{ |
|||
IOUtils.closeQuietly(in); |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,59 @@ |
|||
package com.lzbi.common.utils.file; |
|||
|
|||
/** |
|||
* 媒体类型工具类 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
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,167 @@ |
|||
package com.lzbi.common.utils.html; |
|||
|
|||
import com.lzbi.common.utils.StringUtils; |
|||
|
|||
/** |
|||
* 转义和反转义工具类 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
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,570 @@ |
|||
package com.lzbi.common.utils.html; |
|||
|
|||
import java.util.ArrayList; |
|||
import java.util.Collections; |
|||
import java.util.HashMap; |
|||
import java.util.List; |
|||
import java.util.Map; |
|||
import java.util.concurrent.ConcurrentHashMap; |
|||
import java.util.concurrent.ConcurrentMap; |
|||
import java.util.regex.Matcher; |
|||
import java.util.regex.Pattern; |
|||
|
|||
/** |
|||
* HTML过滤器,用于去除XSS漏洞隐患。 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
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; |
|||
} |
|||
|
|||
private void reset() |
|||
{ |
|||
vTagCounts.clear(); |
|||
} |
|||
|
|||
// ---------------------------------------------------------------
|
|||
// my versions of some PHP library functions
|
|||
public static String chr(final int decimal) |
|||
{ |
|||
return String.valueOf((char) decimal); |
|||
} |
|||
|
|||
public static String htmlSpecialChars(final String s) |
|||
{ |
|||
String result = s; |
|||
result = regexReplace(P_AMP, "&", result); |
|||
result = regexReplace(P_QUOTE, """, result); |
|||
result = regexReplace(P_LEFT_ARROW, "<", result); |
|||
result = regexReplace(P_RIGHT_ARROW, ">", result); |
|||
return result; |
|||
} |
|||
|
|||
// ---------------------------------------------------------------
|
|||
|
|||
/** |
|||
* given a user submitted input String, filter out any invalid or restricted html. |
|||
* |
|||
* @param input text (i.e. submitted by a user) than may contain html |
|||
* @return "clean" version of input, with only valid, whitelisted html elements allowed |
|||
*/ |
|||
public String filter(final String input) |
|||
{ |
|||
reset(); |
|||
String s = input; |
|||
|
|||
s = escapeComments(s); |
|||
|
|||
s = balanceHTML(s); |
|||
|
|||
s = checkTags(s); |
|||
|
|||
s = processRemoveBlanks(s); |
|||
|
|||
// s = validateEntities(s);
|
|||
|
|||
return s; |
|||
} |
|||
|
|||
public boolean 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 static String regexReplace(final Pattern regex_pattern, final String replacement, final String s) |
|||
{ |
|||
Matcher m = regex_pattern.matcher(s); |
|||
return m.replaceAll(replacement); |
|||
} |
|||
|
|||
private String processTag(final String s) |
|||
{ |
|||
// ending tags
|
|||
Matcher m = P_END_TAG.matcher(s); |
|||
if (m.find()) |
|||
{ |
|||
final String name = m.group(1).toLowerCase(); |
|||
if (allowed(name)) |
|||
{ |
|||
if (!inArray(name, vSelfClosingTags)) |
|||
{ |
|||
if (vTagCounts.containsKey(name)) |
|||
{ |
|||
vTagCounts.put(name, vTagCounts.get(name) - 1); |
|||
return "</" + name + ">"; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
// starting tags
|
|||
m = P_START_TAG.matcher(s); |
|||
if (m.find()) |
|||
{ |
|||
final String name = m.group(1).toLowerCase(); |
|||
final String body = m.group(2); |
|||
String ending = m.group(3); |
|||
|
|||
// debug( "in a starting tag, name='" + name + "'; body='" + body + "'; ending='" + ending + "'" );
|
|||
if (allowed(name)) |
|||
{ |
|||
final StringBuilder params = new StringBuilder(); |
|||
|
|||
final Matcher m2 = P_QUOTED_ATTRIBUTES.matcher(body); |
|||
final Matcher m3 = P_UNQUOTED_ATTRIBUTES.matcher(body); |
|||
final List<String> paramNames = new ArrayList<>(); |
|||
final List<String> paramValues = new ArrayList<>(); |
|||
while (m2.find()) |
|||
{ |
|||
paramNames.add(m2.group(1)); // ([a-z0-9]+)
|
|||
paramValues.add(m2.group(3)); // (.*?)
|
|||
} |
|||
while (m3.find()) |
|||
{ |
|||
paramNames.add(m3.group(1)); // ([a-z0-9]+)
|
|||
paramValues.add(m3.group(3)); // ([^\"\\s']+)
|
|||
} |
|||
|
|||
String paramName, paramValue; |
|||
for (int ii = 0; ii < paramNames.size(); ii++) |
|||
{ |
|||
paramName = paramNames.get(ii).toLowerCase(); |
|||
paramValue = paramValues.get(ii); |
|||
|
|||
// debug( "paramName='" + paramName + "'" );
|
|||
// debug( "paramValue='" + paramValue + "'" );
|
|||
// debug( "allowed? " + vAllowed.get( name ).contains( paramName ) );
|
|||
|
|||
if (allowedAttribute(name, paramName)) |
|||
{ |
|||
if (inArray(paramName, vProtocolAtts)) |
|||
{ |
|||
paramValue = processParamProtocol(paramValue); |
|||
} |
|||
params.append(' ').append(paramName).append("=\\\"").append(paramValue).append("\\\""); |
|||
} |
|||
} |
|||
|
|||
if (inArray(name, vSelfClosingTags)) |
|||
{ |
|||
ending = " /"; |
|||
} |
|||
|
|||
if (inArray(name, vNeedClosingTags)) |
|||
{ |
|||
ending = ""; |
|||
} |
|||
|
|||
if (ending == null || ending.length() < 1) |
|||
{ |
|||
if (vTagCounts.containsKey(name)) |
|||
{ |
|||
vTagCounts.put(name, vTagCounts.get(name) + 1); |
|||
} |
|||
else |
|||
{ |
|||
vTagCounts.put(name, 1); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
ending = " /"; |
|||
} |
|||
return "<" + name + params + ending + ">"; |
|||
} |
|||
else |
|||
{ |
|||
return ""; |
|||
} |
|||
} |
|||
|
|||
// comments
|
|||
m = P_COMMENT.matcher(s); |
|||
if (!stripComment && m.find()) |
|||
{ |
|||
return "<" + m.group() + ">"; |
|||
} |
|||
|
|||
return ""; |
|||
} |
|||
|
|||
private String processParamProtocol(String s) |
|||
{ |
|||
s = decodeEntities(s); |
|||
final Matcher m = P_PROTOCOL.matcher(s); |
|||
if (m.find()) |
|||
{ |
|||
final String protocol = m.group(1); |
|||
if (!inArray(protocol, vAllowedProtocols)) |
|||
{ |
|||
// bad protocol, turn into local anchor link instead
|
|||
s = "#" + s.substring(protocol.length() + 1); |
|||
if (s.startsWith("#//")) |
|||
{ |
|||
s = "#" + s.substring(3); |
|||
} |
|||
} |
|||
} |
|||
|
|||
return s; |
|||
} |
|||
|
|||
private String decodeEntities(String s) |
|||
{ |
|||
StringBuffer buf = new StringBuffer(); |
|||
|
|||
Matcher m = P_ENTITY.matcher(s); |
|||
while (m.find()) |
|||
{ |
|||
final String match = m.group(1); |
|||
final int decimal = Integer.decode(match).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 static boolean inArray(final String s, final String[] array) |
|||
{ |
|||
for (String item : array) |
|||
{ |
|||
if (item != null && item.equals(s)) |
|||
{ |
|||
return true; |
|||
} |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
private boolean allowed(final String name) |
|||
{ |
|||
return (vAllowed.isEmpty() || vAllowed.containsKey(name)) && !inArray(name, vDisallowed); |
|||
} |
|||
|
|||
private boolean allowedAttribute(final String name, final String paramName) |
|||
{ |
|||
return allowed(name) && (vAllowed.isEmpty() || vAllowed.get(name).contains(paramName)); |
|||
} |
|||
} |
@ -0,0 +1,55 @@ |
|||
package com.lzbi.common.utils.http; |
|||
|
|||
import java.io.BufferedReader; |
|||
import java.io.IOException; |
|||
import java.io.InputStream; |
|||
import java.io.InputStreamReader; |
|||
import java.nio.charset.StandardCharsets; |
|||
import javax.servlet.ServletRequest; |
|||
import org.apache.commons.lang3.exception.ExceptionUtils; |
|||
import org.slf4j.Logger; |
|||
import org.slf4j.LoggerFactory; |
|||
|
|||
/** |
|||
* 通用http工具封装 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
public class HttpHelper |
|||
{ |
|||
private static final Logger LOGGER = LoggerFactory.getLogger(HttpHelper.class); |
|||
|
|||
public static String getBodyString(ServletRequest request) |
|||
{ |
|||
StringBuilder sb = new StringBuilder(); |
|||
BufferedReader reader = null; |
|||
try (InputStream inputStream = request.getInputStream()) |
|||
{ |
|||
reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); |
|||
String line = ""; |
|||
while ((line = reader.readLine()) != null) |
|||
{ |
|||
sb.append(line); |
|||
} |
|||
} |
|||
catch (IOException e) |
|||
{ |
|||
LOGGER.warn("getBodyString出现问题!"); |
|||
} |
|||
finally |
|||
{ |
|||
if (reader != null) |
|||
{ |
|||
try |
|||
{ |
|||
reader.close(); |
|||
} |
|||
catch (IOException e) |
|||
{ |
|||
LOGGER.error(ExceptionUtils.getMessage(e)); |
|||
} |
|||
} |
|||
} |
|||
return sb.toString(); |
|||
} |
|||
} |
@ -0,0 +1,274 @@ |
|||
package com.lzbi.common.utils.http; |
|||
|
|||
import java.io.BufferedReader; |
|||
import java.io.IOException; |
|||
import java.io.InputStream; |
|||
import java.io.InputStreamReader; |
|||
import java.io.PrintWriter; |
|||
import java.net.ConnectException; |
|||
import java.net.SocketTimeoutException; |
|||
import java.net.URL; |
|||
import java.net.URLConnection; |
|||
import java.nio.charset.StandardCharsets; |
|||
import java.security.cert.X509Certificate; |
|||
import javax.net.ssl.HostnameVerifier; |
|||
import javax.net.ssl.HttpsURLConnection; |
|||
import javax.net.ssl.SSLContext; |
|||
import javax.net.ssl.SSLSession; |
|||
import javax.net.ssl.TrustManager; |
|||
import javax.net.ssl.X509TrustManager; |
|||
import org.slf4j.Logger; |
|||
import org.slf4j.LoggerFactory; |
|||
import com.lzbi.common.constant.Constants; |
|||
import com.lzbi.common.utils.StringUtils; |
|||
|
|||
/** |
|||
* 通用http发送方法 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
public class HttpUtils |
|||
{ |
|||
private static final Logger log = LoggerFactory.getLogger(HttpUtils.class); |
|||
|
|||
/** |
|||
* 向指定 URL 发送GET方法的请求 |
|||
* |
|||
* @param url 发送请求的 URL |
|||
* @return 所代表远程资源的响应结果 |
|||
*/ |
|||
public static String sendGet(String url) |
|||
{ |
|||
return sendGet(url, StringUtils.EMPTY); |
|||
} |
|||
|
|||
/** |
|||
* 向指定 URL 发送GET方法的请求 |
|||
* |
|||
* @param url 发送请求的 URL |
|||
* @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 |
|||
* @return 所代表远程资源的响应结果 |
|||
*/ |
|||
public static String sendGet(String url, String param) |
|||
{ |
|||
return sendGet(url, param, Constants.UTF8); |
|||
} |
|||
|
|||
/** |
|||
* 向指定 URL 发送GET方法的请求 |
|||
* |
|||
* @param url 发送请求的 URL |
|||
* @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 |
|||
* @param contentType 编码类型 |
|||
* @return 所代表远程资源的响应结果 |
|||
*/ |
|||
public static String sendGet(String url, String param, String contentType) |
|||
{ |
|||
StringBuilder result = new StringBuilder(); |
|||
BufferedReader in = null; |
|||
try |
|||
{ |
|||
String urlNameString = StringUtils.isNotBlank(param) ? url + "?" + param : url; |
|||
log.info("sendGet - {}", urlNameString); |
|||
URL realUrl = new URL(urlNameString); |
|||
URLConnection connection = realUrl.openConnection(); |
|||
connection.setRequestProperty("accept", "*/*"); |
|||
connection.setRequestProperty("connection", "Keep-Alive"); |
|||
connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); |
|||
connection.connect(); |
|||
in = new BufferedReader(new InputStreamReader(connection.getInputStream(), contentType)); |
|||
String line; |
|||
while ((line = in.readLine()) != null) |
|||
{ |
|||
result.append(line); |
|||
} |
|||
log.info("recv - {}", result); |
|||
} |
|||
catch (ConnectException e) |
|||
{ |
|||
log.error("调用HttpUtils.sendGet ConnectException, url=" + url + ",param=" + param, e); |
|||
} |
|||
catch (SocketTimeoutException e) |
|||
{ |
|||
log.error("调用HttpUtils.sendGet SocketTimeoutException, url=" + url + ",param=" + param, e); |
|||
} |
|||
catch (IOException e) |
|||
{ |
|||
log.error("调用HttpUtils.sendGet IOException, url=" + url + ",param=" + param, e); |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
log.error("调用HttpsUtil.sendGet Exception, url=" + url + ",param=" + param, e); |
|||
} |
|||
finally |
|||
{ |
|||
try |
|||
{ |
|||
if (in != null) |
|||
{ |
|||
in.close(); |
|||
} |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
log.error("调用in.close Exception, url=" + url + ",param=" + param, ex); |
|||
} |
|||
} |
|||
return result.toString(); |
|||
} |
|||
|
|||
/** |
|||
* 向指定 URL 发送POST方法的请求 |
|||
* |
|||
* @param url 发送请求的 URL |
|||
* @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 |
|||
* @return 所代表远程资源的响应结果 |
|||
*/ |
|||
public static String sendPost(String url, String param) |
|||
{ |
|||
PrintWriter out = null; |
|||
BufferedReader in = null; |
|||
StringBuilder result = new StringBuilder(); |
|||
try |
|||
{ |
|||
log.info("sendPost - {}", url); |
|||
URL realUrl = new URL(url); |
|||
URLConnection conn = realUrl.openConnection(); |
|||
conn.setRequestProperty("accept", "*/*"); |
|||
conn.setRequestProperty("connection", "Keep-Alive"); |
|||
conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); |
|||
conn.setRequestProperty("Accept-Charset", "utf-8"); |
|||
conn.setRequestProperty("contentType", "utf-8"); |
|||
conn.setDoOutput(true); |
|||
conn.setDoInput(true); |
|||
out = new PrintWriter(conn.getOutputStream()); |
|||
out.print(param); |
|||
out.flush(); |
|||
in = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8)); |
|||
String line; |
|||
while ((line = in.readLine()) != null) |
|||
{ |
|||
result.append(line); |
|||
} |
|||
log.info("recv - {}", result); |
|||
} |
|||
catch (ConnectException e) |
|||
{ |
|||
log.error("调用HttpUtils.sendPost ConnectException, url=" + url + ",param=" + param, e); |
|||
} |
|||
catch (SocketTimeoutException e) |
|||
{ |
|||
log.error("调用HttpUtils.sendPost SocketTimeoutException, url=" + url + ",param=" + param, e); |
|||
} |
|||
catch (IOException e) |
|||
{ |
|||
log.error("调用HttpUtils.sendPost IOException, url=" + url + ",param=" + param, e); |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
log.error("调用HttpsUtil.sendPost Exception, url=" + url + ",param=" + param, e); |
|||
} |
|||
finally |
|||
{ |
|||
try |
|||
{ |
|||
if (out != null) |
|||
{ |
|||
out.close(); |
|||
} |
|||
if (in != null) |
|||
{ |
|||
in.close(); |
|||
} |
|||
} |
|||
catch (IOException ex) |
|||
{ |
|||
log.error("调用in.close Exception, url=" + url + ",param=" + param, ex); |
|||
} |
|||
} |
|||
return result.toString(); |
|||
} |
|||
|
|||
public static String sendSSLPost(String url, String param) |
|||
{ |
|||
StringBuilder result = new StringBuilder(); |
|||
String urlNameString = url + "?" + param; |
|||
try |
|||
{ |
|||
log.info("sendSSLPost - {}", urlNameString); |
|||
SSLContext sc = SSLContext.getInstance("SSL"); |
|||
sc.init(null, new TrustManager[] { new TrustAnyTrustManager() }, new java.security.SecureRandom()); |
|||
URL console = new URL(urlNameString); |
|||
HttpsURLConnection conn = (HttpsURLConnection) console.openConnection(); |
|||
conn.setRequestProperty("accept", "*/*"); |
|||
conn.setRequestProperty("connection", "Keep-Alive"); |
|||
conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); |
|||
conn.setRequestProperty("Accept-Charset", "utf-8"); |
|||
conn.setRequestProperty("contentType", "utf-8"); |
|||
conn.setDoOutput(true); |
|||
conn.setDoInput(true); |
|||
|
|||
conn.setSSLSocketFactory(sc.getSocketFactory()); |
|||
conn.setHostnameVerifier(new TrustAnyHostnameVerifier()); |
|||
conn.connect(); |
|||
InputStream is = conn.getInputStream(); |
|||
BufferedReader br = new BufferedReader(new InputStreamReader(is)); |
|||
String ret = ""; |
|||
while ((ret = br.readLine()) != null) |
|||
{ |
|||
if (ret != null && !"".equals(ret.trim())) |
|||
{ |
|||
result.append(new String(ret.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8)); |
|||
} |
|||
} |
|||
log.info("recv - {}", result); |
|||
conn.disconnect(); |
|||
br.close(); |
|||
} |
|||
catch (ConnectException e) |
|||
{ |
|||
log.error("调用HttpUtils.sendSSLPost ConnectException, url=" + url + ",param=" + param, e); |
|||
} |
|||
catch (SocketTimeoutException e) |
|||
{ |
|||
log.error("调用HttpUtils.sendSSLPost SocketTimeoutException, url=" + url + ",param=" + param, e); |
|||
} |
|||
catch (IOException e) |
|||
{ |
|||
log.error("调用HttpUtils.sendSSLPost IOException, url=" + url + ",param=" + param, e); |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
log.error("调用HttpsUtil.sendSSLPost Exception, url=" + url + ",param=" + param, e); |
|||
} |
|||
return result.toString(); |
|||
} |
|||
|
|||
private static class TrustAnyTrustManager implements X509TrustManager |
|||
{ |
|||
@Override |
|||
public void checkClientTrusted(X509Certificate[] chain, String authType) |
|||
{ |
|||
} |
|||
|
|||
@Override |
|||
public void checkServerTrusted(X509Certificate[] chain, String authType) |
|||
{ |
|||
} |
|||
|
|||
@Override |
|||
public X509Certificate[] getAcceptedIssuers() |
|||
{ |
|||
return new X509Certificate[] {}; |
|||
} |
|||
} |
|||
|
|||
private static class TrustAnyHostnameVerifier implements HostnameVerifier |
|||
{ |
|||
@Override |
|||
public boolean verify(String hostname, SSLSession session) |
|||
{ |
|||
return true; |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,56 @@ |
|||
package com.lzbi.common.utils.ip; |
|||
|
|||
import org.slf4j.Logger; |
|||
import org.slf4j.LoggerFactory; |
|||
import com.alibaba.fastjson2.JSON; |
|||
import com.alibaba.fastjson2.JSONObject; |
|||
import com.lzbi.common.config.RuoYiConfig; |
|||
import com.lzbi.common.constant.Constants; |
|||
import com.lzbi.common.utils.StringUtils; |
|||
import com.lzbi.common.utils.http.HttpUtils; |
|||
|
|||
/** |
|||
* 获取地址类 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
public class AddressUtils |
|||
{ |
|||
private static final Logger log = LoggerFactory.getLogger(AddressUtils.class); |
|||
|
|||
// IP地址查询
|
|||
public static final String IP_URL = "http://whois.pconline.com.cn/ipJson.jsp"; |
|||
|
|||
// 未知地址
|
|||
public static final String UNKNOWN = "XX XX"; |
|||
|
|||
public static String getRealAddressByIP(String ip) |
|||
{ |
|||
// 内网不查询
|
|||
if (IpUtils.internalIp(ip)) |
|||
{ |
|||
return "内网IP"; |
|||
} |
|||
if (RuoYiConfig.isAddressEnabled()) |
|||
{ |
|||
try |
|||
{ |
|||
String rspStr = HttpUtils.sendGet(IP_URL, "ip=" + ip + "&json=true", Constants.GBK); |
|||
if (StringUtils.isEmpty(rspStr)) |
|||
{ |
|||
log.error("获取地理位置异常 {}", ip); |
|||
return UNKNOWN; |
|||
} |
|||
JSONObject obj = JSON.parseObject(rspStr); |
|||
String region = obj.getString("pro"); |
|||
String city = obj.getString("city"); |
|||
return String.format("%s %s", region, city); |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
log.error("获取地理位置异常 {}", ip); |
|||
} |
|||
} |
|||
return UNKNOWN; |
|||
} |
|||
} |
@ -0,0 +1,382 @@ |
|||
package com.lzbi.common.utils.ip; |
|||
|
|||
import java.net.InetAddress; |
|||
import java.net.UnknownHostException; |
|||
import javax.servlet.http.HttpServletRequest; |
|||
import com.lzbi.common.utils.ServletUtils; |
|||
import com.lzbi.common.utils.StringUtils; |
|||
|
|||
/** |
|||
* 获取IP方法 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
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_WILDCARD = "(((\\*\\.){3}\\*)|(" + REGX_0_255 + "(\\.\\*){3})|(" + REGX_0_255 + "\\." + REGX_0_255 + ")(\\.\\*){2}" + "|((" + REGX_0_255 + "\\.){3}\\*))"; |
|||
// 匹配网段
|
|||
public final static String REGX_IP_SEG = "(" + REGX_IP + "\\-" + REGX_IP + ")"; |
|||
|
|||
/** |
|||
* 获取客户端IP |
|||
* |
|||
* @return IP地址 |
|||
*/ |
|||
public static String getIpAddr() |
|||
{ |
|||
return getIpAddr(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,24 @@ |
|||
package com.lzbi.common.utils.poi; |
|||
|
|||
import org.apache.poi.ss.usermodel.Cell; |
|||
import org.apache.poi.ss.usermodel.Workbook; |
|||
|
|||
/** |
|||
* Excel数据格式处理适配器 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
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
@ -0,0 +1,410 @@ |
|||
package com.lzbi.common.utils.reflect; |
|||
|
|||
import java.lang.reflect.Field; |
|||
import java.lang.reflect.InvocationTargetException; |
|||
import java.lang.reflect.Method; |
|||
import java.lang.reflect.Modifier; |
|||
import java.lang.reflect.ParameterizedType; |
|||
import java.lang.reflect.Type; |
|||
import java.util.Date; |
|||
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 com.lzbi.common.core.text.Convert; |
|||
import com.lzbi.common.utils.DateUtils; |
|||
|
|||
/** |
|||
* 反射工具类. 提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class, 被AOP过的真实类等工具函数. |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
@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,291 @@ |
|||
package com.lzbi.common.utils.sign; |
|||
|
|||
/** |
|||
* Base64工具类 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
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,67 @@ |
|||
package com.lzbi.common.utils.sign; |
|||
|
|||
import java.nio.charset.StandardCharsets; |
|||
import java.security.MessageDigest; |
|||
import org.slf4j.Logger; |
|||
import org.slf4j.LoggerFactory; |
|||
|
|||
/** |
|||
* Md5加密方法 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
public class Md5Utils |
|||
{ |
|||
private static final Logger log = LoggerFactory.getLogger(Md5Utils.class); |
|||
|
|||
private static byte[] md5(String s) |
|||
{ |
|||
MessageDigest algorithm; |
|||
try |
|||
{ |
|||
algorithm = MessageDigest.getInstance("MD5"); |
|||
algorithm.reset(); |
|||
algorithm.update(s.getBytes("UTF-8")); |
|||
byte[] messageDigest = algorithm.digest(); |
|||
return messageDigest; |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
log.error("MD5 Error...", e); |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
private static final String toHex(byte hash[]) |
|||
{ |
|||
if (hash == null) |
|||
{ |
|||
return null; |
|||
} |
|||
StringBuffer buf = new StringBuffer(hash.length * 2); |
|||
int i; |
|||
|
|||
for (i = 0; i < hash.length; i++) |
|||
{ |
|||
if ((hash[i] & 0xff) < 0x10) |
|||
{ |
|||
buf.append("0"); |
|||
} |
|||
buf.append(Long.toString(hash[i] & 0xff, 16)); |
|||
} |
|||
return buf.toString(); |
|||
} |
|||
|
|||
public static String hash(String s) |
|||
{ |
|||
try |
|||
{ |
|||
return new String(toHex(md5(s)).getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8); |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
log.error("not supported charset...{}", e); |
|||
return s; |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,158 @@ |
|||
package com.lzbi.common.utils.spring; |
|||
|
|||
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.context.ApplicationContext; |
|||
import org.springframework.context.ApplicationContextAware; |
|||
import org.springframework.stereotype.Component; |
|||
import com.lzbi.common.utils.StringUtils; |
|||
|
|||
/** |
|||
* spring工具类 方便在非spring管理环境中获取bean |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
@Component |
|||
public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationContextAware |
|||
{ |
|||
/** Spring应用上下文环境 */ |
|||
private static ConfigurableListableBeanFactory beanFactory; |
|||
|
|||
private static ApplicationContext applicationContext; |
|||
|
|||
@Override |
|||
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException |
|||
{ |
|||
SpringUtils.beanFactory = beanFactory; |
|||
} |
|||
|
|||
@Override |
|||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException |
|||
{ |
|||
SpringUtils.applicationContext = applicationContext; |
|||
} |
|||
|
|||
/** |
|||
* 获取对象 |
|||
* |
|||
* @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(); |
|||
} |
|||
|
|||
/** |
|||
* 获取当前的环境配置,无配置返回null |
|||
* |
|||
* @return 当前的环境配置 |
|||
*/ |
|||
public static String[] getActiveProfiles() |
|||
{ |
|||
return applicationContext.getEnvironment().getActiveProfiles(); |
|||
} |
|||
|
|||
/** |
|||
* 获取当前的环境配置,当有多个环境配置时,只获取第一个 |
|||
* |
|||
* @return 当前的环境配置 |
|||
*/ |
|||
public static String getActiveProfile() |
|||
{ |
|||
final String[] activeProfiles = getActiveProfiles(); |
|||
return StringUtils.isNotEmpty(activeProfiles) ? activeProfiles[0] : null; |
|||
} |
|||
|
|||
/** |
|||
* 获取配置文件中的值 |
|||
* |
|||
* @param key 配置文件的key |
|||
* @return 当前的配置文件的值 |
|||
* |
|||
*/ |
|||
public static String getRequiredProperty(String key) |
|||
{ |
|||
return applicationContext.getEnvironment().getRequiredProperty(key); |
|||
} |
|||
} |
@ -0,0 +1,70 @@ |
|||
package com.lzbi.common.utils.sql; |
|||
|
|||
import com.lzbi.common.exception.UtilException; |
|||
import com.lzbi.common.utils.StringUtils; |
|||
|
|||
/** |
|||
* sql操作工具类 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
public class SqlUtil |
|||
{ |
|||
/** |
|||
* 定义常用的 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_\\ \\,\\.]+"; |
|||
|
|||
/** |
|||
* 限制orderBy最大长度 |
|||
*/ |
|||
private static final int ORDER_BY_MAX_LENGTH = 500; |
|||
|
|||
/** |
|||
* 检查字符,防止注入绕过 |
|||
*/ |
|||
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,49 @@ |
|||
package com.lzbi.common.utils.uuid; |
|||
|
|||
/** |
|||
* ID生成器工具类 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
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,86 @@ |
|||
package com.lzbi.common.utils.uuid; |
|||
|
|||
import java.util.concurrent.atomic.AtomicInteger; |
|||
import com.lzbi.common.utils.DateUtils; |
|||
import com.lzbi.common.utils.StringUtils; |
|||
|
|||
/** |
|||
* @author ruoyi 序列生成类 |
|||
*/ |
|||
public class Seq |
|||
{ |
|||
// 通用序列类型
|
|||
public static final String commSeqType = "COMMON"; |
|||
|
|||
// 上传序列类型
|
|||
public static final String uploadSeqType = "UPLOAD"; |
|||
|
|||
// 通用接口序列数
|
|||
private static AtomicInteger commSeq = new AtomicInteger(1); |
|||
|
|||
// 上传接口序列数
|
|||
private static AtomicInteger uploadSeq = new AtomicInteger(1); |
|||
|
|||
// 机器标识
|
|||
private static final String machineCode = "A"; |
|||
|
|||
/** |
|||
* 获取通用序列号 |
|||
* |
|||
* @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,484 @@ |
|||
package com.lzbi.common.utils.uuid; |
|||
|
|||
import java.security.MessageDigest; |
|||
import java.security.NoSuchAlgorithmException; |
|||
import java.security.SecureRandom; |
|||
import java.util.Random; |
|||
import java.util.concurrent.ThreadLocalRandom; |
|||
import com.lzbi.common.exception.UtilException; |
|||
|
|||
/** |
|||
* 提供通用唯一识别码(universally unique identifier)(UUID)实现 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
public final class UUID implements java.io.Serializable, Comparable<UUID> |
|||
{ |
|||
private static final long serialVersionUID = -1185015143654744140L; |
|||
|
|||
/** |
|||
* SecureRandom 的单例 |
|||
* |
|||
*/ |
|||
private static class Holder |
|||
{ |
|||
static final SecureRandom numberGenerator = getSecureRandom(); |
|||
} |
|||
|
|||
/** 此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); |
|||
} |
|||
|
|||
/** |
|||
* 返回此 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 (!isSimple) |
|||
{ |
|||
builder.append('-'); |
|||
} |
|||
// time_mid
|
|||
builder.append(digits(mostSigBits >> 16, 4)); |
|||
if (!isSimple) |
|||
{ |
|||
builder.append('-'); |
|||
} |
|||
// time_high_and_version
|
|||
builder.append(digits(mostSigBits, 4)); |
|||
if (!isSimple) |
|||
{ |
|||
builder.append('-'); |
|||
} |
|||
// variant_and_sequence
|
|||
builder.append(digits(leastSigBits >> 48, 4)); |
|||
if (!isSimple) |
|||
{ |
|||
builder.append('-'); |
|||
} |
|||
// node
|
|||
builder.append(digits(leastSigBits, 12)); |
|||
|
|||
return builder.toString(); |
|||
} |
|||
|
|||
/** |
|||
* 返回此 UUID 的哈希码。 |
|||
* |
|||
* @return UUID 的哈希码值。 |
|||
*/ |
|||
@Override |
|||
public int hashCode() |
|||
{ |
|||
long hilo = mostSigBits ^ leastSigBits; |
|||
return ((int) (hilo >> 32)) ^ (int) hilo; |
|||
} |
|||
|
|||
/** |
|||
* 将此对象与指定对象比较。 |
|||
* <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); |
|||
} |
|||
|
|||
// Comparison Operations
|
|||
|
|||
/** |
|||
* 将此 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)))); |
|||
} |
|||
|
|||
// -------------------------------------------------------------------------------------------------------------------
|
|||
// Private method start
|
|||
/** |
|||
* 返回指定数字对应的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); |
|||
} |
|||
|
|||
/** |
|||
* 检查是否为time-based版本UUID |
|||
*/ |
|||
private void checkTimeBase() |
|||
{ |
|||
if (version() != 1) |
|||
{ |
|||
throw new UnsupportedOperationException("Not a time-based UUID"); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 获取{@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(); |
|||
} |
|||
} |
@ -0,0 +1,27 @@ |
|||
package com.lzbi.common.xss; |
|||
|
|||
import javax.validation.Constraint; |
|||
import javax.validation.Payload; |
|||
import java.lang.annotation.ElementType; |
|||
import java.lang.annotation.Retention; |
|||
import java.lang.annotation.RetentionPolicy; |
|||
import java.lang.annotation.Target; |
|||
|
|||
/** |
|||
* 自定义xss校验注解 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
@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,34 @@ |
|||
package com.lzbi.common.xss; |
|||
|
|||
import com.lzbi.common.utils.StringUtils; |
|||
import javax.validation.ConstraintValidator; |
|||
import javax.validation.ConstraintValidatorContext; |
|||
import java.util.regex.Matcher; |
|||
import java.util.regex.Pattern; |
|||
|
|||
/** |
|||
* 自定义xss校验注解实现 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
public class XssValidator implements ConstraintValidator<Xss, String> |
|||
{ |
|||
private static final String HTML_PATTERN = "<(\\S*?)[^>]*>.*?|<.*? />"; |
|||
|
|||
@Override |
|||
public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) |
|||
{ |
|||
if (StringUtils.isBlank(value)) |
|||
{ |
|||
return true; |
|||
} |
|||
return !containsHtml(value); |
|||
} |
|||
|
|||
public static boolean containsHtml(String value) |
|||
{ |
|||
Pattern pattern = Pattern.compile(HTML_PATTERN); |
|||
Matcher matcher = pattern.matcher(value); |
|||
return matcher.matches(); |
|||
} |
|||
} |
@ -0,0 +1,64 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" |
|||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
|||
<parent> |
|||
<artifactId>lzbi</artifactId> |
|||
<groupId>com.lzbi</groupId> |
|||
<version>3.8.6</version> |
|||
</parent> |
|||
<modelVersion>4.0.0</modelVersion> |
|||
|
|||
<artifactId>lzbi-framework</artifactId> |
|||
|
|||
<description> |
|||
framework框架核心 |
|||
</description> |
|||
|
|||
<dependencies> |
|||
|
|||
<!-- SpringBoot Web容器 --> |
|||
<dependency> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-starter-web</artifactId> |
|||
</dependency> |
|||
|
|||
<!-- SpringBoot 拦截器 --> |
|||
<dependency> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-starter-aop</artifactId> |
|||
</dependency> |
|||
|
|||
<!-- 阿里数据库连接池 --> |
|||
<dependency> |
|||
<groupId>com.alibaba</groupId> |
|||
<artifactId>druid-spring-boot-starter</artifactId> |
|||
</dependency> |
|||
|
|||
<!-- 验证码 --> |
|||
<dependency> |
|||
<groupId>pro.fessional</groupId> |
|||
<artifactId>kaptcha</artifactId> |
|||
<exclusions> |
|||
<exclusion> |
|||
<artifactId>servlet-api</artifactId> |
|||
<groupId>javax.servlet</groupId> |
|||
</exclusion> |
|||
</exclusions> |
|||
</dependency> |
|||
|
|||
<!-- 获取系统信息 --> |
|||
<dependency> |
|||
<groupId>com.github.oshi</groupId> |
|||
<artifactId>oshi-core</artifactId> |
|||
</dependency> |
|||
|
|||
<!-- 系统模块--> |
|||
<dependency> |
|||
<groupId>com.lzbi</groupId> |
|||
<artifactId>lzbi-system</artifactId> |
|||
</dependency> |
|||
|
|||
</dependencies> |
|||
|
|||
</project> |
@ -0,0 +1,174 @@ |
|||
package com.lzbi.framework.aspectj; |
|||
|
|||
import java.util.ArrayList; |
|||
import java.util.List; |
|||
import org.aspectj.lang.JoinPoint; |
|||
import org.aspectj.lang.annotation.Aspect; |
|||
import org.aspectj.lang.annotation.Before; |
|||
import org.springframework.stereotype.Component; |
|||
import com.lzbi.common.annotation.DataScope; |
|||
import com.lzbi.common.core.domain.BaseEntity; |
|||
import com.lzbi.common.core.domain.entity.SysRole; |
|||
import com.lzbi.common.core.domain.entity.SysUser; |
|||
import com.lzbi.common.core.domain.model.LoginUser; |
|||
import com.lzbi.common.core.text.Convert; |
|||
import com.lzbi.common.utils.SecurityUtils; |
|||
import com.lzbi.common.utils.StringUtils; |
|||
import com.lzbi.framework.security.context.PermissionContextHolder; |
|||
|
|||
/** |
|||
* 数据过滤处理 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
@Aspect |
|||
@Component |
|||
public class DataScopeAspect |
|||
{ |
|||
/** |
|||
* 全部数据权限 |
|||
*/ |
|||
public static final String DATA_SCOPE_ALL = "1"; |
|||
|
|||
/** |
|||
* 自定数据权限 |
|||
*/ |
|||
public static final String DATA_SCOPE_CUSTOM = "2"; |
|||
|
|||
/** |
|||
* 部门数据权限 |
|||
*/ |
|||
public static final String DATA_SCOPE_DEPT = "3"; |
|||
|
|||
/** |
|||
* 部门及以下数据权限 |
|||
*/ |
|||
public static final String DATA_SCOPE_DEPT_AND_CHILD = "4"; |
|||
|
|||
/** |
|||
* 仅本人数据权限 |
|||
*/ |
|||
public static final String DATA_SCOPE_SELF = "5"; |
|||
|
|||
/** |
|||
* 数据权限过滤关键字 |
|||
*/ |
|||
public static final String DATA_SCOPE = "dataScope"; |
|||
|
|||
@Before("@annotation(controllerDataScope)") |
|||
public void doBefore(JoinPoint point, DataScope controllerDataScope) throws Throwable |
|||
{ |
|||
clearDataScope(point); |
|||
handleDataScope(point, controllerDataScope); |
|||
} |
|||
|
|||
protected void handleDataScope(final JoinPoint joinPoint, DataScope controllerDataScope) |
|||
{ |
|||
// 获取当前的用户
|
|||
LoginUser loginUser = SecurityUtils.getLoginUser(); |
|||
if (StringUtils.isNotNull(loginUser)) |
|||
{ |
|||
SysUser currentUser = loginUser.getUser(); |
|||
// 如果是超级管理员,则不过滤数据
|
|||
if (StringUtils.isNotNull(currentUser) && !currentUser.isAdmin()) |
|||
{ |
|||
String permission = StringUtils.defaultIfEmpty(controllerDataScope.permission(), PermissionContextHolder.getContext()); |
|||
dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptAlias(), |
|||
controllerDataScope.userAlias(), permission); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 数据范围过滤 |
|||
* |
|||
* @param joinPoint 切点 |
|||
* @param user 用户 |
|||
* @param deptAlias 部门别名 |
|||
* @param userAlias 用户别名 |
|||
* @param permission 权限字符 |
|||
*/ |
|||
public static void dataScopeFilter(JoinPoint joinPoint, SysUser user, String deptAlias, String userAlias, String permission) |
|||
{ |
|||
StringBuilder sqlString = new StringBuilder(); |
|||
List<String> conditions = new ArrayList<String>(); |
|||
|
|||
for (SysRole role : user.getRoles()) |
|||
{ |
|||
String dataScope = role.getDataScope(); |
|||
if (!DATA_SCOPE_CUSTOM.equals(dataScope) && conditions.contains(dataScope)) |
|||
{ |
|||
continue; |
|||
} |
|||
if (StringUtils.isNotEmpty(permission) && StringUtils.isNotEmpty(role.getPermissions()) |
|||
&& !StringUtils.containsAny(role.getPermissions(), Convert.toStrArray(permission))) |
|||
{ |
|||
continue; |
|||
} |
|||
if (DATA_SCOPE_ALL.equals(dataScope)) |
|||
{ |
|||
sqlString = new StringBuilder(); |
|||
conditions.add(dataScope); |
|||
break; |
|||
} |
|||
else if (DATA_SCOPE_CUSTOM.equals(dataScope)) |
|||
{ |
|||
sqlString.append(StringUtils.format( |
|||
" OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ", deptAlias, |
|||
role.getRoleId())); |
|||
} |
|||
else if (DATA_SCOPE_DEPT.equals(dataScope)) |
|||
{ |
|||
sqlString.append(StringUtils.format(" OR {}.dept_id = {} ", deptAlias, user.getDeptId())); |
|||
} |
|||
else if (DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope)) |
|||
{ |
|||
sqlString.append(StringUtils.format( |
|||
" OR {}.dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} or find_in_set( {} , ancestors ) )", |
|||
deptAlias, user.getDeptId(), user.getDeptId())); |
|||
} |
|||
else if (DATA_SCOPE_SELF.equals(dataScope)) |
|||
{ |
|||
if (StringUtils.isNotBlank(userAlias)) |
|||
{ |
|||
sqlString.append(StringUtils.format(" OR {}.user_id = {} ", userAlias, user.getUserId())); |
|||
} |
|||
else |
|||
{ |
|||
// 数据权限为仅本人且没有userAlias别名不查询任何数据
|
|||
sqlString.append(StringUtils.format(" OR {}.dept_id = 0 ", deptAlias)); |
|||
} |
|||
} |
|||
conditions.add(dataScope); |
|||
} |
|||
|
|||
// 多角色情况下,所有角色都不包含传递过来的权限字符,这个时候sqlString也会为空,所以要限制一下,不查询任何数据
|
|||
if (StringUtils.isEmpty(conditions)) |
|||
{ |
|||
sqlString.append(StringUtils.format(" OR {}.dept_id = 0 ", deptAlias)); |
|||
} |
|||
|
|||
if (StringUtils.isNotBlank(sqlString.toString())) |
|||
{ |
|||
Object params = joinPoint.getArgs()[0]; |
|||
if (StringUtils.isNotNull(params) && params instanceof BaseEntity) |
|||
{ |
|||
BaseEntity baseEntity = (BaseEntity) params; |
|||
baseEntity.getParams().put(DATA_SCOPE, " AND (" + sqlString.substring(4) + ")"); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 拼接权限sql前先清空params.dataScope参数防止注入 |
|||
*/ |
|||
private void clearDataScope(final JoinPoint joinPoint) |
|||
{ |
|||
Object params = joinPoint.getArgs()[0]; |
|||
if (StringUtils.isNotNull(params) && params instanceof BaseEntity) |
|||
{ |
|||
BaseEntity baseEntity = (BaseEntity) params; |
|||
baseEntity.getParams().put(DATA_SCOPE, ""); |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,72 @@ |
|||
package com.lzbi.framework.aspectj; |
|||
|
|||
import java.util.Objects; |
|||
import org.aspectj.lang.ProceedingJoinPoint; |
|||
import org.aspectj.lang.annotation.Around; |
|||
import org.aspectj.lang.annotation.Aspect; |
|||
import org.aspectj.lang.annotation.Pointcut; |
|||
import org.aspectj.lang.reflect.MethodSignature; |
|||
import org.slf4j.Logger; |
|||
import org.slf4j.LoggerFactory; |
|||
import org.springframework.core.annotation.AnnotationUtils; |
|||
import org.springframework.core.annotation.Order; |
|||
import org.springframework.stereotype.Component; |
|||
import com.lzbi.common.annotation.DataSource; |
|||
import com.lzbi.common.utils.StringUtils; |
|||
import com.lzbi.framework.datasource.DynamicDataSourceContextHolder; |
|||
|
|||
/** |
|||
* 多数据源处理 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
@Aspect |
|||
@Order(1) |
|||
@Component |
|||
public class DataSourceAspect |
|||
{ |
|||
protected Logger logger = LoggerFactory.getLogger(getClass()); |
|||
|
|||
@Pointcut("@annotation(com.lzbi.common.annotation.DataSource)" |
|||
+ "|| @within(com.lzbi.common.annotation.DataSource)") |
|||
public void dsPointCut() |
|||
{ |
|||
|
|||
} |
|||
|
|||
@Around("dsPointCut()") |
|||
public Object around(ProceedingJoinPoint point) throws Throwable |
|||
{ |
|||
DataSource dataSource = getDataSource(point); |
|||
|
|||
if (StringUtils.isNotNull(dataSource)) |
|||
{ |
|||
DynamicDataSourceContextHolder.setDataSourceType(dataSource.value().name()); |
|||
} |
|||
|
|||
try |
|||
{ |
|||
return point.proceed(); |
|||
} |
|||
finally |
|||
{ |
|||
// 销毁数据源 在执行方法之后
|
|||
DynamicDataSourceContextHolder.clearDataSourceType(); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 获取需要切换的数据源 |
|||
*/ |
|||
public DataSource getDataSource(ProceedingJoinPoint point) |
|||
{ |
|||
MethodSignature signature = (MethodSignature) point.getSignature(); |
|||
DataSource dataSource = AnnotationUtils.findAnnotation(signature.getMethod(), DataSource.class); |
|||
if (Objects.nonNull(dataSource)) |
|||
{ |
|||
return dataSource; |
|||
} |
|||
|
|||
return AnnotationUtils.findAnnotation(signature.getDeclaringType(), DataSource.class); |
|||
} |
|||
} |
@ -0,0 +1,249 @@ |
|||
package com.lzbi.framework.aspectj; |
|||
|
|||
import java.util.Collection; |
|||
import java.util.Map; |
|||
import javax.servlet.http.HttpServletRequest; |
|||
import javax.servlet.http.HttpServletResponse; |
|||
import org.apache.commons.lang3.ArrayUtils; |
|||
import org.aspectj.lang.JoinPoint; |
|||
import org.aspectj.lang.annotation.AfterReturning; |
|||
import org.aspectj.lang.annotation.AfterThrowing; |
|||
import org.aspectj.lang.annotation.Aspect; |
|||
import org.aspectj.lang.annotation.Before; |
|||
import org.slf4j.Logger; |
|||
import org.slf4j.LoggerFactory; |
|||
import org.springframework.core.NamedThreadLocal; |
|||
import org.springframework.stereotype.Component; |
|||
import org.springframework.validation.BindingResult; |
|||
import org.springframework.web.multipart.MultipartFile; |
|||
import com.alibaba.fastjson2.JSON; |
|||
import com.lzbi.common.annotation.Log; |
|||
import com.lzbi.common.core.domain.model.LoginUser; |
|||
import com.lzbi.common.enums.BusinessStatus; |
|||
import com.lzbi.common.enums.HttpMethod; |
|||
import com.lzbi.common.filter.PropertyPreExcludeFilter; |
|||
import com.lzbi.common.utils.SecurityUtils; |
|||
import com.lzbi.common.utils.ServletUtils; |
|||
import com.lzbi.common.utils.StringUtils; |
|||
import com.lzbi.common.utils.ip.IpUtils; |
|||
import com.lzbi.framework.manager.AsyncManager; |
|||
import com.lzbi.framework.manager.factory.AsyncFactory; |
|||
import com.lzbi.system.domain.SysOperLog; |
|||
|
|||
/** |
|||
* 操作日志记录处理 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
@Aspect |
|||
@Component |
|||
public class LogAspect |
|||
{ |
|||
private static final Logger log = LoggerFactory.getLogger(LogAspect.class); |
|||
|
|||
/** 排除敏感属性字段 */ |
|||
public static final String[] EXCLUDE_PROPERTIES = { "password", "oldPassword", "newPassword", "confirmPassword" }; |
|||
|
|||
/** 计算操作消耗时间 */ |
|||
private static final ThreadLocal<Long> TIME_THREADLOCAL = new NamedThreadLocal<Long>("Cost Time"); |
|||
|
|||
/** |
|||
* 处理请求前执行 |
|||
*/ |
|||
@Before(value = "@annotation(controllerLog)") |
|||
public void boBefore(JoinPoint joinPoint, Log controllerLog) |
|||
{ |
|||
TIME_THREADLOCAL.set(System.currentTimeMillis()); |
|||
} |
|||
|
|||
/** |
|||
* 处理完请求后执行 |
|||
* |
|||
* @param joinPoint 切点 |
|||
*/ |
|||
@AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult") |
|||
public void doAfterReturning(JoinPoint joinPoint, Log controllerLog, Object jsonResult) |
|||
{ |
|||
handleLog(joinPoint, controllerLog, null, jsonResult); |
|||
} |
|||
|
|||
/** |
|||
* 拦截异常操作 |
|||
* |
|||
* @param joinPoint 切点 |
|||
* @param e 异常 |
|||
*/ |
|||
@AfterThrowing(value = "@annotation(controllerLog)", throwing = "e") |
|||
public void doAfterThrowing(JoinPoint joinPoint, Log controllerLog, Exception e) |
|||
{ |
|||
handleLog(joinPoint, controllerLog, e, null); |
|||
} |
|||
|
|||
protected void handleLog(final JoinPoint joinPoint, Log controllerLog, final Exception e, Object jsonResult) |
|||
{ |
|||
try |
|||
{ |
|||
// 获取当前的用户
|
|||
LoginUser loginUser = SecurityUtils.getLoginUser(); |
|||
|
|||
// *========数据库日志=========*//
|
|||
SysOperLog operLog = new SysOperLog(); |
|||
operLog.setStatus(BusinessStatus.SUCCESS.ordinal()); |
|||
// 请求的地址
|
|||
String ip = IpUtils.getIpAddr(); |
|||
operLog.setOperIp(ip); |
|||
operLog.setOperUrl(StringUtils.substring(ServletUtils.getRequest().getRequestURI(), 0, 255)); |
|||
if (loginUser != null) |
|||
{ |
|||
operLog.setOperName(loginUser.getUsername()); |
|||
} |
|||
|
|||
if (e != null) |
|||
{ |
|||
operLog.setStatus(BusinessStatus.FAIL.ordinal()); |
|||
operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000)); |
|||
} |
|||
// 设置方法名称
|
|||
String className = joinPoint.getTarget().getClass().getName(); |
|||
String methodName = joinPoint.getSignature().getName(); |
|||
operLog.setMethod(className + "." + methodName + "()"); |
|||
// 设置请求方式
|
|||
operLog.setRequestMethod(ServletUtils.getRequest().getMethod()); |
|||
// 处理设置注解上的参数
|
|||
getControllerMethodDescription(joinPoint, controllerLog, operLog, jsonResult); |
|||
// 设置消耗时间
|
|||
operLog.setCostTime(System.currentTimeMillis() - TIME_THREADLOCAL.get()); |
|||
// 保存数据库
|
|||
AsyncManager.me().execute(AsyncFactory.recordOper(operLog)); |
|||
} |
|||
catch (Exception exp) |
|||
{ |
|||
// 记录本地异常日志
|
|||
log.error("异常信息:{}", exp.getMessage()); |
|||
exp.printStackTrace(); |
|||
} |
|||
finally |
|||
{ |
|||
TIME_THREADLOCAL.remove(); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 获取注解中对方法的描述信息 用于Controller层注解 |
|||
* |
|||
* @param log 日志 |
|||
* @param operLog 操作日志 |
|||
* @throws Exception |
|||
*/ |
|||
public void getControllerMethodDescription(JoinPoint joinPoint, Log log, SysOperLog operLog, Object jsonResult) throws Exception |
|||
{ |
|||
// 设置action动作
|
|||
operLog.setBusinessType(log.businessType().ordinal()); |
|||
// 设置标题
|
|||
operLog.setTitle(log.title()); |
|||
// 设置操作人类别
|
|||
operLog.setOperatorType(log.operatorType().ordinal()); |
|||
// 是否需要保存request,参数和值
|
|||
if (log.isSaveRequestData()) |
|||
{ |
|||
// 获取参数的信息,传入到数据库中。
|
|||
setRequestValue(joinPoint, operLog, log.excludeParamNames()); |
|||
} |
|||
// 是否需要保存response,参数和值
|
|||
if (log.isSaveResponseData() && StringUtils.isNotNull(jsonResult)) |
|||
{ |
|||
operLog.setJsonResult(StringUtils.substring(JSON.toJSONString(jsonResult), 0, 2000)); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 获取请求的参数,放到log中 |
|||
* |
|||
* @param operLog 操作日志 |
|||
* @throws Exception 异常 |
|||
*/ |
|||
private void setRequestValue(JoinPoint joinPoint, SysOperLog operLog, String[] excludeParamNames) throws Exception |
|||
{ |
|||
Map<?, ?> paramsMap = ServletUtils.getParamMap(ServletUtils.getRequest()); |
|||
String requestMethod = operLog.getRequestMethod(); |
|||
if (StringUtils.isEmpty(paramsMap) |
|||
&& (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod))) |
|||
{ |
|||
String params = argsArrayToString(joinPoint.getArgs(), excludeParamNames); |
|||
operLog.setOperParam(StringUtils.substring(params, 0, 2000)); |
|||
} |
|||
else |
|||
{ |
|||
operLog.setOperParam(StringUtils.substring(JSON.toJSONString(paramsMap, excludePropertyPreFilter(excludeParamNames)), 0, 2000)); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 参数拼装 |
|||
*/ |
|||
private String argsArrayToString(Object[] paramsArray, String[] excludeParamNames) |
|||
{ |
|||
String params = ""; |
|||
if (paramsArray != null && paramsArray.length > 0) |
|||
{ |
|||
for (Object o : paramsArray) |
|||
{ |
|||
if (StringUtils.isNotNull(o) && !isFilterObject(o)) |
|||
{ |
|||
try |
|||
{ |
|||
String jsonObj = JSON.toJSONString(o, excludePropertyPreFilter(excludeParamNames)); |
|||
params += jsonObj.toString() + " "; |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
} |
|||
return params.trim(); |
|||
} |
|||
|
|||
/** |
|||
* 忽略敏感属性 |
|||
*/ |
|||
public PropertyPreExcludeFilter excludePropertyPreFilter(String[] excludeParamNames) |
|||
{ |
|||
return new PropertyPreExcludeFilter().addExcludes(ArrayUtils.addAll(EXCLUDE_PROPERTIES, excludeParamNames)); |
|||
} |
|||
|
|||
/** |
|||
* 判断是否需要过滤的对象。 |
|||
* |
|||
* @param o 对象信息。 |
|||
* @return 如果是需要过滤的对象,则返回true;否则返回false。 |
|||
*/ |
|||
@SuppressWarnings("rawtypes") |
|||
public boolean isFilterObject(final Object o) |
|||
{ |
|||
Class<?> clazz = o.getClass(); |
|||
if (clazz.isArray()) |
|||
{ |
|||
return clazz.getComponentType().isAssignableFrom(MultipartFile.class); |
|||
} |
|||
else if (Collection.class.isAssignableFrom(clazz)) |
|||
{ |
|||
Collection collection = (Collection) o; |
|||
for (Object value : collection) |
|||
{ |
|||
return value instanceof MultipartFile; |
|||
} |
|||
} |
|||
else if (Map.class.isAssignableFrom(clazz)) |
|||
{ |
|||
Map map = (Map) o; |
|||
for (Object value : map.entrySet()) |
|||
{ |
|||
Map.Entry entry = (Map.Entry) value; |
|||
return entry.getValue() instanceof MultipartFile; |
|||
} |
|||
} |
|||
return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse |
|||
|| o instanceof BindingResult; |
|||
} |
|||
} |
@ -0,0 +1,89 @@ |
|||
package com.lzbi.framework.aspectj; |
|||
|
|||
import java.lang.reflect.Method; |
|||
import java.util.Collections; |
|||
import java.util.List; |
|||
import org.aspectj.lang.JoinPoint; |
|||
import org.aspectj.lang.annotation.Aspect; |
|||
import org.aspectj.lang.annotation.Before; |
|||
import org.aspectj.lang.reflect.MethodSignature; |
|||
import org.slf4j.Logger; |
|||
import org.slf4j.LoggerFactory; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.data.redis.core.RedisTemplate; |
|||
import org.springframework.data.redis.core.script.RedisScript; |
|||
import org.springframework.stereotype.Component; |
|||
import com.lzbi.common.annotation.RateLimiter; |
|||
import com.lzbi.common.enums.LimitType; |
|||
import com.lzbi.common.exception.ServiceException; |
|||
import com.lzbi.common.utils.StringUtils; |
|||
import com.lzbi.common.utils.ip.IpUtils; |
|||
|
|||
/** |
|||
* 限流处理 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
@Aspect |
|||
@Component |
|||
public class RateLimiterAspect |
|||
{ |
|||
private static final Logger log = LoggerFactory.getLogger(RateLimiterAspect.class); |
|||
|
|||
private RedisTemplate<Object, Object> redisTemplate; |
|||
|
|||
private RedisScript<Long> limitScript; |
|||
|
|||
@Autowired |
|||
public void setRedisTemplate1(RedisTemplate<Object, Object> redisTemplate) |
|||
{ |
|||
this.redisTemplate = redisTemplate; |
|||
} |
|||
|
|||
@Autowired |
|||
public void setLimitScript(RedisScript<Long> limitScript) |
|||
{ |
|||
this.limitScript = limitScript; |
|||
} |
|||
|
|||
@Before("@annotation(rateLimiter)") |
|||
public void doBefore(JoinPoint point, RateLimiter rateLimiter) throws Throwable |
|||
{ |
|||
int time = rateLimiter.time(); |
|||
int count = rateLimiter.count(); |
|||
|
|||
String combineKey = getCombineKey(rateLimiter, point); |
|||
List<Object> keys = Collections.singletonList(combineKey); |
|||
try |
|||
{ |
|||
Long number = redisTemplate.execute(limitScript, keys, count, time); |
|||
if (StringUtils.isNull(number) || number.intValue() > count) |
|||
{ |
|||
throw new ServiceException("访问过于频繁,请稍候再试"); |
|||
} |
|||
log.info("限制请求'{}',当前请求'{}',缓存key'{}'", count, number.intValue(), combineKey); |
|||
} |
|||
catch (ServiceException e) |
|||
{ |
|||
throw e; |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
throw new RuntimeException("服务器限流异常,请稍候再试"); |
|||
} |
|||
} |
|||
|
|||
public String getCombineKey(RateLimiter rateLimiter, JoinPoint point) |
|||
{ |
|||
StringBuffer stringBuffer = new StringBuffer(rateLimiter.key()); |
|||
if (rateLimiter.limitType() == LimitType.IP) |
|||
{ |
|||
stringBuffer.append(IpUtils.getIpAddr()).append("-"); |
|||
} |
|||
MethodSignature signature = (MethodSignature) point.getSignature(); |
|||
Method method = signature.getMethod(); |
|||
Class<?> targetClass = method.getDeclaringClass(); |
|||
stringBuffer.append(targetClass.getName()).append("-").append(method.getName()); |
|||
return stringBuffer.toString(); |
|||
} |
|||
} |
@ -0,0 +1,30 @@ |
|||
package com.lzbi.framework.config; |
|||
|
|||
import java.util.TimeZone; |
|||
import org.mybatis.spring.annotation.MapperScan; |
|||
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; |
|||
import org.springframework.context.annotation.Bean; |
|||
import org.springframework.context.annotation.Configuration; |
|||
import org.springframework.context.annotation.EnableAspectJAutoProxy; |
|||
|
|||
/** |
|||
* 程序注解配置 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
@Configuration |
|||
// 表示通过aop框架暴露该代理对象,AopContext能够访问
|
|||
@EnableAspectJAutoProxy(exposeProxy = true) |
|||
// 指定要扫描的Mapper类的包的路径
|
|||
@MapperScan("com.lzbi.**.mapper") |
|||
public class ApplicationConfig |
|||
{ |
|||
/** |
|||
* 时区配置 |
|||
*/ |
|||
@Bean |
|||
public Jackson2ObjectMapperBuilderCustomizer jacksonObjectMapperCustomization() |
|||
{ |
|||
return jacksonObjectMapperBuilder -> jacksonObjectMapperBuilder.timeZone(TimeZone.getDefault()); |
|||
} |
|||
} |
@ -0,0 +1,83 @@ |
|||
package com.lzbi.framework.config; |
|||
|
|||
import java.util.Properties; |
|||
import org.springframework.context.annotation.Bean; |
|||
import org.springframework.context.annotation.Configuration; |
|||
import com.google.code.kaptcha.impl.DefaultKaptcha; |
|||
import com.google.code.kaptcha.util.Config; |
|||
import static com.google.code.kaptcha.Constants.*; |
|||
|
|||
/** |
|||
* 验证码配置 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
@Configuration |
|||
public class CaptchaConfig |
|||
{ |
|||
@Bean(name = "captchaProducer") |
|||
public DefaultKaptcha getKaptchaBean() |
|||
{ |
|||
DefaultKaptcha defaultKaptcha = new DefaultKaptcha(); |
|||
Properties properties = new Properties(); |
|||
// 是否有边框 默认为true 我们可以自己设置yes,no
|
|||
properties.setProperty(KAPTCHA_BORDER, "yes"); |
|||
// 验证码文本字符颜色 默认为Color.BLACK
|
|||
properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "black"); |
|||
// 验证码图片宽度 默认为200
|
|||
properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160"); |
|||
// 验证码图片高度 默认为50
|
|||
properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60"); |
|||
// 验证码文本字符大小 默认为40
|
|||
properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "38"); |
|||
// KAPTCHA_SESSION_KEY
|
|||
properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCode"); |
|||
// 验证码文本字符长度 默认为5
|
|||
properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "4"); |
|||
// 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize)
|
|||
properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier"); |
|||
// 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpy
|
|||
properties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy"); |
|||
Config config = new Config(properties); |
|||
defaultKaptcha.setConfig(config); |
|||
return defaultKaptcha; |
|||
} |
|||
|
|||
@Bean(name = "captchaProducerMath") |
|||
public DefaultKaptcha getKaptchaBeanMath() |
|||
{ |
|||
DefaultKaptcha defaultKaptcha = new DefaultKaptcha(); |
|||
Properties properties = new Properties(); |
|||
// 是否有边框 默认为true 我们可以自己设置yes,no
|
|||
properties.setProperty(KAPTCHA_BORDER, "yes"); |
|||
// 边框颜色 默认为Color.BLACK
|
|||
properties.setProperty(KAPTCHA_BORDER_COLOR, "105,179,90"); |
|||
// 验证码文本字符颜色 默认为Color.BLACK
|
|||
properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "blue"); |
|||
// 验证码图片宽度 默认为200
|
|||
properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160"); |
|||
// 验证码图片高度 默认为50
|
|||
properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60"); |
|||
// 验证码文本字符大小 默认为40
|
|||
properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "35"); |
|||
// KAPTCHA_SESSION_KEY
|
|||
properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCodeMath"); |
|||
// 验证码文本生成器
|
|||
properties.setProperty(KAPTCHA_TEXTPRODUCER_IMPL, "com.lzbi.framework.config.KaptchaTextCreator"); |
|||
// 验证码文本字符间距 默认为2
|
|||
properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_SPACE, "3"); |
|||
// 验证码文本字符长度 默认为5
|
|||
properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "6"); |
|||
// 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize)
|
|||
properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier"); |
|||
// 验证码噪点颜色 默认为Color.BLACK
|
|||
properties.setProperty(KAPTCHA_NOISE_COLOR, "white"); |
|||
// 干扰实现类
|
|||
properties.setProperty(KAPTCHA_NOISE_IMPL, "com.google.code.kaptcha.impl.NoNoise"); |
|||
// 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpy
|
|||
properties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy"); |
|||
Config config = new Config(properties); |
|||
defaultKaptcha.setConfig(config); |
|||
return defaultKaptcha; |
|||
} |
|||
} |
@ -0,0 +1,126 @@ |
|||
package com.lzbi.framework.config; |
|||
|
|||
import java.io.IOException; |
|||
import java.util.HashMap; |
|||
import java.util.Map; |
|||
import javax.servlet.Filter; |
|||
import javax.servlet.FilterChain; |
|||
import javax.servlet.ServletException; |
|||
import javax.servlet.ServletRequest; |
|||
import javax.servlet.ServletResponse; |
|||
import javax.sql.DataSource; |
|||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
|||
import org.springframework.boot.context.properties.ConfigurationProperties; |
|||
import org.springframework.boot.web.servlet.FilterRegistrationBean; |
|||
import org.springframework.context.annotation.Bean; |
|||
import org.springframework.context.annotation.Configuration; |
|||
import org.springframework.context.annotation.Primary; |
|||
import com.alibaba.druid.pool.DruidDataSource; |
|||
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder; |
|||
import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties; |
|||
import com.alibaba.druid.util.Utils; |
|||
import com.lzbi.common.enums.DataSourceType; |
|||
import com.lzbi.common.utils.spring.SpringUtils; |
|||
import com.lzbi.framework.config.properties.DruidProperties; |
|||
import com.lzbi.framework.datasource.DynamicDataSource; |
|||
|
|||
/** |
|||
* druid 配置多数据源 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
@Configuration |
|||
public class DruidConfig |
|||
{ |
|||
@Bean |
|||
@ConfigurationProperties("spring.datasource.druid.master") |
|||
public DataSource masterDataSource(DruidProperties druidProperties) |
|||
{ |
|||
DruidDataSource dataSource = DruidDataSourceBuilder.create().build(); |
|||
return druidProperties.dataSource(dataSource); |
|||
} |
|||
|
|||
@Bean |
|||
@ConfigurationProperties("spring.datasource.druid.slave") |
|||
@ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true") |
|||
public DataSource slaveDataSource(DruidProperties druidProperties) |
|||
{ |
|||
DruidDataSource dataSource = DruidDataSourceBuilder.create().build(); |
|||
return druidProperties.dataSource(dataSource); |
|||
} |
|||
|
|||
@Bean(name = "dynamicDataSource") |
|||
@Primary |
|||
public DynamicDataSource dataSource(DataSource masterDataSource) |
|||
{ |
|||
Map<Object, Object> targetDataSources = new HashMap<>(); |
|||
targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource); |
|||
setDataSource(targetDataSources, DataSourceType.SLAVE.name(), "slaveDataSource"); |
|||
return new DynamicDataSource(masterDataSource, targetDataSources); |
|||
} |
|||
|
|||
/** |
|||
* 设置数据源 |
|||
* |
|||
* @param targetDataSources 备选数据源集合 |
|||
* @param sourceName 数据源名称 |
|||
* @param beanName bean名称 |
|||
*/ |
|||
public void setDataSource(Map<Object, Object> targetDataSources, String sourceName, String beanName) |
|||
{ |
|||
try |
|||
{ |
|||
DataSource dataSource = SpringUtils.getBean(beanName); |
|||
targetDataSources.put(sourceName, dataSource); |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 去除监控页面底部的广告 |
|||
*/ |
|||
@SuppressWarnings({ "rawtypes", "unchecked" }) |
|||
@Bean |
|||
@ConditionalOnProperty(name = "spring.datasource.druid.statViewServlet.enabled", havingValue = "true") |
|||
public FilterRegistrationBean removeDruidFilterRegistrationBean(DruidStatProperties properties) |
|||
{ |
|||
// 获取web监控页面的参数
|
|||
DruidStatProperties.StatViewServlet config = properties.getStatViewServlet(); |
|||
// 提取common.js的配置路径
|
|||
String pattern = config.getUrlPattern() != null ? config.getUrlPattern() : "/druid/*"; |
|||
String commonJsPattern = pattern.replaceAll("\\*", "js/common.js"); |
|||
final String filePath = "support/http/resources/js/common.js"; |
|||
// 创建filter进行过滤
|
|||
Filter filter = new Filter() |
|||
{ |
|||
@Override |
|||
public void init(javax.servlet.FilterConfig filterConfig) throws ServletException |
|||
{ |
|||
} |
|||
@Override |
|||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) |
|||
throws IOException, ServletException |
|||
{ |
|||
chain.doFilter(request, response); |
|||
// 重置缓冲区,响应头不会被重置
|
|||
response.resetBuffer(); |
|||
// 获取common.js
|
|||
String text = Utils.readFromResource(filePath); |
|||
// 正则替换banner, 除去底部的广告信息
|
|||
text = text.replaceAll("<a.*?banner\"></a><br/>", ""); |
|||
text = text.replaceAll("powered.*?shrek.wang</a>", ""); |
|||
response.getWriter().write(text); |
|||
} |
|||
@Override |
|||
public void destroy() |
|||
{ |
|||
} |
|||
}; |
|||
FilterRegistrationBean registrationBean = new FilterRegistrationBean(); |
|||
registrationBean.setFilter(filter); |
|||
registrationBean.addUrlPatterns(commonJsPattern); |
|||
return registrationBean; |
|||
} |
|||
} |
@ -0,0 +1,52 @@ |
|||
package com.lzbi.framework.config; |
|||
|
|||
import java.nio.charset.Charset; |
|||
import org.springframework.data.redis.serializer.RedisSerializer; |
|||
import org.springframework.data.redis.serializer.SerializationException; |
|||
import com.alibaba.fastjson2.JSON; |
|||
import com.alibaba.fastjson2.JSONReader; |
|||
import com.alibaba.fastjson2.JSONWriter; |
|||
import com.alibaba.fastjson2.filter.Filter; |
|||
import com.lzbi.common.constant.Constants; |
|||
|
|||
/** |
|||
* Redis使用FastJson序列化 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
public class FastJson2JsonRedisSerializer<T> implements RedisSerializer<T> |
|||
{ |
|||
public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); |
|||
|
|||
static final Filter AUTO_TYPE_FILTER = JSONReader.autoTypeFilter(Constants.JSON_WHITELIST_STR); |
|||
|
|||
private Class<T> clazz; |
|||
|
|||
public FastJson2JsonRedisSerializer(Class<T> clazz) |
|||
{ |
|||
super(); |
|||
this.clazz = clazz; |
|||
} |
|||
|
|||
@Override |
|||
public byte[] serialize(T t) throws SerializationException |
|||
{ |
|||
if (t == null) |
|||
{ |
|||
return new byte[0]; |
|||
} |
|||
return JSON.toJSONString(t, JSONWriter.Feature.WriteClassName).getBytes(DEFAULT_CHARSET); |
|||
} |
|||
|
|||
@Override |
|||
public T deserialize(byte[] bytes) throws SerializationException |
|||
{ |
|||
if (bytes == null || bytes.length <= 0) |
|||
{ |
|||
return null; |
|||
} |
|||
String str = new String(bytes, DEFAULT_CHARSET); |
|||
|
|||
return JSON.parseObject(str, clazz, AUTO_TYPE_FILTER); |
|||
} |
|||
} |
@ -0,0 +1,58 @@ |
|||
package com.lzbi.framework.config; |
|||
|
|||
import java.util.HashMap; |
|||
import java.util.Map; |
|||
import javax.servlet.DispatcherType; |
|||
import org.springframework.beans.factory.annotation.Value; |
|||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
|||
import org.springframework.boot.web.servlet.FilterRegistrationBean; |
|||
import org.springframework.context.annotation.Bean; |
|||
import org.springframework.context.annotation.Configuration; |
|||
import com.lzbi.common.filter.RepeatableFilter; |
|||
import com.lzbi.common.filter.XssFilter; |
|||
import com.lzbi.common.utils.StringUtils; |
|||
|
|||
/** |
|||
* Filter配置 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
@Configuration |
|||
public class FilterConfig |
|||
{ |
|||
@Value("${xss.excludes}") |
|||
private String excludes; |
|||
|
|||
@Value("${xss.urlPatterns}") |
|||
private String urlPatterns; |
|||
|
|||
@SuppressWarnings({ "rawtypes", "unchecked" }) |
|||
@Bean |
|||
@ConditionalOnProperty(value = "xss.enabled", havingValue = "true") |
|||
public FilterRegistrationBean xssFilterRegistration() |
|||
{ |
|||
FilterRegistrationBean registration = new FilterRegistrationBean(); |
|||
registration.setDispatcherTypes(DispatcherType.REQUEST); |
|||
registration.setFilter(new XssFilter()); |
|||
registration.addUrlPatterns(StringUtils.split(urlPatterns, ",")); |
|||
registration.setName("xssFilter"); |
|||
registration.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE); |
|||
Map<String, String> initParameters = new HashMap<String, String>(); |
|||
initParameters.put("excludes", excludes); |
|||
registration.setInitParameters(initParameters); |
|||
return registration; |
|||
} |
|||
|
|||
@SuppressWarnings({ "rawtypes", "unchecked" }) |
|||
@Bean |
|||
public FilterRegistrationBean someFilterRegistration() |
|||
{ |
|||
FilterRegistrationBean registration = new FilterRegistrationBean(); |
|||
registration.setFilter(new RepeatableFilter()); |
|||
registration.addUrlPatterns("/*"); |
|||
registration.setName("repeatableFilter"); |
|||
registration.setOrder(FilterRegistrationBean.LOWEST_PRECEDENCE); |
|||
return registration; |
|||
} |
|||
|
|||
} |
@ -0,0 +1,68 @@ |
|||
package com.lzbi.framework.config; |
|||
|
|||
import java.util.Random; |
|||
import com.google.code.kaptcha.text.impl.DefaultTextCreator; |
|||
|
|||
/** |
|||
* 验证码文本生成器 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
public class KaptchaTextCreator extends DefaultTextCreator |
|||
{ |
|||
private static final String[] CNUMBERS = "0,1,2,3,4,5,6,7,8,9,10".split(","); |
|||
|
|||
@Override |
|||
public String getText() |
|||
{ |
|||
Integer result = 0; |
|||
Random random = new Random(); |
|||
int x = random.nextInt(10); |
|||
int y = random.nextInt(10); |
|||
StringBuilder suChinese = new StringBuilder(); |
|||
int randomoperands = random.nextInt(3); |
|||
if (randomoperands == 0) |
|||
{ |
|||
result = x * y; |
|||
suChinese.append(CNUMBERS[x]); |
|||
suChinese.append("*"); |
|||
suChinese.append(CNUMBERS[y]); |
|||
} |
|||
else if (randomoperands == 1) |
|||
{ |
|||
if ((x != 0) && y % x == 0) |
|||
{ |
|||
result = y / x; |
|||
suChinese.append(CNUMBERS[y]); |
|||
suChinese.append("/"); |
|||
suChinese.append(CNUMBERS[x]); |
|||
} |
|||
else |
|||
{ |
|||
result = x + y; |
|||
suChinese.append(CNUMBERS[x]); |
|||
suChinese.append("+"); |
|||
suChinese.append(CNUMBERS[y]); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
if (x >= y) |
|||
{ |
|||
result = x - y; |
|||
suChinese.append(CNUMBERS[x]); |
|||
suChinese.append("-"); |
|||
suChinese.append(CNUMBERS[y]); |
|||
} |
|||
else |
|||
{ |
|||
result = y - x; |
|||
suChinese.append(CNUMBERS[y]); |
|||
suChinese.append("-"); |
|||
suChinese.append(CNUMBERS[x]); |
|||
} |
|||
} |
|||
suChinese.append("=?@" + result); |
|||
return suChinese.toString(); |
|||
} |
|||
} |
@ -0,0 +1,62 @@ |
|||
package com.lzbi.framework.config; |
|||
|
|||
import com.baomidou.mybatisplus.annotation.DbType; |
|||
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; |
|||
import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor; |
|||
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor; |
|||
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; |
|||
import org.springframework.context.annotation.Bean; |
|||
import org.springframework.context.annotation.Configuration; |
|||
import org.springframework.transaction.annotation.EnableTransactionManagement; |
|||
|
|||
/** |
|||
* Mybatis Plus 配置 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
@EnableTransactionManagement(proxyTargetClass = true) |
|||
@Configuration |
|||
public class MybatisPlusConfig |
|||
{ |
|||
@Bean |
|||
public MybatisPlusInterceptor mybatisPlusInterceptor() |
|||
{ |
|||
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); |
|||
// 分页插件
|
|||
interceptor.addInnerInterceptor(paginationInnerInterceptor()); |
|||
// 乐观锁插件
|
|||
interceptor.addInnerInterceptor(optimisticLockerInnerInterceptor()); |
|||
// 阻断插件
|
|||
interceptor.addInnerInterceptor(blockAttackInnerInterceptor()); |
|||
return interceptor; |
|||
} |
|||
|
|||
/** |
|||
* 分页插件,自动识别数据库类型 https://baomidou.com/guide/interceptor-pagination.html
|
|||
*/ |
|||
public PaginationInnerInterceptor paginationInnerInterceptor() |
|||
{ |
|||
PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(); |
|||
// 设置数据库类型为mysql
|
|||
paginationInnerInterceptor.setDbType(DbType.MYSQL); |
|||
// 设置最大单页限制数量,默认 500 条,-1 不受限制
|
|||
paginationInnerInterceptor.setMaxLimit(-1L); |
|||
return paginationInnerInterceptor; |
|||
} |
|||
|
|||
/** |
|||
* 乐观锁插件 https://baomidou.com/guide/interceptor-optimistic-locker.html
|
|||
*/ |
|||
public OptimisticLockerInnerInterceptor optimisticLockerInnerInterceptor() |
|||
{ |
|||
return new OptimisticLockerInnerInterceptor(); |
|||
} |
|||
|
|||
/** |
|||
* 如果是对全表的删除或更新操作,就会终止该操作 https://baomidou.com/guide/interceptor-block-attack.html
|
|||
*/ |
|||
public BlockAttackInnerInterceptor blockAttackInnerInterceptor() |
|||
{ |
|||
return new BlockAttackInnerInterceptor(); |
|||
} |
|||
} |
@ -0,0 +1,69 @@ |
|||
package com.lzbi.framework.config; |
|||
|
|||
import org.springframework.cache.annotation.CachingConfigurerSupport; |
|||
import org.springframework.cache.annotation.EnableCaching; |
|||
import org.springframework.context.annotation.Bean; |
|||
import org.springframework.context.annotation.Configuration; |
|||
import org.springframework.data.redis.connection.RedisConnectionFactory; |
|||
import org.springframework.data.redis.core.RedisTemplate; |
|||
import org.springframework.data.redis.core.script.DefaultRedisScript; |
|||
import org.springframework.data.redis.serializer.StringRedisSerializer; |
|||
|
|||
/** |
|||
* redis配置 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
@Configuration |
|||
@EnableCaching |
|||
public class RedisConfig extends CachingConfigurerSupport |
|||
{ |
|||
@Bean |
|||
@SuppressWarnings(value = { "unchecked", "rawtypes" }) |
|||
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) |
|||
{ |
|||
RedisTemplate<Object, Object> template = new RedisTemplate<>(); |
|||
template.setConnectionFactory(connectionFactory); |
|||
|
|||
FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class); |
|||
|
|||
// 使用StringRedisSerializer来序列化和反序列化redis的key值
|
|||
template.setKeySerializer(new StringRedisSerializer()); |
|||
template.setValueSerializer(serializer); |
|||
|
|||
// Hash的key也采用StringRedisSerializer的序列化方式
|
|||
template.setHashKeySerializer(new StringRedisSerializer()); |
|||
template.setHashValueSerializer(serializer); |
|||
|
|||
template.afterPropertiesSet(); |
|||
return template; |
|||
} |
|||
|
|||
@Bean |
|||
public DefaultRedisScript<Long> limitScript() |
|||
{ |
|||
DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(); |
|||
redisScript.setScriptText(limitScriptText()); |
|||
redisScript.setResultType(Long.class); |
|||
return redisScript; |
|||
} |
|||
|
|||
/** |
|||
* 限流脚本 |
|||
*/ |
|||
private String limitScriptText() |
|||
{ |
|||
return "local key = KEYS[1]\n" + |
|||
"local count = tonumber(ARGV[1])\n" + |
|||
"local time = tonumber(ARGV[2])\n" + |
|||
"local current = redis.call('get', key);\n" + |
|||
"if current and tonumber(current) > count then\n" + |
|||
" return tonumber(current);\n" + |
|||
"end\n" + |
|||
"current = redis.call('incr', key)\n" + |
|||
"if tonumber(current) == 1 then\n" + |
|||
" redis.call('expire', key, time)\n" + |
|||
"end\n" + |
|||
"return tonumber(current);"; |
|||
} |
|||
} |
@ -0,0 +1,73 @@ |
|||
package com.lzbi.framework.config; |
|||
|
|||
import java.util.concurrent.TimeUnit; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.context.annotation.Bean; |
|||
import org.springframework.context.annotation.Configuration; |
|||
import org.springframework.http.CacheControl; |
|||
import org.springframework.web.cors.CorsConfiguration; |
|||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource; |
|||
import org.springframework.web.filter.CorsFilter; |
|||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry; |
|||
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; |
|||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; |
|||
import com.lzbi.common.config.RuoYiConfig; |
|||
import com.lzbi.common.constant.Constants; |
|||
import com.lzbi.framework.interceptor.RepeatSubmitInterceptor; |
|||
|
|||
/** |
|||
* 通用配置 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
@Configuration |
|||
public class ResourcesConfig implements WebMvcConfigurer |
|||
{ |
|||
@Autowired |
|||
private RepeatSubmitInterceptor repeatSubmitInterceptor; |
|||
|
|||
@Override |
|||
public void addResourceHandlers(ResourceHandlerRegistry registry) |
|||
{ |
|||
/** 本地文件上传路径 */ |
|||
registry.addResourceHandler(Constants.RESOURCE_PREFIX + "/**") |
|||
.addResourceLocations("file:" + RuoYiConfig.getProfile() + "/"); |
|||
|
|||
// /** swagger配置 */
|
|||
// registry.addResourceHandler("/swagger-ui/**")
|
|||
// .addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/")
|
|||
// .setCacheControl(CacheControl.maxAge(5, TimeUnit.HOURS).cachePublic());;
|
|||
} |
|||
|
|||
/** |
|||
* 自定义拦截规则 |
|||
*/ |
|||
@Override |
|||
public void addInterceptors(InterceptorRegistry registry) |
|||
{ |
|||
registry.addInterceptor(repeatSubmitInterceptor).addPathPatterns("/**"); |
|||
} |
|||
|
|||
/** |
|||
* 跨域配置 |
|||
*/ |
|||
@Bean |
|||
public CorsFilter corsFilter() |
|||
{ |
|||
CorsConfiguration config = new CorsConfiguration(); |
|||
config.setAllowCredentials(true); |
|||
// 设置访问源地址
|
|||
config.addAllowedOriginPattern("*"); |
|||
// 设置访问源请求头
|
|||
config.addAllowedHeader("*"); |
|||
// 设置访问源请求方法
|
|||
config.addAllowedMethod("*"); |
|||
// 有效期 1800秒
|
|||
config.setMaxAge(1800L); |
|||
// 添加映射路径,拦截一切请求
|
|||
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); |
|||
source.registerCorsConfiguration("/**", config); |
|||
// 返回新的CorsFilter
|
|||
return new CorsFilter(source); |
|||
} |
|||
} |
@ -0,0 +1,148 @@ |
|||
package com.lzbi.framework.config; |
|||
|
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.context.annotation.Bean; |
|||
import org.springframework.http.HttpMethod; |
|||
import org.springframework.security.authentication.AuthenticationManager; |
|||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; |
|||
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; |
|||
import org.springframework.security.config.annotation.web.builders.HttpSecurity; |
|||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; |
|||
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer; |
|||
import org.springframework.security.config.http.SessionCreationPolicy; |
|||
import org.springframework.security.core.userdetails.UserDetailsService; |
|||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; |
|||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; |
|||
import org.springframework.security.web.authentication.logout.LogoutFilter; |
|||
import org.springframework.web.filter.CorsFilter; |
|||
import com.lzbi.framework.config.properties.PermitAllUrlProperties; |
|||
import com.lzbi.framework.security.filter.JwtAuthenticationTokenFilter; |
|||
import com.lzbi.framework.security.handle.AuthenticationEntryPointImpl; |
|||
import com.lzbi.framework.security.handle.LogoutSuccessHandlerImpl; |
|||
|
|||
/** |
|||
* spring security配置 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) |
|||
public class SecurityConfig extends WebSecurityConfigurerAdapter |
|||
{ |
|||
/** |
|||
* 自定义用户认证逻辑 |
|||
*/ |
|||
@Autowired |
|||
private UserDetailsService userDetailsService; |
|||
|
|||
/** |
|||
* 认证失败处理类 |
|||
*/ |
|||
@Autowired |
|||
private AuthenticationEntryPointImpl unauthorizedHandler; |
|||
|
|||
/** |
|||
* 退出处理类 |
|||
*/ |
|||
@Autowired |
|||
private LogoutSuccessHandlerImpl logoutSuccessHandler; |
|||
|
|||
/** |
|||
* token认证过滤器 |
|||
*/ |
|||
@Autowired |
|||
private JwtAuthenticationTokenFilter authenticationTokenFilter; |
|||
|
|||
/** |
|||
* 跨域过滤器 |
|||
*/ |
|||
@Autowired |
|||
private CorsFilter corsFilter; |
|||
|
|||
/** |
|||
* 允许匿名访问的地址 |
|||
*/ |
|||
@Autowired |
|||
private PermitAllUrlProperties permitAllUrl; |
|||
|
|||
/** |
|||
* 解决 无法直接注入 AuthenticationManager |
|||
* |
|||
* @return |
|||
* @throws Exception |
|||
*/ |
|||
@Bean |
|||
@Override |
|||
public AuthenticationManager authenticationManagerBean() throws Exception |
|||
{ |
|||
return super.authenticationManagerBean(); |
|||
} |
|||
|
|||
/** |
|||
* anyRequest | 匹配所有请求路径 |
|||
* access | SpringEl表达式结果为true时可以访问 |
|||
* anonymous | 匿名可以访问 |
|||
* denyAll | 用户不能访问 |
|||
* fullyAuthenticated | 用户完全认证可以访问(非remember-me下自动登录) |
|||
* hasAnyAuthority | 如果有参数,参数表示权限,则其中任何一个权限可以访问 |
|||
* hasAnyRole | 如果有参数,参数表示角色,则其中任何一个角色可以访问 |
|||
* hasAuthority | 如果有参数,参数表示权限,则其权限可以访问 |
|||
* hasIpAddress | 如果有参数,参数表示IP地址,如果用户IP和参数匹配,则可以访问 |
|||
* hasRole | 如果有参数,参数表示角色,则其角色可以访问 |
|||
* permitAll | 用户可以任意访问 |
|||
* rememberMe | 允许通过remember-me登录的用户访问 |
|||
* authenticated | 用户登录后可访问 |
|||
*/ |
|||
@Override |
|||
protected void configure(HttpSecurity httpSecurity) throws Exception |
|||
{ |
|||
// 注解标记允许匿名访问的url
|
|||
ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = httpSecurity.authorizeRequests(); |
|||
permitAllUrl.getUrls().forEach(url -> registry.antMatchers(url).permitAll()); |
|||
|
|||
httpSecurity |
|||
// CSRF禁用,因为不使用session
|
|||
.csrf().disable() |
|||
// 禁用HTTP响应标头
|
|||
.headers().cacheControl().disable().and() |
|||
// 认证失败处理类
|
|||
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and() |
|||
// 基于token,所以不需要session
|
|||
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() |
|||
// 过滤请求
|
|||
.authorizeRequests() |
|||
// 对于登录login 注册register 验证码captchaImage 允许匿名访问
|
|||
.antMatchers("/login", "/register", "/captchaImage","/assetData/**").permitAll() |
|||
// 静态资源,可匿名访问
|
|||
.antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll() |
|||
.antMatchers( "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll() |
|||
// 除上面外的所有请求全部需要鉴权认证
|
|||
.anyRequest().authenticated() |
|||
.and() |
|||
.headers().frameOptions().disable(); |
|||
// 添加Logout filter
|
|||
httpSecurity.logout().logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler); |
|||
// 添加JWT filter
|
|||
httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); |
|||
// 添加CORS filter
|
|||
httpSecurity.addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class); |
|||
httpSecurity.addFilterBefore(corsFilter, LogoutFilter.class); |
|||
} |
|||
|
|||
/** |
|||
* 强散列哈希加密实现 |
|||
*/ |
|||
@Bean |
|||
public BCryptPasswordEncoder bCryptPasswordEncoder() |
|||
{ |
|||
return new BCryptPasswordEncoder(); |
|||
} |
|||
|
|||
/** |
|||
* 身份认证接口 |
|||
*/ |
|||
@Override |
|||
protected void configure(AuthenticationManagerBuilder auth) throws Exception |
|||
{ |
|||
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder()); |
|||
} |
|||
} |
@ -0,0 +1,32 @@ |
|||
package com.lzbi.framework.config; |
|||
|
|||
import javax.servlet.http.HttpServletRequest; |
|||
import org.springframework.stereotype.Component; |
|||
import com.lzbi.common.utils.ServletUtils; |
|||
|
|||
/** |
|||
* 服务相关配置 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
@Component |
|||
public class ServerConfig |
|||
{ |
|||
/** |
|||
* 获取完整的请求路径,包括:域名,端口,上下文访问路径 |
|||
* |
|||
* @return 服务地址 |
|||
*/ |
|||
public String getUrl() |
|||
{ |
|||
HttpServletRequest request = ServletUtils.getRequest(); |
|||
return getDomain(request); |
|||
} |
|||
|
|||
public static String getDomain(HttpServletRequest request) |
|||
{ |
|||
StringBuffer url = request.getRequestURL(); |
|||
String contextPath = request.getServletContext().getContextPath(); |
|||
return url.delete(url.length() - request.getRequestURI().length(), url.length()).append(contextPath).toString(); |
|||
} |
|||
} |
@ -0,0 +1,63 @@ |
|||
package com.lzbi.framework.config; |
|||
|
|||
import com.lzbi.common.utils.Threads; |
|||
import org.apache.commons.lang3.concurrent.BasicThreadFactory; |
|||
import org.springframework.context.annotation.Bean; |
|||
import org.springframework.context.annotation.Configuration; |
|||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; |
|||
import java.util.concurrent.ScheduledExecutorService; |
|||
import java.util.concurrent.ScheduledThreadPoolExecutor; |
|||
import java.util.concurrent.ThreadPoolExecutor; |
|||
|
|||
/** |
|||
* 线程池配置 |
|||
* |
|||
* @author ruoyi |
|||
**/ |
|||
@Configuration |
|||
public class ThreadPoolConfig |
|||
{ |
|||
// 核心线程池大小
|
|||
private int corePoolSize = 50; |
|||
|
|||
// 最大可创建的线程数
|
|||
private int maxPoolSize = 200; |
|||
|
|||
// 队列最大长度
|
|||
private int queueCapacity = 1000; |
|||
|
|||
// 线程池维护线程所允许的空闲时间
|
|||
private int keepAliveSeconds = 300; |
|||
|
|||
@Bean(name = "threadPoolTaskExecutor") |
|||
public ThreadPoolTaskExecutor threadPoolTaskExecutor() |
|||
{ |
|||
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); |
|||
executor.setMaxPoolSize(maxPoolSize); |
|||
executor.setCorePoolSize(corePoolSize); |
|||
executor.setQueueCapacity(queueCapacity); |
|||
executor.setKeepAliveSeconds(keepAliveSeconds); |
|||
// 线程池对拒绝任务(无线程可用)的处理策略
|
|||
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); |
|||
return executor; |
|||
} |
|||
|
|||
/** |
|||
* 执行周期性或定时任务 |
|||
*/ |
|||
@Bean(name = "scheduledExecutorService") |
|||
protected ScheduledExecutorService scheduledExecutorService() |
|||
{ |
|||
return new ScheduledThreadPoolExecutor(corePoolSize, |
|||
new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build(), |
|||
new ThreadPoolExecutor.CallerRunsPolicy()) |
|||
{ |
|||
@Override |
|||
protected void afterExecute(Runnable r, Throwable t) |
|||
{ |
|||
super.afterExecute(r, t); |
|||
Threads.printException(r, t); |
|||
} |
|||
}; |
|||
} |
|||
} |
@ -0,0 +1,89 @@ |
|||
package com.lzbi.framework.config.properties; |
|||
|
|||
import org.springframework.beans.factory.annotation.Value; |
|||
import org.springframework.context.annotation.Configuration; |
|||
import com.alibaba.druid.pool.DruidDataSource; |
|||
|
|||
/** |
|||
* druid 配置属性 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
@Configuration |
|||
public class DruidProperties |
|||
{ |
|||
@Value("${spring.datasource.druid.initialSize}") |
|||
private int initialSize; |
|||
|
|||
@Value("${spring.datasource.druid.minIdle}") |
|||
private int minIdle; |
|||
|
|||
@Value("${spring.datasource.druid.maxActive}") |
|||
private int maxActive; |
|||
|
|||
@Value("${spring.datasource.druid.maxWait}") |
|||
private int maxWait; |
|||
|
|||
@Value("${spring.datasource.druid.connectTimeout}") |
|||
private int connectTimeout; |
|||
|
|||
@Value("${spring.datasource.druid.socketTimeout}") |
|||
private int socketTimeout; |
|||
|
|||
@Value("${spring.datasource.druid.timeBetweenEvictionRunsMillis}") |
|||
private int timeBetweenEvictionRunsMillis; |
|||
|
|||
@Value("${spring.datasource.druid.minEvictableIdleTimeMillis}") |
|||
private int minEvictableIdleTimeMillis; |
|||
|
|||
@Value("${spring.datasource.druid.maxEvictableIdleTimeMillis}") |
|||
private int maxEvictableIdleTimeMillis; |
|||
|
|||
@Value("${spring.datasource.druid.validationQuery}") |
|||
private String validationQuery; |
|||
|
|||
@Value("${spring.datasource.druid.testWhileIdle}") |
|||
private boolean testWhileIdle; |
|||
|
|||
@Value("${spring.datasource.druid.testOnBorrow}") |
|||
private boolean testOnBorrow; |
|||
|
|||
@Value("${spring.datasource.druid.testOnReturn}") |
|||
private boolean testOnReturn; |
|||
|
|||
public DruidDataSource dataSource(DruidDataSource datasource) |
|||
{ |
|||
/** 配置初始化大小、最小、最大 */ |
|||
datasource.setInitialSize(initialSize); |
|||
datasource.setMaxActive(maxActive); |
|||
datasource.setMinIdle(minIdle); |
|||
|
|||
/** 配置获取连接等待超时的时间 */ |
|||
datasource.setMaxWait(maxWait); |
|||
|
|||
/** 配置驱动连接超时时间,检测数据库建立连接的超时时间,单位是毫秒 */ |
|||
datasource.setConnectTimeout(connectTimeout); |
|||
|
|||
/** 配置网络超时时间,等待数据库操作完成的网络超时时间,单位是毫秒 */ |
|||
datasource.setSocketTimeout(socketTimeout); |
|||
|
|||
/** 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 */ |
|||
datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis); |
|||
|
|||
/** 配置一个连接在池中最小、最大生存的时间,单位是毫秒 */ |
|||
datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis); |
|||
datasource.setMaxEvictableIdleTimeMillis(maxEvictableIdleTimeMillis); |
|||
|
|||
/** |
|||
* 用来检测连接是否有效的sql,要求是一个查询语句,常用select 'x'。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会起作用。 |
|||
*/ |
|||
datasource.setValidationQuery(validationQuery); |
|||
/** 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。 */ |
|||
datasource.setTestWhileIdle(testWhileIdle); |
|||
/** 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 */ |
|||
datasource.setTestOnBorrow(testOnBorrow); |
|||
/** 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 */ |
|||
datasource.setTestOnReturn(testOnReturn); |
|||
return datasource; |
|||
} |
|||
} |
@ -0,0 +1,73 @@ |
|||
package com.lzbi.framework.config.properties; |
|||
|
|||
import java.util.ArrayList; |
|||
import java.util.List; |
|||
import java.util.Map; |
|||
import java.util.Objects; |
|||
import java.util.Optional; |
|||
import java.util.regex.Pattern; |
|||
import org.apache.commons.lang3.RegExUtils; |
|||
import org.springframework.beans.BeansException; |
|||
import org.springframework.beans.factory.InitializingBean; |
|||
import org.springframework.context.ApplicationContext; |
|||
import org.springframework.context.ApplicationContextAware; |
|||
import org.springframework.context.annotation.Configuration; |
|||
import org.springframework.core.annotation.AnnotationUtils; |
|||
import org.springframework.web.method.HandlerMethod; |
|||
import org.springframework.web.servlet.mvc.method.RequestMappingInfo; |
|||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; |
|||
import com.lzbi.common.annotation.Anonymous; |
|||
|
|||
/** |
|||
* 设置Anonymous注解允许匿名访问的url |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
@Configuration |
|||
public class PermitAllUrlProperties implements InitializingBean, ApplicationContextAware |
|||
{ |
|||
private static final Pattern PATTERN = Pattern.compile("\\{(.*?)\\}"); |
|||
|
|||
private ApplicationContext applicationContext; |
|||
|
|||
private List<String> urls = new ArrayList<>(); |
|||
|
|||
public String ASTERISK = "*"; |
|||
|
|||
@Override |
|||
public void afterPropertiesSet() |
|||
{ |
|||
RequestMappingHandlerMapping mapping = applicationContext.getBean(RequestMappingHandlerMapping.class); |
|||
Map<RequestMappingInfo, HandlerMethod> map = mapping.getHandlerMethods(); |
|||
|
|||
map.keySet().forEach(info -> { |
|||
HandlerMethod handlerMethod = map.get(info); |
|||
|
|||
// 获取方法上边的注解 替代path variable 为 *
|
|||
Anonymous method = AnnotationUtils.findAnnotation(handlerMethod.getMethod(), Anonymous.class); |
|||
Optional.ofNullable(method).ifPresent(anonymous -> Objects.requireNonNull(info.getPatternsCondition().getPatterns()) |
|||
.forEach(url -> urls.add(RegExUtils.replaceAll(url, PATTERN, ASTERISK)))); |
|||
|
|||
// 获取类上边的注解, 替代path variable 为 *
|
|||
Anonymous controller = AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), Anonymous.class); |
|||
Optional.ofNullable(controller).ifPresent(anonymous -> Objects.requireNonNull(info.getPatternsCondition().getPatterns()) |
|||
.forEach(url -> urls.add(RegExUtils.replaceAll(url, PATTERN, ASTERISK)))); |
|||
}); |
|||
} |
|||
|
|||
@Override |
|||
public void setApplicationContext(ApplicationContext context) throws BeansException |
|||
{ |
|||
this.applicationContext = context; |
|||
} |
|||
|
|||
public List<String> getUrls() |
|||
{ |
|||
return urls; |
|||
} |
|||
|
|||
public void setUrls(List<String> urls) |
|||
{ |
|||
this.urls = urls; |
|||
} |
|||
} |
@ -0,0 +1,26 @@ |
|||
package com.lzbi.framework.datasource; |
|||
|
|||
import java.util.Map; |
|||
import javax.sql.DataSource; |
|||
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; |
|||
|
|||
/** |
|||
* 动态数据源 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
public class DynamicDataSource extends AbstractRoutingDataSource |
|||
{ |
|||
public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) |
|||
{ |
|||
super.setDefaultTargetDataSource(defaultTargetDataSource); |
|||
super.setTargetDataSources(targetDataSources); |
|||
super.afterPropertiesSet(); |
|||
} |
|||
|
|||
@Override |
|||
protected Object determineCurrentLookupKey() |
|||
{ |
|||
return DynamicDataSourceContextHolder.getDataSourceType(); |
|||
} |
|||
} |
@ -0,0 +1,45 @@ |
|||
package com.lzbi.framework.datasource; |
|||
|
|||
import org.slf4j.Logger; |
|||
import org.slf4j.LoggerFactory; |
|||
|
|||
/** |
|||
* 数据源切换处理 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
public class DynamicDataSourceContextHolder |
|||
{ |
|||
public static final Logger log = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class); |
|||
|
|||
/** |
|||
* 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本, |
|||
* 所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。 |
|||
*/ |
|||
private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>(); |
|||
|
|||
/** |
|||
* 设置数据源的变量 |
|||
*/ |
|||
public static void setDataSourceType(String dsType) |
|||
{ |
|||
log.info("切换到{}数据源", dsType); |
|||
CONTEXT_HOLDER.set(dsType); |
|||
} |
|||
|
|||
/** |
|||
* 获得数据源的变量 |
|||
*/ |
|||
public static String getDataSourceType() |
|||
{ |
|||
return CONTEXT_HOLDER.get(); |
|||
} |
|||
|
|||
/** |
|||
* 清空数据源变量 |
|||
*/ |
|||
public static void clearDataSourceType() |
|||
{ |
|||
CONTEXT_HOLDER.remove(); |
|||
} |
|||
} |
@ -0,0 +1,56 @@ |
|||
package com.lzbi.framework.interceptor; |
|||
|
|||
import java.lang.reflect.Method; |
|||
import javax.servlet.http.HttpServletRequest; |
|||
import javax.servlet.http.HttpServletResponse; |
|||
import org.springframework.stereotype.Component; |
|||
import org.springframework.web.method.HandlerMethod; |
|||
import org.springframework.web.servlet.HandlerInterceptor; |
|||
import com.alibaba.fastjson2.JSON; |
|||
import com.lzbi.common.annotation.RepeatSubmit; |
|||
import com.lzbi.common.core.domain.AjaxResult; |
|||
import com.lzbi.common.utils.ServletUtils; |
|||
|
|||
/** |
|||
* 防止重复提交拦截器 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
@Component |
|||
public abstract class RepeatSubmitInterceptor implements HandlerInterceptor |
|||
{ |
|||
@Override |
|||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception |
|||
{ |
|||
if (handler instanceof HandlerMethod) |
|||
{ |
|||
HandlerMethod handlerMethod = (HandlerMethod) handler; |
|||
Method method = handlerMethod.getMethod(); |
|||
RepeatSubmit annotation = method.getAnnotation(RepeatSubmit.class); |
|||
if (annotation != null) |
|||
{ |
|||
if (this.isRepeatSubmit(request, annotation)) |
|||
{ |
|||
AjaxResult ajaxResult = AjaxResult.error(annotation.message()); |
|||
ServletUtils.renderString(response, JSON.toJSONString(ajaxResult)); |
|||
return false; |
|||
} |
|||
} |
|||
return true; |
|||
} |
|||
else |
|||
{ |
|||
return true; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 验证是否重复提交由子类实现具体的防重复提交的规则 |
|||
* |
|||
* @param request 请求信息 |
|||
* @param annotation 防重复注解参数 |
|||
* @return 结果 |
|||
* @throws Exception |
|||
*/ |
|||
public abstract boolean isRepeatSubmit(HttpServletRequest request, RepeatSubmit annotation); |
|||
} |
@ -0,0 +1,110 @@ |
|||
package com.lzbi.framework.interceptor.impl; |
|||
|
|||
import java.util.HashMap; |
|||
import java.util.Map; |
|||
import java.util.concurrent.TimeUnit; |
|||
import javax.servlet.http.HttpServletRequest; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.beans.factory.annotation.Value; |
|||
import org.springframework.stereotype.Component; |
|||
import com.alibaba.fastjson2.JSON; |
|||
import com.lzbi.common.annotation.RepeatSubmit; |
|||
import com.lzbi.common.constant.CacheConstants; |
|||
import com.lzbi.common.core.redis.RedisCache; |
|||
import com.lzbi.common.filter.RepeatedlyRequestWrapper; |
|||
import com.lzbi.common.utils.StringUtils; |
|||
import com.lzbi.common.utils.http.HttpHelper; |
|||
import com.lzbi.framework.interceptor.RepeatSubmitInterceptor; |
|||
|
|||
/** |
|||
* 判断请求url和数据是否和上一次相同, |
|||
* 如果和上次相同,则是重复提交表单。 有效时间为10秒内。 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
@Component |
|||
public class SameUrlDataInterceptor extends RepeatSubmitInterceptor |
|||
{ |
|||
public final String REPEAT_PARAMS = "repeatParams"; |
|||
|
|||
public final String REPEAT_TIME = "repeatTime"; |
|||
|
|||
// 令牌自定义标识
|
|||
@Value("${token.header}") |
|||
private String header; |
|||
|
|||
@Autowired |
|||
private RedisCache redisCache; |
|||
|
|||
@SuppressWarnings("unchecked") |
|||
@Override |
|||
public boolean isRepeatSubmit(HttpServletRequest request, RepeatSubmit annotation) |
|||
{ |
|||
String nowParams = ""; |
|||
if (request instanceof RepeatedlyRequestWrapper) |
|||
{ |
|||
RepeatedlyRequestWrapper repeatedlyRequest = (RepeatedlyRequestWrapper) request; |
|||
nowParams = HttpHelper.getBodyString(repeatedlyRequest); |
|||
} |
|||
|
|||
// body参数为空,获取Parameter的数据
|
|||
if (StringUtils.isEmpty(nowParams)) |
|||
{ |
|||
nowParams = JSON.toJSONString(request.getParameterMap()); |
|||
} |
|||
Map<String, Object> nowDataMap = new HashMap<String, Object>(); |
|||
nowDataMap.put(REPEAT_PARAMS, nowParams); |
|||
nowDataMap.put(REPEAT_TIME, System.currentTimeMillis()); |
|||
|
|||
// 请求地址(作为存放cache的key值)
|
|||
String url = request.getRequestURI(); |
|||
|
|||
// 唯一值(没有消息头则使用请求地址)
|
|||
String submitKey = StringUtils.trimToEmpty(request.getHeader(header)); |
|||
|
|||
// 唯一标识(指定key + url + 消息头)
|
|||
String cacheRepeatKey = CacheConstants.REPEAT_SUBMIT_KEY + url + submitKey; |
|||
|
|||
Object sessionObj = redisCache.getCacheObject(cacheRepeatKey); |
|||
if (sessionObj != null) |
|||
{ |
|||
Map<String, Object> sessionMap = (Map<String, Object>) sessionObj; |
|||
if (sessionMap.containsKey(url)) |
|||
{ |
|||
Map<String, Object> preDataMap = (Map<String, Object>) sessionMap.get(url); |
|||
if (compareParams(nowDataMap, preDataMap) && compareTime(nowDataMap, preDataMap, annotation.interval())) |
|||
{ |
|||
return true; |
|||
} |
|||
} |
|||
} |
|||
Map<String, Object> cacheMap = new HashMap<String, Object>(); |
|||
cacheMap.put(url, nowDataMap); |
|||
redisCache.setCacheObject(cacheRepeatKey, cacheMap, annotation.interval(), TimeUnit.MILLISECONDS); |
|||
return false; |
|||
} |
|||
|
|||
/** |
|||
* 判断参数是否相同 |
|||
*/ |
|||
private boolean compareParams(Map<String, Object> nowMap, Map<String, Object> preMap) |
|||
{ |
|||
String nowParams = (String) nowMap.get(REPEAT_PARAMS); |
|||
String preParams = (String) preMap.get(REPEAT_PARAMS); |
|||
return nowParams.equals(preParams); |
|||
} |
|||
|
|||
/** |
|||
* 判断两次间隔时间 |
|||
*/ |
|||
private boolean compareTime(Map<String, Object> nowMap, Map<String, Object> preMap, int interval) |
|||
{ |
|||
long time1 = (Long) nowMap.get(REPEAT_TIME); |
|||
long time2 = (Long) preMap.get(REPEAT_TIME); |
|||
if ((time1 - time2) < interval) |
|||
{ |
|||
return true; |
|||
} |
|||
return false; |
|||
} |
|||
} |
@ -0,0 +1,55 @@ |
|||
package com.lzbi.framework.manager; |
|||
|
|||
import java.util.TimerTask; |
|||
import java.util.concurrent.ScheduledExecutorService; |
|||
import java.util.concurrent.TimeUnit; |
|||
import com.lzbi.common.utils.Threads; |
|||
import com.lzbi.common.utils.spring.SpringUtils; |
|||
|
|||
/** |
|||
* 异步任务管理器 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
public class AsyncManager |
|||
{ |
|||
/** |
|||
* 操作延迟10毫秒 |
|||
*/ |
|||
private final int OPERATE_DELAY_TIME = 10; |
|||
|
|||
/** |
|||
* 异步操作任务调度线程池 |
|||
*/ |
|||
private ScheduledExecutorService executor = SpringUtils.getBean("scheduledExecutorService"); |
|||
|
|||
/** |
|||
* 单例模式 |
|||
*/ |
|||
private AsyncManager(){} |
|||
|
|||
private static AsyncManager me = new AsyncManager(); |
|||
|
|||
public static AsyncManager me() |
|||
{ |
|||
return me; |
|||
} |
|||
|
|||
/** |
|||
* 执行任务 |
|||
* |
|||
* @param task 任务 |
|||
*/ |
|||
public void execute(TimerTask task) |
|||
{ |
|||
executor.schedule(task, OPERATE_DELAY_TIME, TimeUnit.MILLISECONDS); |
|||
} |
|||
|
|||
/** |
|||
* 停止任务线程池 |
|||
*/ |
|||
public void shutdown() |
|||
{ |
|||
Threads.shutdownAndAwaitTermination(executor); |
|||
} |
|||
} |
@ -0,0 +1,39 @@ |
|||
package com.lzbi.framework.manager; |
|||
|
|||
import org.slf4j.Logger; |
|||
import org.slf4j.LoggerFactory; |
|||
import org.springframework.stereotype.Component; |
|||
import javax.annotation.PreDestroy; |
|||
|
|||
/** |
|||
* 确保应用退出时能关闭后台线程 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
@Component |
|||
public class ShutdownManager |
|||
{ |
|||
private static final Logger logger = LoggerFactory.getLogger("sys-user"); |
|||
|
|||
@PreDestroy |
|||
public void destroy() |
|||
{ |
|||
shutdownAsyncManager(); |
|||
} |
|||
|
|||
/** |
|||
* 停止异步执行任务 |
|||
*/ |
|||
private void shutdownAsyncManager() |
|||
{ |
|||
try |
|||
{ |
|||
logger.info("====关闭后台任务任务线程池===="); |
|||
AsyncManager.me().shutdown(); |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
logger.error(e.getMessage(), e); |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,102 @@ |
|||
package com.lzbi.framework.manager.factory; |
|||
|
|||
import java.util.TimerTask; |
|||
import org.slf4j.Logger; |
|||
import org.slf4j.LoggerFactory; |
|||
import com.lzbi.common.constant.Constants; |
|||
import com.lzbi.common.utils.LogUtils; |
|||
import com.lzbi.common.utils.ServletUtils; |
|||
import com.lzbi.common.utils.StringUtils; |
|||
import com.lzbi.common.utils.ip.AddressUtils; |
|||
import com.lzbi.common.utils.ip.IpUtils; |
|||
import com.lzbi.common.utils.spring.SpringUtils; |
|||
import com.lzbi.system.domain.SysLogininfor; |
|||
import com.lzbi.system.domain.SysOperLog; |
|||
import com.lzbi.system.service.ISysLogininforService; |
|||
import com.lzbi.system.service.ISysOperLogService; |
|||
import eu.bitwalker.useragentutils.UserAgent; |
|||
|
|||
/** |
|||
* 异步工厂(产生任务用) |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
public class AsyncFactory |
|||
{ |
|||
private static final Logger sys_user_logger = LoggerFactory.getLogger("sys-user"); |
|||
|
|||
/** |
|||
* 记录登录信息 |
|||
* |
|||
* @param username 用户名 |
|||
* @param status 状态 |
|||
* @param message 消息 |
|||
* @param args 列表 |
|||
* @return 任务task |
|||
*/ |
|||
public static TimerTask recordLogininfor(final String username, final String status, final String message, |
|||
final Object... args) |
|||
{ |
|||
final UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent")); |
|||
final String ip = IpUtils.getIpAddr(); |
|||
return new TimerTask() |
|||
{ |
|||
@Override |
|||
public void run() |
|||
{ |
|||
String address = AddressUtils.getRealAddressByIP(ip); |
|||
StringBuilder s = new StringBuilder(); |
|||
s.append(LogUtils.getBlock(ip)); |
|||
s.append(address); |
|||
s.append(LogUtils.getBlock(username)); |
|||
s.append(LogUtils.getBlock(status)); |
|||
s.append(LogUtils.getBlock(message)); |
|||
// 打印信息到日志
|
|||
sys_user_logger.info(s.toString(), args); |
|||
// 获取客户端操作系统
|
|||
String os = userAgent.getOperatingSystem().getName(); |
|||
// 获取客户端浏览器
|
|||
String browser = userAgent.getBrowser().getName(); |
|||
// 封装对象
|
|||
SysLogininfor logininfor = new SysLogininfor(); |
|||
logininfor.setUserName(username); |
|||
logininfor.setIpaddr(ip); |
|||
logininfor.setLoginLocation(address); |
|||
logininfor.setBrowser(browser); |
|||
logininfor.setOs(os); |
|||
logininfor.setMsg(message); |
|||
// 日志状态
|
|||
if (StringUtils.equalsAny(status, Constants.LOGIN_SUCCESS, Constants.LOGOUT, Constants.REGISTER)) |
|||
{ |
|||
logininfor.setStatus(Constants.SUCCESS); |
|||
} |
|||
else if (Constants.LOGIN_FAIL.equals(status)) |
|||
{ |
|||
logininfor.setStatus(Constants.FAIL); |
|||
} |
|||
// 插入数据
|
|||
SpringUtils.getBean(ISysLogininforService.class).insertLogininfor(logininfor); |
|||
} |
|||
}; |
|||
} |
|||
|
|||
/** |
|||
* 操作日志记录 |
|||
* |
|||
* @param operLog 操作日志信息 |
|||
* @return 任务task |
|||
*/ |
|||
public static TimerTask recordOper(final SysOperLog operLog) |
|||
{ |
|||
return new TimerTask() |
|||
{ |
|||
@Override |
|||
public void run() |
|||
{ |
|||
// 远程查询操作地点
|
|||
operLog.setOperLocation(AddressUtils.getRealAddressByIP(operLog.getOperIp())); |
|||
SpringUtils.getBean(ISysOperLogService.class).insertOperlog(operLog); |
|||
} |
|||
}; |
|||
} |
|||
} |
@ -0,0 +1,28 @@ |
|||
package com.lzbi.framework.security.context; |
|||
|
|||
import org.springframework.security.core.Authentication; |
|||
|
|||
/** |
|||
* 身份验证信息 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
public class AuthenticationContextHolder |
|||
{ |
|||
private static final ThreadLocal<Authentication> contextHolder = new ThreadLocal<>(); |
|||
|
|||
public static Authentication getContext() |
|||
{ |
|||
return contextHolder.get(); |
|||
} |
|||
|
|||
public static void setContext(Authentication context) |
|||
{ |
|||
contextHolder.set(context); |
|||
} |
|||
|
|||
public static void clearContext() |
|||
{ |
|||
contextHolder.remove(); |
|||
} |
|||
} |
@ -0,0 +1,27 @@ |
|||
package com.lzbi.framework.security.context; |
|||
|
|||
import org.springframework.web.context.request.RequestAttributes; |
|||
import org.springframework.web.context.request.RequestContextHolder; |
|||
import com.lzbi.common.core.text.Convert; |
|||
|
|||
/** |
|||
* 权限信息 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
public class PermissionContextHolder |
|||
{ |
|||
private static final String PERMISSION_CONTEXT_ATTRIBUTES = "PERMISSION_CONTEXT"; |
|||
|
|||
public static void setContext(String permission) |
|||
{ |
|||
RequestContextHolder.currentRequestAttributes().setAttribute(PERMISSION_CONTEXT_ATTRIBUTES, permission, |
|||
RequestAttributes.SCOPE_REQUEST); |
|||
} |
|||
|
|||
public static String getContext() |
|||
{ |
|||
return Convert.toStr(RequestContextHolder.currentRequestAttributes().getAttribute(PERMISSION_CONTEXT_ATTRIBUTES, |
|||
RequestAttributes.SCOPE_REQUEST)); |
|||
} |
|||
} |
@ -0,0 +1,44 @@ |
|||
package com.lzbi.framework.security.filter; |
|||
|
|||
import java.io.IOException; |
|||
import javax.servlet.FilterChain; |
|||
import javax.servlet.ServletException; |
|||
import javax.servlet.http.HttpServletRequest; |
|||
import javax.servlet.http.HttpServletResponse; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; |
|||
import org.springframework.security.core.context.SecurityContextHolder; |
|||
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; |
|||
import org.springframework.stereotype.Component; |
|||
import org.springframework.web.filter.OncePerRequestFilter; |
|||
import com.lzbi.common.core.domain.model.LoginUser; |
|||
import com.lzbi.common.utils.SecurityUtils; |
|||
import com.lzbi.common.utils.StringUtils; |
|||
import com.lzbi.framework.web.service.TokenService; |
|||
|
|||
/** |
|||
* token过滤器 验证token有效性 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
@Component |
|||
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter |
|||
{ |
|||
@Autowired |
|||
private TokenService tokenService; |
|||
|
|||
@Override |
|||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) |
|||
throws ServletException, IOException |
|||
{ |
|||
LoginUser loginUser = tokenService.getLoginUser(request); |
|||
if (StringUtils.isNotNull(loginUser) && StringUtils.isNull(SecurityUtils.getAuthentication())) |
|||
{ |
|||
tokenService.verifyToken(loginUser); |
|||
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities()); |
|||
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); |
|||
SecurityContextHolder.getContext().setAuthentication(authenticationToken); |
|||
} |
|||
chain.doFilter(request, response); |
|||
} |
|||
} |
@ -0,0 +1,34 @@ |
|||
package com.lzbi.framework.security.handle; |
|||
|
|||
import java.io.IOException; |
|||
import java.io.Serializable; |
|||
import javax.servlet.http.HttpServletRequest; |
|||
import javax.servlet.http.HttpServletResponse; |
|||
import org.springframework.security.core.AuthenticationException; |
|||
import org.springframework.security.web.AuthenticationEntryPoint; |
|||
import org.springframework.stereotype.Component; |
|||
import com.alibaba.fastjson2.JSON; |
|||
import com.lzbi.common.constant.HttpStatus; |
|||
import com.lzbi.common.core.domain.AjaxResult; |
|||
import com.lzbi.common.utils.ServletUtils; |
|||
import com.lzbi.common.utils.StringUtils; |
|||
|
|||
/** |
|||
* 认证失败处理类 返回未授权 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
@Component |
|||
public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint, Serializable |
|||
{ |
|||
private static final long serialVersionUID = -8970718410437077606L; |
|||
|
|||
@Override |
|||
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) |
|||
throws IOException |
|||
{ |
|||
int code = HttpStatus.UNAUTHORIZED; |
|||
String msg = StringUtils.format("请求访问:{},认证失败,无法访问系统资源", request.getRequestURI()); |
|||
ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.error(code, msg))); |
|||
} |
|||
} |
@ -0,0 +1,52 @@ |
|||
package com.lzbi.framework.security.handle; |
|||
|
|||
import java.io.IOException; |
|||
import javax.servlet.ServletException; |
|||
import javax.servlet.http.HttpServletRequest; |
|||
import javax.servlet.http.HttpServletResponse; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.context.annotation.Configuration; |
|||
import org.springframework.security.core.Authentication; |
|||
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; |
|||
import com.alibaba.fastjson2.JSON; |
|||
import com.lzbi.common.constant.Constants; |
|||
import com.lzbi.common.core.domain.AjaxResult; |
|||
import com.lzbi.common.core.domain.model.LoginUser; |
|||
import com.lzbi.common.utils.ServletUtils; |
|||
import com.lzbi.common.utils.StringUtils; |
|||
import com.lzbi.framework.manager.AsyncManager; |
|||
import com.lzbi.framework.manager.factory.AsyncFactory; |
|||
import com.lzbi.framework.web.service.TokenService; |
|||
|
|||
/** |
|||
* 自定义退出处理类 返回成功 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
@Configuration |
|||
public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler |
|||
{ |
|||
@Autowired |
|||
private TokenService tokenService; |
|||
|
|||
/** |
|||
* 退出处理 |
|||
* |
|||
* @return |
|||
*/ |
|||
@Override |
|||
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) |
|||
throws IOException, ServletException |
|||
{ |
|||
LoginUser loginUser = tokenService.getLoginUser(request); |
|||
if (StringUtils.isNotNull(loginUser)) |
|||
{ |
|||
String userName = loginUser.getUsername(); |
|||
// 删除用户缓存记录
|
|||
tokenService.delLoginUser(loginUser.getToken()); |
|||
// 记录用户退出日志
|
|||
AsyncManager.me().execute(AsyncFactory.recordLogininfor(userName, Constants.LOGOUT, "退出成功")); |
|||
} |
|||
ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.success("退出成功"))); |
|||
} |
|||
} |
@ -0,0 +1,240 @@ |
|||
package com.lzbi.framework.web.domain; |
|||
|
|||
import java.net.UnknownHostException; |
|||
import java.util.LinkedList; |
|||
import java.util.List; |
|||
import java.util.Properties; |
|||
import com.lzbi.common.utils.Arith; |
|||
import com.lzbi.common.utils.ip.IpUtils; |
|||
import com.lzbi.framework.web.domain.server.Cpu; |
|||
import com.lzbi.framework.web.domain.server.Jvm; |
|||
import com.lzbi.framework.web.domain.server.Mem; |
|||
import com.lzbi.framework.web.domain.server.Sys; |
|||
import com.lzbi.framework.web.domain.server.SysFile; |
|||
import oshi.SystemInfo; |
|||
import oshi.hardware.CentralProcessor; |
|||
import oshi.hardware.CentralProcessor.TickType; |
|||
import oshi.hardware.GlobalMemory; |
|||
import oshi.hardware.HardwareAbstractionLayer; |
|||
import oshi.software.os.FileSystem; |
|||
import oshi.software.os.OSFileStore; |
|||
import oshi.software.os.OperatingSystem; |
|||
import oshi.util.Util; |
|||
|
|||
/** |
|||
* 服务器相关信息 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
public class Server |
|||
{ |
|||
private static final int OSHI_WAIT_SECOND = 1000; |
|||
|
|||
/** |
|||
* CPU相关信息 |
|||
*/ |
|||
private Cpu cpu = new Cpu(); |
|||
|
|||
/** |
|||
* 內存相关信息 |
|||
*/ |
|||
private Mem mem = new Mem(); |
|||
|
|||
/** |
|||
* JVM相关信息 |
|||
*/ |
|||
private Jvm jvm = new Jvm(); |
|||
|
|||
/** |
|||
* 服务器相关信息 |
|||
*/ |
|||
private Sys sys = new Sys(); |
|||
|
|||
/** |
|||
* 磁盘相关信息 |
|||
*/ |
|||
private List<SysFile> sysFiles = new LinkedList<SysFile>(); |
|||
|
|||
public Cpu getCpu() |
|||
{ |
|||
return cpu; |
|||
} |
|||
|
|||
public void setCpu(Cpu cpu) |
|||
{ |
|||
this.cpu = cpu; |
|||
} |
|||
|
|||
public Mem getMem() |
|||
{ |
|||
return mem; |
|||
} |
|||
|
|||
public void setMem(Mem mem) |
|||
{ |
|||
this.mem = mem; |
|||
} |
|||
|
|||
public Jvm getJvm() |
|||
{ |
|||
return jvm; |
|||
} |
|||
|
|||
public void setJvm(Jvm jvm) |
|||
{ |
|||
this.jvm = jvm; |
|||
} |
|||
|
|||
public Sys getSys() |
|||
{ |
|||
return sys; |
|||
} |
|||
|
|||
public void setSys(Sys sys) |
|||
{ |
|||
this.sys = sys; |
|||
} |
|||
|
|||
public List<SysFile> getSysFiles() |
|||
{ |
|||
return sysFiles; |
|||
} |
|||
|
|||
public void setSysFiles(List<SysFile> sysFiles) |
|||
{ |
|||
this.sysFiles = sysFiles; |
|||
} |
|||
|
|||
public void copyTo() throws Exception |
|||
{ |
|||
SystemInfo si = new SystemInfo(); |
|||
HardwareAbstractionLayer hal = si.getHardware(); |
|||
|
|||
setCpuInfo(hal.getProcessor()); |
|||
|
|||
setMemInfo(hal.getMemory()); |
|||
|
|||
setSysInfo(); |
|||
|
|||
setJvmInfo(); |
|||
|
|||
setSysFiles(si.getOperatingSystem()); |
|||
} |
|||
|
|||
/** |
|||
* 设置CPU信息 |
|||
*/ |
|||
private void setCpuInfo(CentralProcessor processor) |
|||
{ |
|||
// CPU信息
|
|||
long[] prevTicks = processor.getSystemCpuLoadTicks(); |
|||
Util.sleep(OSHI_WAIT_SECOND); |
|||
long[] ticks = processor.getSystemCpuLoadTicks(); |
|||
long nice = ticks[TickType.NICE.getIndex()] - prevTicks[TickType.NICE.getIndex()]; |
|||
long irq = ticks[TickType.IRQ.getIndex()] - prevTicks[TickType.IRQ.getIndex()]; |
|||
long softirq = ticks[TickType.SOFTIRQ.getIndex()] - prevTicks[TickType.SOFTIRQ.getIndex()]; |
|||
long steal = ticks[TickType.STEAL.getIndex()] - prevTicks[TickType.STEAL.getIndex()]; |
|||
long cSys = ticks[TickType.SYSTEM.getIndex()] - prevTicks[TickType.SYSTEM.getIndex()]; |
|||
long user = ticks[TickType.USER.getIndex()] - prevTicks[TickType.USER.getIndex()]; |
|||
long iowait = ticks[TickType.IOWAIT.getIndex()] - prevTicks[TickType.IOWAIT.getIndex()]; |
|||
long idle = ticks[TickType.IDLE.getIndex()] - prevTicks[TickType.IDLE.getIndex()]; |
|||
long totalCpu = user + nice + cSys + idle + iowait + irq + softirq + steal; |
|||
cpu.setCpuNum(processor.getLogicalProcessorCount()); |
|||
cpu.setTotal(totalCpu); |
|||
cpu.setSys(cSys); |
|||
cpu.setUsed(user); |
|||
cpu.setWait(iowait); |
|||
cpu.setFree(idle); |
|||
} |
|||
|
|||
/** |
|||
* 设置内存信息 |
|||
*/ |
|||
private void setMemInfo(GlobalMemory memory) |
|||
{ |
|||
mem.setTotal(memory.getTotal()); |
|||
mem.setUsed(memory.getTotal() - memory.getAvailable()); |
|||
mem.setFree(memory.getAvailable()); |
|||
} |
|||
|
|||
/** |
|||
* 设置服务器信息 |
|||
*/ |
|||
private void setSysInfo() |
|||
{ |
|||
Properties props = System.getProperties(); |
|||
sys.setComputerName(IpUtils.getHostName()); |
|||
sys.setComputerIp(IpUtils.getHostIp()); |
|||
sys.setOsName(props.getProperty("os.name")); |
|||
sys.setOsArch(props.getProperty("os.arch")); |
|||
sys.setUserDir(props.getProperty("user.dir")); |
|||
} |
|||
|
|||
/** |
|||
* 设置Java虚拟机 |
|||
*/ |
|||
private void setJvmInfo() throws UnknownHostException |
|||
{ |
|||
Properties props = System.getProperties(); |
|||
jvm.setTotal(Runtime.getRuntime().totalMemory()); |
|||
jvm.setMax(Runtime.getRuntime().maxMemory()); |
|||
jvm.setFree(Runtime.getRuntime().freeMemory()); |
|||
jvm.setVersion(props.getProperty("java.version")); |
|||
jvm.setHome(props.getProperty("java.home")); |
|||
} |
|||
|
|||
/** |
|||
* 设置磁盘信息 |
|||
*/ |
|||
private void setSysFiles(OperatingSystem os) |
|||
{ |
|||
FileSystem fileSystem = os.getFileSystem(); |
|||
List<OSFileStore> fsArray = fileSystem.getFileStores(); |
|||
for (OSFileStore fs : fsArray) |
|||
{ |
|||
long free = fs.getUsableSpace(); |
|||
long total = fs.getTotalSpace(); |
|||
long used = total - free; |
|||
SysFile sysFile = new SysFile(); |
|||
sysFile.setDirName(fs.getMount()); |
|||
sysFile.setSysTypeName(fs.getType()); |
|||
sysFile.setTypeName(fs.getName()); |
|||
sysFile.setTotal(convertFileSize(total)); |
|||
sysFile.setFree(convertFileSize(free)); |
|||
sysFile.setUsed(convertFileSize(used)); |
|||
sysFile.setUsage(Arith.mul(Arith.div(used, total, 4), 100)); |
|||
sysFiles.add(sysFile); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 字节转换 |
|||
* |
|||
* @param size 字节大小 |
|||
* @return 转换后值 |
|||
*/ |
|||
public String convertFileSize(long size) |
|||
{ |
|||
long kb = 1024; |
|||
long mb = kb * 1024; |
|||
long gb = mb * 1024; |
|||
if (size >= gb) |
|||
{ |
|||
return String.format("%.1f GB", (float) size / gb); |
|||
} |
|||
else if (size >= mb) |
|||
{ |
|||
float f = (float) size / mb; |
|||
return String.format(f > 100 ? "%.0f MB" : "%.1f MB", f); |
|||
} |
|||
else if (size >= kb) |
|||
{ |
|||
float f = (float) size / kb; |
|||
return String.format(f > 100 ? "%.0f KB" : "%.1f KB", f); |
|||
} |
|||
else |
|||
{ |
|||
return String.format("%d B", size); |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,101 @@ |
|||
package com.lzbi.framework.web.domain.server; |
|||
|
|||
import com.lzbi.common.utils.Arith; |
|||
|
|||
/** |
|||
* CPU相关信息 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
public class Cpu |
|||
{ |
|||
/** |
|||
* 核心数 |
|||
*/ |
|||
private int cpuNum; |
|||
|
|||
/** |
|||
* CPU总的使用率 |
|||
*/ |
|||
private double total; |
|||
|
|||
/** |
|||
* CPU系统使用率 |
|||
*/ |
|||
private double sys; |
|||
|
|||
/** |
|||
* CPU用户使用率 |
|||
*/ |
|||
private double used; |
|||
|
|||
/** |
|||
* CPU当前等待率 |
|||
*/ |
|||
private double wait; |
|||
|
|||
/** |
|||
* CPU当前空闲率 |
|||
*/ |
|||
private double free; |
|||
|
|||
public int getCpuNum() |
|||
{ |
|||
return cpuNum; |
|||
} |
|||
|
|||
public void setCpuNum(int cpuNum) |
|||
{ |
|||
this.cpuNum = cpuNum; |
|||
} |
|||
|
|||
public double getTotal() |
|||
{ |
|||
return Arith.round(Arith.mul(total, 100), 2); |
|||
} |
|||
|
|||
public void setTotal(double total) |
|||
{ |
|||
this.total = total; |
|||
} |
|||
|
|||
public double getSys() |
|||
{ |
|||
return Arith.round(Arith.mul(sys / total, 100), 2); |
|||
} |
|||
|
|||
public void setSys(double sys) |
|||
{ |
|||
this.sys = sys; |
|||
} |
|||
|
|||
public double getUsed() |
|||
{ |
|||
return Arith.round(Arith.mul(used / total, 100), 2); |
|||
} |
|||
|
|||
public void setUsed(double used) |
|||
{ |
|||
this.used = used; |
|||
} |
|||
|
|||
public double getWait() |
|||
{ |
|||
return Arith.round(Arith.mul(wait / total, 100), 2); |
|||
} |
|||
|
|||
public void setWait(double wait) |
|||
{ |
|||
this.wait = wait; |
|||
} |
|||
|
|||
public double getFree() |
|||
{ |
|||
return Arith.round(Arith.mul(free / total, 100), 2); |
|||
} |
|||
|
|||
public void setFree(double free) |
|||
{ |
|||
this.free = free; |
|||
} |
|||
} |
@ -0,0 +1,130 @@ |
|||
package com.lzbi.framework.web.domain.server; |
|||
|
|||
import java.lang.management.ManagementFactory; |
|||
import com.lzbi.common.utils.Arith; |
|||
import com.lzbi.common.utils.DateUtils; |
|||
|
|||
/** |
|||
* JVM相关信息 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
public class Jvm |
|||
{ |
|||
/** |
|||
* 当前JVM占用的内存总数(M) |
|||
*/ |
|||
private double total; |
|||
|
|||
/** |
|||
* JVM最大可用内存总数(M) |
|||
*/ |
|||
private double max; |
|||
|
|||
/** |
|||
* JVM空闲内存(M) |
|||
*/ |
|||
private double free; |
|||
|
|||
/** |
|||
* JDK版本 |
|||
*/ |
|||
private String version; |
|||
|
|||
/** |
|||
* JDK路径 |
|||
*/ |
|||
private String home; |
|||
|
|||
public double getTotal() |
|||
{ |
|||
return Arith.div(total, (1024 * 1024), 2); |
|||
} |
|||
|
|||
public void setTotal(double total) |
|||
{ |
|||
this.total = total; |
|||
} |
|||
|
|||
public double getMax() |
|||
{ |
|||
return Arith.div(max, (1024 * 1024), 2); |
|||
} |
|||
|
|||
public void setMax(double max) |
|||
{ |
|||
this.max = max; |
|||
} |
|||
|
|||
public double getFree() |
|||
{ |
|||
return Arith.div(free, (1024 * 1024), 2); |
|||
} |
|||
|
|||
public void setFree(double free) |
|||
{ |
|||
this.free = free; |
|||
} |
|||
|
|||
public double getUsed() |
|||
{ |
|||
return Arith.div(total - free, (1024 * 1024), 2); |
|||
} |
|||
|
|||
public double getUsage() |
|||
{ |
|||
return Arith.mul(Arith.div(total - free, total, 4), 100); |
|||
} |
|||
|
|||
/** |
|||
* 获取JDK名称 |
|||
*/ |
|||
public String getName() |
|||
{ |
|||
return ManagementFactory.getRuntimeMXBean().getVmName(); |
|||
} |
|||
|
|||
public String getVersion() |
|||
{ |
|||
return version; |
|||
} |
|||
|
|||
public void setVersion(String version) |
|||
{ |
|||
this.version = version; |
|||
} |
|||
|
|||
public String getHome() |
|||
{ |
|||
return home; |
|||
} |
|||
|
|||
public void setHome(String home) |
|||
{ |
|||
this.home = home; |
|||
} |
|||
|
|||
/** |
|||
* JDK启动时间 |
|||
*/ |
|||
public String getStartTime() |
|||
{ |
|||
return DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, DateUtils.getServerStartDate()); |
|||
} |
|||
|
|||
/** |
|||
* JDK运行时间 |
|||
*/ |
|||
public String getRunTime() |
|||
{ |
|||
return DateUtils.timeDistance(DateUtils.getNowDate(), DateUtils.getServerStartDate()); |
|||
} |
|||
|
|||
/** |
|||
* 运行参数 |
|||
*/ |
|||
public String getInputArgs() |
|||
{ |
|||
return ManagementFactory.getRuntimeMXBean().getInputArguments().toString(); |
|||
} |
|||
} |
@ -0,0 +1,61 @@ |
|||
package com.lzbi.framework.web.domain.server; |
|||
|
|||
import com.lzbi.common.utils.Arith; |
|||
|
|||
/** |
|||
* 內存相关信息 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
public class Mem |
|||
{ |
|||
/** |
|||
* 内存总量 |
|||
*/ |
|||
private double total; |
|||
|
|||
/** |
|||
* 已用内存 |
|||
*/ |
|||
private double used; |
|||
|
|||
/** |
|||
* 剩余内存 |
|||
*/ |
|||
private double free; |
|||
|
|||
public double getTotal() |
|||
{ |
|||
return Arith.div(total, (1024 * 1024 * 1024), 2); |
|||
} |
|||
|
|||
public void setTotal(long total) |
|||
{ |
|||
this.total = total; |
|||
} |
|||
|
|||
public double getUsed() |
|||
{ |
|||
return Arith.div(used, (1024 * 1024 * 1024), 2); |
|||
} |
|||
|
|||
public void setUsed(long used) |
|||
{ |
|||
this.used = used; |
|||
} |
|||
|
|||
public double getFree() |
|||
{ |
|||
return Arith.div(free, (1024 * 1024 * 1024), 2); |
|||
} |
|||
|
|||
public void setFree(long free) |
|||
{ |
|||
this.free = free; |
|||
} |
|||
|
|||
public double getUsage() |
|||
{ |
|||
return Arith.mul(Arith.div(used, total, 4), 100); |
|||
} |
|||
} |
@ -0,0 +1,84 @@ |
|||
package com.lzbi.framework.web.domain.server; |
|||
|
|||
/** |
|||
* 系统相关信息 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
public class Sys |
|||
{ |
|||
/** |
|||
* 服务器名称 |
|||
*/ |
|||
private String computerName; |
|||
|
|||
/** |
|||
* 服务器Ip |
|||
*/ |
|||
private String computerIp; |
|||
|
|||
/** |
|||
* 项目路径 |
|||
*/ |
|||
private String userDir; |
|||
|
|||
/** |
|||
* 操作系统 |
|||
*/ |
|||
private String osName; |
|||
|
|||
/** |
|||
* 系统架构 |
|||
*/ |
|||
private String osArch; |
|||
|
|||
public String getComputerName() |
|||
{ |
|||
return computerName; |
|||
} |
|||
|
|||
public void setComputerName(String computerName) |
|||
{ |
|||
this.computerName = computerName; |
|||
} |
|||
|
|||
public String getComputerIp() |
|||
{ |
|||
return computerIp; |
|||
} |
|||
|
|||
public void setComputerIp(String computerIp) |
|||
{ |
|||
this.computerIp = computerIp; |
|||
} |
|||
|
|||
public String getUserDir() |
|||
{ |
|||
return userDir; |
|||
} |
|||
|
|||
public void setUserDir(String userDir) |
|||
{ |
|||
this.userDir = userDir; |
|||
} |
|||
|
|||
public String getOsName() |
|||
{ |
|||
return osName; |
|||
} |
|||
|
|||
public void setOsName(String osName) |
|||
{ |
|||
this.osName = osName; |
|||
} |
|||
|
|||
public String getOsArch() |
|||
{ |
|||
return osArch; |
|||
} |
|||
|
|||
public void setOsArch(String osArch) |
|||
{ |
|||
this.osArch = osArch; |
|||
} |
|||
} |
@ -0,0 +1,114 @@ |
|||
package com.lzbi.framework.web.domain.server; |
|||
|
|||
/** |
|||
* 系统文件相关信息 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
public class SysFile |
|||
{ |
|||
/** |
|||
* 盘符路径 |
|||
*/ |
|||
private String dirName; |
|||
|
|||
/** |
|||
* 盘符类型 |
|||
*/ |
|||
private String sysTypeName; |
|||
|
|||
/** |
|||
* 文件类型 |
|||
*/ |
|||
private String typeName; |
|||
|
|||
/** |
|||
* 总大小 |
|||
*/ |
|||
private String total; |
|||
|
|||
/** |
|||
* 剩余大小 |
|||
*/ |
|||
private String free; |
|||
|
|||
/** |
|||
* 已经使用量 |
|||
*/ |
|||
private String used; |
|||
|
|||
/** |
|||
* 资源的使用率 |
|||
*/ |
|||
private double usage; |
|||
|
|||
public String getDirName() |
|||
{ |
|||
return dirName; |
|||
} |
|||
|
|||
public void setDirName(String dirName) |
|||
{ |
|||
this.dirName = dirName; |
|||
} |
|||
|
|||
public String getSysTypeName() |
|||
{ |
|||
return sysTypeName; |
|||
} |
|||
|
|||
public void setSysTypeName(String sysTypeName) |
|||
{ |
|||
this.sysTypeName = sysTypeName; |
|||
} |
|||
|
|||
public String getTypeName() |
|||
{ |
|||
return typeName; |
|||
} |
|||
|
|||
public void setTypeName(String typeName) |
|||
{ |
|||
this.typeName = typeName; |
|||
} |
|||
|
|||
public String getTotal() |
|||
{ |
|||
return total; |
|||
} |
|||
|
|||
public void setTotal(String total) |
|||
{ |
|||
this.total = total; |
|||
} |
|||
|
|||
public String getFree() |
|||
{ |
|||
return free; |
|||
} |
|||
|
|||
public void setFree(String free) |
|||
{ |
|||
this.free = free; |
|||
} |
|||
|
|||
public String getUsed() |
|||
{ |
|||
return used; |
|||
} |
|||
|
|||
public void setUsed(String used) |
|||
{ |
|||
this.used = used; |
|||
} |
|||
|
|||
public double getUsage() |
|||
{ |
|||
return usage; |
|||
} |
|||
|
|||
public void setUsage(double usage) |
|||
{ |
|||
this.usage = usage; |
|||
} |
|||
} |
@ -0,0 +1,138 @@ |
|||
package com.lzbi.framework.web.exception; |
|||
|
|||
import javax.servlet.http.HttpServletRequest; |
|||
import org.slf4j.Logger; |
|||
import org.slf4j.LoggerFactory; |
|||
import org.springframework.security.access.AccessDeniedException; |
|||
import org.springframework.validation.BindException; |
|||
import org.springframework.web.HttpRequestMethodNotSupportedException; |
|||
import org.springframework.web.bind.MethodArgumentNotValidException; |
|||
import org.springframework.web.bind.MissingPathVariableException; |
|||
import org.springframework.web.bind.annotation.ExceptionHandler; |
|||
import org.springframework.web.bind.annotation.RestControllerAdvice; |
|||
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; |
|||
import com.lzbi.common.constant.HttpStatus; |
|||
import com.lzbi.common.core.domain.AjaxResult; |
|||
import com.lzbi.common.exception.DemoModeException; |
|||
import com.lzbi.common.exception.ServiceException; |
|||
import com.lzbi.common.utils.StringUtils; |
|||
|
|||
/** |
|||
* 全局异常处理器 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
@RestControllerAdvice |
|||
public class GlobalExceptionHandler |
|||
{ |
|||
private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class); |
|||
|
|||
/** |
|||
* 权限校验异常 |
|||
*/ |
|||
@ExceptionHandler(AccessDeniedException.class) |
|||
public AjaxResult handleAccessDeniedException(AccessDeniedException e, HttpServletRequest request) |
|||
{ |
|||
String requestURI = request.getRequestURI(); |
|||
log.error("请求地址'{}',权限校验失败'{}'", requestURI, e.getMessage()); |
|||
return AjaxResult.error(HttpStatus.FORBIDDEN, "没有权限,请联系管理员授权"); |
|||
} |
|||
|
|||
/** |
|||
* 请求方式不支持 |
|||
*/ |
|||
@ExceptionHandler(HttpRequestMethodNotSupportedException.class) |
|||
public AjaxResult handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e, |
|||
HttpServletRequest request) |
|||
{ |
|||
String requestURI = request.getRequestURI(); |
|||
log.error("请求地址'{}',不支持'{}'请求", requestURI, e.getMethod()); |
|||
return AjaxResult.error(e.getMessage()); |
|||
} |
|||
|
|||
/** |
|||
* 业务异常 |
|||
*/ |
|||
@ExceptionHandler(ServiceException.class) |
|||
public AjaxResult handleServiceException(ServiceException e, HttpServletRequest request) |
|||
{ |
|||
log.error(e.getMessage(), e); |
|||
Integer code = e.getCode(); |
|||
return StringUtils.isNotNull(code) ? AjaxResult.error(code, e.getMessage()) : AjaxResult.error(e.getMessage()); |
|||
} |
|||
|
|||
/** |
|||
* 请求路径中缺少必需的路径变量 |
|||
*/ |
|||
@ExceptionHandler(MissingPathVariableException.class) |
|||
public AjaxResult handleMissingPathVariableException(MissingPathVariableException e, HttpServletRequest request) |
|||
{ |
|||
String requestURI = request.getRequestURI(); |
|||
log.error("请求路径中缺少必需的路径变量'{}',发生系统异常.", requestURI, e); |
|||
return AjaxResult.error(String.format("请求路径中缺少必需的路径变量[%s]", e.getVariableName())); |
|||
} |
|||
|
|||
/** |
|||
* 请求参数类型不匹配 |
|||
*/ |
|||
@ExceptionHandler(MethodArgumentTypeMismatchException.class) |
|||
public AjaxResult handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e, HttpServletRequest request) |
|||
{ |
|||
String requestURI = request.getRequestURI(); |
|||
log.error("请求参数类型不匹配'{}',发生系统异常.", requestURI, e); |
|||
return AjaxResult.error(String.format("请求参数类型不匹配,参数[%s]要求类型为:'%s',但输入值为:'%s'", e.getName(), e.getRequiredType().getName(), e.getValue())); |
|||
} |
|||
|
|||
/** |
|||
* 拦截未知的运行时异常 |
|||
*/ |
|||
@ExceptionHandler(RuntimeException.class) |
|||
public AjaxResult handleRuntimeException(RuntimeException e, HttpServletRequest request) |
|||
{ |
|||
String requestURI = request.getRequestURI(); |
|||
log.error("请求地址'{}',发生未知异常.", requestURI, e); |
|||
return AjaxResult.error(e.getMessage()); |
|||
} |
|||
|
|||
/** |
|||
* 系统异常 |
|||
*/ |
|||
@ExceptionHandler(Exception.class) |
|||
public AjaxResult handleException(Exception e, HttpServletRequest request) |
|||
{ |
|||
String requestURI = request.getRequestURI(); |
|||
log.error("请求地址'{}',发生系统异常.", requestURI, e); |
|||
return AjaxResult.error(e.getMessage()); |
|||
} |
|||
|
|||
/** |
|||
* 自定义验证异常 |
|||
*/ |
|||
@ExceptionHandler(BindException.class) |
|||
public AjaxResult handleBindException(BindException e) |
|||
{ |
|||
log.error(e.getMessage(), e); |
|||
String message = e.getAllErrors().get(0).getDefaultMessage(); |
|||
return AjaxResult.error(message); |
|||
} |
|||
|
|||
/** |
|||
* 自定义验证异常 |
|||
*/ |
|||
@ExceptionHandler(MethodArgumentNotValidException.class) |
|||
public Object handleMethodArgumentNotValidException(MethodArgumentNotValidException e) |
|||
{ |
|||
log.error(e.getMessage(), e); |
|||
String message = e.getBindingResult().getFieldError().getDefaultMessage(); |
|||
return AjaxResult.error(message); |
|||
} |
|||
|
|||
/** |
|||
* 演示模式异常 |
|||
*/ |
|||
@ExceptionHandler(DemoModeException.class) |
|||
public AjaxResult handleDemoModeException(DemoModeException e) |
|||
{ |
|||
return AjaxResult.error("演示模式,不允许操作"); |
|||
} |
|||
} |
@ -0,0 +1,159 @@ |
|||
package com.lzbi.framework.web.service; |
|||
|
|||
import java.util.Set; |
|||
import org.springframework.stereotype.Service; |
|||
import org.springframework.util.CollectionUtils; |
|||
import com.lzbi.common.constant.Constants; |
|||
import com.lzbi.common.core.domain.entity.SysRole; |
|||
import com.lzbi.common.core.domain.model.LoginUser; |
|||
import com.lzbi.common.utils.SecurityUtils; |
|||
import com.lzbi.common.utils.StringUtils; |
|||
import com.lzbi.framework.security.context.PermissionContextHolder; |
|||
|
|||
/** |
|||
* RuoYi首创 自定义权限实现,ss取自SpringSecurity首字母 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
@Service("ss") |
|||
public class PermissionService |
|||
{ |
|||
/** |
|||
* 验证用户是否具备某权限 |
|||
* |
|||
* @param permission 权限字符串 |
|||
* @return 用户是否具备某权限 |
|||
*/ |
|||
public boolean hasPermi(String permission) |
|||
{ |
|||
if (StringUtils.isEmpty(permission)) |
|||
{ |
|||
return false; |
|||
} |
|||
LoginUser loginUser = SecurityUtils.getLoginUser(); |
|||
if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getPermissions())) |
|||
{ |
|||
return false; |
|||
} |
|||
PermissionContextHolder.setContext(permission); |
|||
return hasPermissions(loginUser.getPermissions(), permission); |
|||
} |
|||
|
|||
/** |
|||
* 验证用户是否不具备某权限,与 hasPermi逻辑相反 |
|||
* |
|||
* @param permission 权限字符串 |
|||
* @return 用户是否不具备某权限 |
|||
*/ |
|||
public boolean lacksPermi(String permission) |
|||
{ |
|||
return hasPermi(permission) != true; |
|||
} |
|||
|
|||
/** |
|||
* 验证用户是否具有以下任意一个权限 |
|||
* |
|||
* @param permissions 以 PERMISSION_DELIMETER 为分隔符的权限列表 |
|||
* @return 用户是否具有以下任意一个权限 |
|||
*/ |
|||
public boolean hasAnyPermi(String permissions) |
|||
{ |
|||
if (StringUtils.isEmpty(permissions)) |
|||
{ |
|||
return false; |
|||
} |
|||
LoginUser loginUser = SecurityUtils.getLoginUser(); |
|||
if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getPermissions())) |
|||
{ |
|||
return false; |
|||
} |
|||
PermissionContextHolder.setContext(permissions); |
|||
Set<String> authorities = loginUser.getPermissions(); |
|||
for (String permission : permissions.split(Constants.PERMISSION_DELIMETER)) |
|||
{ |
|||
if (permission != null && hasPermissions(authorities, permission)) |
|||
{ |
|||
return true; |
|||
} |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
/** |
|||
* 判断用户是否拥有某个角色 |
|||
* |
|||
* @param role 角色字符串 |
|||
* @return 用户是否具备某角色 |
|||
*/ |
|||
public boolean hasRole(String role) |
|||
{ |
|||
if (StringUtils.isEmpty(role)) |
|||
{ |
|||
return false; |
|||
} |
|||
LoginUser loginUser = SecurityUtils.getLoginUser(); |
|||
if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getUser().getRoles())) |
|||
{ |
|||
return false; |
|||
} |
|||
for (SysRole sysRole : loginUser.getUser().getRoles()) |
|||
{ |
|||
String roleKey = sysRole.getRoleKey(); |
|||
if (Constants.SUPER_ADMIN.equals(roleKey) || roleKey.equals(StringUtils.trim(role))) |
|||
{ |
|||
return true; |
|||
} |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
/** |
|||
* 验证用户是否不具备某角色,与 isRole逻辑相反。 |
|||
* |
|||
* @param role 角色名称 |
|||
* @return 用户是否不具备某角色 |
|||
*/ |
|||
public boolean lacksRole(String role) |
|||
{ |
|||
return hasRole(role) != true; |
|||
} |
|||
|
|||
/** |
|||
* 验证用户是否具有以下任意一个角色 |
|||
* |
|||
* @param roles 以 ROLE_NAMES_DELIMETER 为分隔符的角色列表 |
|||
* @return 用户是否具有以下任意一个角色 |
|||
*/ |
|||
public boolean hasAnyRoles(String roles) |
|||
{ |
|||
if (StringUtils.isEmpty(roles)) |
|||
{ |
|||
return false; |
|||
} |
|||
LoginUser loginUser = SecurityUtils.getLoginUser(); |
|||
if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getUser().getRoles())) |
|||
{ |
|||
return false; |
|||
} |
|||
for (String role : roles.split(Constants.ROLE_DELIMETER)) |
|||
{ |
|||
if (hasRole(role)) |
|||
{ |
|||
return true; |
|||
} |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
/** |
|||
* 判断是否包含权限 |
|||
* |
|||
* @param permissions 权限列表 |
|||
* @param permission 权限字符串 |
|||
* @return 用户是否具备某权限 |
|||
*/ |
|||
private boolean hasPermissions(Set<String> permissions, String permission) |
|||
{ |
|||
return permissions.contains(Constants.ALL_PERMISSION) || permissions.contains(StringUtils.trim(permission)); |
|||
} |
|||
} |
@ -0,0 +1,181 @@ |
|||
package com.lzbi.framework.web.service; |
|||
|
|||
import javax.annotation.Resource; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.security.authentication.AuthenticationManager; |
|||
import org.springframework.security.authentication.BadCredentialsException; |
|||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; |
|||
import org.springframework.security.core.Authentication; |
|||
import org.springframework.stereotype.Component; |
|||
import com.lzbi.common.constant.CacheConstants; |
|||
import com.lzbi.common.constant.Constants; |
|||
import com.lzbi.common.constant.UserConstants; |
|||
import com.lzbi.common.core.domain.entity.SysUser; |
|||
import com.lzbi.common.core.domain.model.LoginUser; |
|||
import com.lzbi.common.core.redis.RedisCache; |
|||
import com.lzbi.common.exception.ServiceException; |
|||
import com.lzbi.common.exception.user.BlackListException; |
|||
import com.lzbi.common.exception.user.CaptchaException; |
|||
import com.lzbi.common.exception.user.CaptchaExpireException; |
|||
import com.lzbi.common.exception.user.UserNotExistsException; |
|||
import com.lzbi.common.exception.user.UserPasswordNotMatchException; |
|||
import com.lzbi.common.utils.DateUtils; |
|||
import com.lzbi.common.utils.MessageUtils; |
|||
import com.lzbi.common.utils.StringUtils; |
|||
import com.lzbi.common.utils.ip.IpUtils; |
|||
import com.lzbi.framework.manager.AsyncManager; |
|||
import com.lzbi.framework.manager.factory.AsyncFactory; |
|||
import com.lzbi.framework.security.context.AuthenticationContextHolder; |
|||
import com.lzbi.system.service.ISysConfigService; |
|||
import com.lzbi.system.service.ISysUserService; |
|||
|
|||
/** |
|||
* 登录校验方法 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
@Component |
|||
public class SysLoginService |
|||
{ |
|||
@Autowired |
|||
private TokenService tokenService; |
|||
|
|||
@Resource |
|||
private AuthenticationManager authenticationManager; |
|||
|
|||
@Autowired |
|||
private RedisCache redisCache; |
|||
|
|||
@Autowired |
|||
private ISysUserService userService; |
|||
|
|||
@Autowired |
|||
private ISysConfigService configService; |
|||
|
|||
/** |
|||
* 登录验证 |
|||
* |
|||
* @param username 用户名 |
|||
* @param password 密码 |
|||
* @param code 验证码 |
|||
* @param uuid 唯一标识 |
|||
* @return 结果 |
|||
*/ |
|||
public String login(String username, String password, String code, String uuid) |
|||
{ |
|||
// 验证码校验
|
|||
validateCaptcha(username, code, uuid); |
|||
// 登录前置校验
|
|||
loginPreCheck(username, password); |
|||
// 用户验证
|
|||
Authentication authentication = null; |
|||
try |
|||
{ |
|||
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password); |
|||
AuthenticationContextHolder.setContext(authenticationToken); |
|||
// 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
|
|||
authentication = authenticationManager.authenticate(authenticationToken); |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
if (e instanceof BadCredentialsException) |
|||
{ |
|||
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match"))); |
|||
throw new UserPasswordNotMatchException(); |
|||
} |
|||
else |
|||
{ |
|||
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage())); |
|||
throw new ServiceException(e.getMessage()); |
|||
} |
|||
} |
|||
finally |
|||
{ |
|||
AuthenticationContextHolder.clearContext(); |
|||
} |
|||
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"))); |
|||
LoginUser loginUser = (LoginUser) authentication.getPrincipal(); |
|||
recordLoginInfo(loginUser.getUserId()); |
|||
// 生成token
|
|||
return tokenService.createToken(loginUser); |
|||
} |
|||
|
|||
/** |
|||
* 校验验证码 |
|||
* |
|||
* @param username 用户名 |
|||
* @param code 验证码 |
|||
* @param uuid 唯一标识 |
|||
* @return 结果 |
|||
*/ |
|||
public void validateCaptcha(String username, String code, String uuid) |
|||
{ |
|||
boolean captchaEnabled = configService.selectCaptchaEnabled(); |
|||
if (captchaEnabled) |
|||
{ |
|||
String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StringUtils.nvl(uuid, ""); |
|||
String captcha = redisCache.getCacheObject(verifyKey); |
|||
redisCache.deleteObject(verifyKey); |
|||
if (captcha == null) |
|||
{ |
|||
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"))); |
|||
throw new CaptchaExpireException(); |
|||
} |
|||
if (!code.equalsIgnoreCase(captcha)) |
|||
{ |
|||
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error"))); |
|||
throw new CaptchaException(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 登录前置校验 |
|||
* @param username 用户名 |
|||
* @param password 用户密码 |
|||
*/ |
|||
public void loginPreCheck(String username, String password) |
|||
{ |
|||
// 用户名或密码为空 错误
|
|||
if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) |
|||
{ |
|||
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("not.null"))); |
|||
throw new UserNotExistsException(); |
|||
} |
|||
// 密码如果不在指定范围内 错误
|
|||
if (password.length() < UserConstants.PASSWORD_MIN_LENGTH |
|||
|| password.length() > UserConstants.PASSWORD_MAX_LENGTH) |
|||
{ |
|||
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match"))); |
|||
throw new UserPasswordNotMatchException(); |
|||
} |
|||
// 用户名不在指定范围内 错误
|
|||
if (username.length() < UserConstants.USERNAME_MIN_LENGTH |
|||
|| username.length() > UserConstants.USERNAME_MAX_LENGTH) |
|||
{ |
|||
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match"))); |
|||
throw new UserPasswordNotMatchException(); |
|||
} |
|||
// IP黑名单校验
|
|||
String blackStr = configService.selectConfigByKey("sys.login.blackIPList"); |
|||
if (IpUtils.isMatchedIp(blackStr, IpUtils.getIpAddr())) |
|||
{ |
|||
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("login.blocked"))); |
|||
throw new BlackListException(); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 记录登录信息 |
|||
* |
|||
* @param userId 用户ID |
|||
*/ |
|||
public void recordLoginInfo(Long userId) |
|||
{ |
|||
SysUser sysUser = new SysUser(); |
|||
sysUser.setUserId(userId); |
|||
sysUser.setLoginIp(IpUtils.getIpAddr()); |
|||
sysUser.setLoginDate(DateUtils.getNowDate()); |
|||
userService.updateUserProfile(sysUser); |
|||
} |
|||
} |
@ -0,0 +1,94 @@ |
|||
package com.lzbi.framework.web.service; |
|||
|
|||
import java.util.concurrent.TimeUnit; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.beans.factory.annotation.Value; |
|||
import org.springframework.security.core.Authentication; |
|||
import org.springframework.stereotype.Component; |
|||
import com.lzbi.common.constant.CacheConstants; |
|||
import com.lzbi.common.constant.Constants; |
|||
import com.lzbi.common.core.domain.entity.SysUser; |
|||
import com.lzbi.common.core.redis.RedisCache; |
|||
import com.lzbi.common.exception.user.UserPasswordNotMatchException; |
|||
import com.lzbi.common.exception.user.UserPasswordRetryLimitExceedException; |
|||
import com.lzbi.common.utils.MessageUtils; |
|||
import com.lzbi.common.utils.SecurityUtils; |
|||
import com.lzbi.framework.manager.AsyncManager; |
|||
import com.lzbi.framework.manager.factory.AsyncFactory; |
|||
import com.lzbi.framework.security.context.AuthenticationContextHolder; |
|||
|
|||
/** |
|||
* 登录密码方法 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
@Component |
|||
public class SysPasswordService |
|||
{ |
|||
@Autowired |
|||
private RedisCache redisCache; |
|||
|
|||
@Value(value = "${user.password.maxRetryCount}") |
|||
private int maxRetryCount; |
|||
|
|||
@Value(value = "${user.password.lockTime}") |
|||
private int lockTime; |
|||
|
|||
/** |
|||
* 登录账户密码错误次数缓存键名 |
|||
* |
|||
* @param username 用户名 |
|||
* @return 缓存键key |
|||
*/ |
|||
private String getCacheKey(String username) |
|||
{ |
|||
return CacheConstants.PWD_ERR_CNT_KEY + username; |
|||
} |
|||
|
|||
public void validate(SysUser user) |
|||
{ |
|||
Authentication usernamePasswordAuthenticationToken = AuthenticationContextHolder.getContext(); |
|||
String username = usernamePasswordAuthenticationToken.getName(); |
|||
String password = usernamePasswordAuthenticationToken.getCredentials().toString(); |
|||
|
|||
Integer retryCount = redisCache.getCacheObject(getCacheKey(username)); |
|||
|
|||
if (retryCount == null) |
|||
{ |
|||
retryCount = 0; |
|||
} |
|||
|
|||
if (retryCount >= Integer.valueOf(maxRetryCount).intValue()) |
|||
{ |
|||
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, |
|||
MessageUtils.message("user.password.retry.limit.exceed", maxRetryCount, lockTime))); |
|||
throw new UserPasswordRetryLimitExceedException(maxRetryCount, lockTime); |
|||
} |
|||
|
|||
if (!matches(user, password)) |
|||
{ |
|||
retryCount = retryCount + 1; |
|||
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, |
|||
MessageUtils.message("user.password.retry.limit.count", retryCount))); |
|||
redisCache.setCacheObject(getCacheKey(username), retryCount, lockTime, TimeUnit.MINUTES); |
|||
throw new UserPasswordNotMatchException(); |
|||
} |
|||
else |
|||
{ |
|||
clearLoginRecordCache(username); |
|||
} |
|||
} |
|||
|
|||
public boolean matches(SysUser user, String rawPassword) |
|||
{ |
|||
return SecurityUtils.matchesPassword(rawPassword, user.getPassword()); |
|||
} |
|||
|
|||
public void clearLoginRecordCache(String loginName) |
|||
{ |
|||
if (redisCache.hasKey(getCacheKey(loginName))) |
|||
{ |
|||
redisCache.deleteObject(getCacheKey(loginName)); |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,83 @@ |
|||
package com.lzbi.framework.web.service; |
|||
|
|||
import java.util.HashSet; |
|||
import java.util.List; |
|||
import java.util.Set; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.stereotype.Component; |
|||
import org.springframework.util.CollectionUtils; |
|||
import com.lzbi.common.core.domain.entity.SysRole; |
|||
import com.lzbi.common.core.domain.entity.SysUser; |
|||
import com.lzbi.system.service.ISysMenuService; |
|||
import com.lzbi.system.service.ISysRoleService; |
|||
|
|||
/** |
|||
* 用户权限处理 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
@Component |
|||
public class SysPermissionService |
|||
{ |
|||
@Autowired |
|||
private ISysRoleService roleService; |
|||
|
|||
@Autowired |
|||
private ISysMenuService menuService; |
|||
|
|||
/** |
|||
* 获取角色数据权限 |
|||
* |
|||
* @param user 用户信息 |
|||
* @return 角色权限信息 |
|||
*/ |
|||
public Set<String> getRolePermission(SysUser user) |
|||
{ |
|||
Set<String> roles = new HashSet<String>(); |
|||
// 管理员拥有所有权限
|
|||
if (user.isAdmin()) |
|||
{ |
|||
roles.add("admin"); |
|||
} |
|||
else |
|||
{ |
|||
roles.addAll(roleService.selectRolePermissionByUserId(user.getUserId())); |
|||
} |
|||
return roles; |
|||
} |
|||
|
|||
/** |
|||
* 获取菜单数据权限 |
|||
* |
|||
* @param user 用户信息 |
|||
* @return 菜单权限信息 |
|||
*/ |
|||
public Set<String> getMenuPermission(SysUser user) |
|||
{ |
|||
Set<String> perms = new HashSet<String>(); |
|||
// 管理员拥有所有权限
|
|||
if (user.isAdmin()) |
|||
{ |
|||
perms.add("*:*:*"); |
|||
} |
|||
else |
|||
{ |
|||
List<SysRole> roles = user.getRoles(); |
|||
if (!CollectionUtils.isEmpty(roles)) |
|||
{ |
|||
// 多角色设置permissions属性,以便数据权限匹配权限
|
|||
for (SysRole role : roles) |
|||
{ |
|||
Set<String> rolePerms = menuService.selectMenuPermsByRoleId(role.getRoleId()); |
|||
role.setPermissions(rolePerms); |
|||
perms.addAll(rolePerms); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
perms.addAll(menuService.selectMenuPermsByUserId(user.getUserId())); |
|||
} |
|||
} |
|||
return perms; |
|||
} |
|||
} |
@ -0,0 +1,115 @@ |
|||
package com.lzbi.framework.web.service; |
|||
|
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.stereotype.Component; |
|||
import com.lzbi.common.constant.CacheConstants; |
|||
import com.lzbi.common.constant.Constants; |
|||
import com.lzbi.common.constant.UserConstants; |
|||
import com.lzbi.common.core.domain.entity.SysUser; |
|||
import com.lzbi.common.core.domain.model.RegisterBody; |
|||
import com.lzbi.common.core.redis.RedisCache; |
|||
import com.lzbi.common.exception.user.CaptchaException; |
|||
import com.lzbi.common.exception.user.CaptchaExpireException; |
|||
import com.lzbi.common.utils.MessageUtils; |
|||
import com.lzbi.common.utils.SecurityUtils; |
|||
import com.lzbi.common.utils.StringUtils; |
|||
import com.lzbi.framework.manager.AsyncManager; |
|||
import com.lzbi.framework.manager.factory.AsyncFactory; |
|||
import com.lzbi.system.service.ISysConfigService; |
|||
import com.lzbi.system.service.ISysUserService; |
|||
|
|||
/** |
|||
* 注册校验方法 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
@Component |
|||
public class SysRegisterService |
|||
{ |
|||
@Autowired |
|||
private ISysUserService userService; |
|||
|
|||
@Autowired |
|||
private ISysConfigService configService; |
|||
|
|||
@Autowired |
|||
private RedisCache redisCache; |
|||
|
|||
/** |
|||
* 注册 |
|||
*/ |
|||
public String register(RegisterBody registerBody) |
|||
{ |
|||
String msg = "", username = registerBody.getUsername(), password = registerBody.getPassword(); |
|||
SysUser sysUser = new SysUser(); |
|||
sysUser.setUserName(username); |
|||
|
|||
// 验证码开关
|
|||
boolean captchaEnabled = configService.selectCaptchaEnabled(); |
|||
if (captchaEnabled) |
|||
{ |
|||
validateCaptcha(username, registerBody.getCode(), registerBody.getUuid()); |
|||
} |
|||
|
|||
if (StringUtils.isEmpty(username)) |
|||
{ |
|||
msg = "用户名不能为空"; |
|||
} |
|||
else if (StringUtils.isEmpty(password)) |
|||
{ |
|||
msg = "用户密码不能为空"; |
|||
} |
|||
else if (username.length() < UserConstants.USERNAME_MIN_LENGTH |
|||
|| username.length() > UserConstants.USERNAME_MAX_LENGTH) |
|||
{ |
|||
msg = "账户长度必须在2到20个字符之间"; |
|||
} |
|||
else if (password.length() < UserConstants.PASSWORD_MIN_LENGTH |
|||
|| password.length() > UserConstants.PASSWORD_MAX_LENGTH) |
|||
{ |
|||
msg = "密码长度必须在5到20个字符之间"; |
|||
} |
|||
else if (!userService.checkUserNameUnique(sysUser)) |
|||
{ |
|||
msg = "保存用户'" + username + "'失败,注册账号已存在"; |
|||
} |
|||
else |
|||
{ |
|||
sysUser.setNickName(username); |
|||
sysUser.setPassword(SecurityUtils.encryptPassword(password)); |
|||
boolean regFlag = userService.registerUser(sysUser); |
|||
if (!regFlag) |
|||
{ |
|||
msg = "注册失败,请联系系统管理人员"; |
|||
} |
|||
else |
|||
{ |
|||
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.REGISTER, MessageUtils.message("user.register.success"))); |
|||
} |
|||
} |
|||
return msg; |
|||
} |
|||
|
|||
/** |
|||
* 校验验证码 |
|||
* |
|||
* @param username 用户名 |
|||
* @param code 验证码 |
|||
* @param uuid 唯一标识 |
|||
* @return 结果 |
|||
*/ |
|||
public void validateCaptcha(String username, String code, String uuid) |
|||
{ |
|||
String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StringUtils.nvl(uuid, ""); |
|||
String captcha = redisCache.getCacheObject(verifyKey); |
|||
redisCache.deleteObject(verifyKey); |
|||
if (captcha == null) |
|||
{ |
|||
throw new CaptchaExpireException(); |
|||
} |
|||
if (!code.equalsIgnoreCase(captcha)) |
|||
{ |
|||
throw new CaptchaException(); |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,231 @@ |
|||
package com.lzbi.framework.web.service; |
|||
|
|||
import java.util.HashMap; |
|||
import java.util.Map; |
|||
import java.util.concurrent.TimeUnit; |
|||
import javax.servlet.http.HttpServletRequest; |
|||
import org.slf4j.Logger; |
|||
import org.slf4j.LoggerFactory; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.beans.factory.annotation.Value; |
|||
import org.springframework.stereotype.Component; |
|||
import com.lzbi.common.constant.CacheConstants; |
|||
import com.lzbi.common.constant.Constants; |
|||
import com.lzbi.common.core.domain.model.LoginUser; |
|||
import com.lzbi.common.core.redis.RedisCache; |
|||
import com.lzbi.common.utils.ServletUtils; |
|||
import com.lzbi.common.utils.StringUtils; |
|||
import com.lzbi.common.utils.ip.AddressUtils; |
|||
import com.lzbi.common.utils.ip.IpUtils; |
|||
import com.lzbi.common.utils.uuid.IdUtils; |
|||
import eu.bitwalker.useragentutils.UserAgent; |
|||
import io.jsonwebtoken.Claims; |
|||
import io.jsonwebtoken.Jwts; |
|||
import io.jsonwebtoken.SignatureAlgorithm; |
|||
|
|||
/** |
|||
* token验证处理 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
@Component |
|||
public class TokenService |
|||
{ |
|||
private static final Logger log = LoggerFactory.getLogger(TokenService.class); |
|||
|
|||
// 令牌自定义标识
|
|||
@Value("${token.header}") |
|||
private String header; |
|||
|
|||
// 令牌秘钥
|
|||
@Value("${token.secret}") |
|||
private String secret; |
|||
|
|||
// 令牌有效期(默认30分钟)
|
|||
@Value("${token.expireTime}") |
|||
private int expireTime; |
|||
|
|||
protected static final long MILLIS_SECOND = 1000; |
|||
|
|||
protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND; |
|||
|
|||
private static final Long MILLIS_MINUTE_TEN = 20 * 60 * 1000L; |
|||
|
|||
@Autowired |
|||
private RedisCache redisCache; |
|||
|
|||
/** |
|||
* 获取用户身份信息 |
|||
* |
|||
* @return 用户信息 |
|||
*/ |
|||
public LoginUser getLoginUser(HttpServletRequest request) |
|||
{ |
|||
// 获取请求携带的令牌
|
|||
String token = getToken(request); |
|||
if (StringUtils.isNotEmpty(token)) |
|||
{ |
|||
try |
|||
{ |
|||
Claims claims = parseToken(token); |
|||
// 解析对应的权限以及用户信息
|
|||
String uuid = (String) claims.get(Constants.LOGIN_USER_KEY); |
|||
String userKey = getTokenKey(uuid); |
|||
LoginUser user = redisCache.getCacheObject(userKey); |
|||
return user; |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
log.error("获取用户信息异常'{}'", e.getMessage()); |
|||
} |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
/** |
|||
* 设置用户身份信息 |
|||
*/ |
|||
public void setLoginUser(LoginUser loginUser) |
|||
{ |
|||
if (StringUtils.isNotNull(loginUser) && StringUtils.isNotEmpty(loginUser.getToken())) |
|||
{ |
|||
refreshToken(loginUser); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 删除用户身份信息 |
|||
*/ |
|||
public void delLoginUser(String token) |
|||
{ |
|||
if (StringUtils.isNotEmpty(token)) |
|||
{ |
|||
String userKey = getTokenKey(token); |
|||
redisCache.deleteObject(userKey); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 创建令牌 |
|||
* |
|||
* @param loginUser 用户信息 |
|||
* @return 令牌 |
|||
*/ |
|||
public String createToken(LoginUser loginUser) |
|||
{ |
|||
String token = IdUtils.fastUUID(); |
|||
loginUser.setToken(token); |
|||
setUserAgent(loginUser); |
|||
refreshToken(loginUser); |
|||
|
|||
Map<String, Object> claims = new HashMap<>(); |
|||
claims.put(Constants.LOGIN_USER_KEY, token); |
|||
return createToken(claims); |
|||
} |
|||
|
|||
/** |
|||
* 验证令牌有效期,相差不足20分钟,自动刷新缓存 |
|||
* |
|||
* @param loginUser |
|||
* @return 令牌 |
|||
*/ |
|||
public void verifyToken(LoginUser loginUser) |
|||
{ |
|||
long expireTime = loginUser.getExpireTime(); |
|||
long currentTime = System.currentTimeMillis(); |
|||
if (expireTime - currentTime <= MILLIS_MINUTE_TEN) |
|||
{ |
|||
refreshToken(loginUser); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 刷新令牌有效期 |
|||
* |
|||
* @param loginUser 登录信息 |
|||
*/ |
|||
public void refreshToken(LoginUser loginUser) |
|||
{ |
|||
loginUser.setLoginTime(System.currentTimeMillis()); |
|||
loginUser.setExpireTime(loginUser.getLoginTime() + expireTime * MILLIS_MINUTE); |
|||
// 根据uuid将loginUser缓存
|
|||
String userKey = getTokenKey(loginUser.getToken()); |
|||
redisCache.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES); |
|||
} |
|||
|
|||
/** |
|||
* 设置用户代理信息 |
|||
* |
|||
* @param loginUser 登录信息 |
|||
*/ |
|||
public void setUserAgent(LoginUser loginUser) |
|||
{ |
|||
UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent")); |
|||
String ip = IpUtils.getIpAddr(); |
|||
loginUser.setIpaddr(ip); |
|||
loginUser.setLoginLocation(AddressUtils.getRealAddressByIP(ip)); |
|||
loginUser.setBrowser(userAgent.getBrowser().getName()); |
|||
loginUser.setOs(userAgent.getOperatingSystem().getName()); |
|||
} |
|||
|
|||
/** |
|||
* 从数据声明生成令牌 |
|||
* |
|||
* @param claims 数据声明 |
|||
* @return 令牌 |
|||
*/ |
|||
private String createToken(Map<String, Object> claims) |
|||
{ |
|||
String token = Jwts.builder() |
|||
.setClaims(claims) |
|||
.signWith(SignatureAlgorithm.HS512, secret).compact(); |
|||
return token; |
|||
} |
|||
|
|||
/** |
|||
* 从令牌中获取数据声明 |
|||
* |
|||
* @param token 令牌 |
|||
* @return 数据声明 |
|||
*/ |
|||
private Claims parseToken(String token) |
|||
{ |
|||
return Jwts.parser() |
|||
.setSigningKey(secret) |
|||
.parseClaimsJws(token) |
|||
.getBody(); |
|||
} |
|||
|
|||
/** |
|||
* 从令牌中获取用户名 |
|||
* |
|||
* @param token 令牌 |
|||
* @return 用户名 |
|||
*/ |
|||
public String getUsernameFromToken(String token) |
|||
{ |
|||
Claims claims = parseToken(token); |
|||
return claims.getSubject(); |
|||
} |
|||
|
|||
/** |
|||
* 获取请求token |
|||
* |
|||
* @param request |
|||
* @return token |
|||
*/ |
|||
private String getToken(HttpServletRequest request) |
|||
{ |
|||
String token = request.getHeader(header); |
|||
if (StringUtils.isNotEmpty(token) && token.startsWith(Constants.TOKEN_PREFIX)) |
|||
{ |
|||
token = token.replace(Constants.TOKEN_PREFIX, ""); |
|||
} |
|||
return token; |
|||
} |
|||
|
|||
private String getTokenKey(String uuid) |
|||
{ |
|||
return CacheConstants.LOGIN_TOKEN_KEY + uuid; |
|||
} |
|||
} |
@ -0,0 +1,66 @@ |
|||
package com.lzbi.framework.web.service; |
|||
|
|||
import org.slf4j.Logger; |
|||
import org.slf4j.LoggerFactory; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.security.core.userdetails.UserDetails; |
|||
import org.springframework.security.core.userdetails.UserDetailsService; |
|||
import org.springframework.security.core.userdetails.UsernameNotFoundException; |
|||
import org.springframework.stereotype.Service; |
|||
import com.lzbi.common.core.domain.entity.SysUser; |
|||
import com.lzbi.common.core.domain.model.LoginUser; |
|||
import com.lzbi.common.enums.UserStatus; |
|||
import com.lzbi.common.exception.ServiceException; |
|||
import com.lzbi.common.utils.MessageUtils; |
|||
import com.lzbi.common.utils.StringUtils; |
|||
import com.lzbi.system.service.ISysUserService; |
|||
|
|||
/** |
|||
* 用户验证处理 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
@Service |
|||
public class UserDetailsServiceImpl implements UserDetailsService |
|||
{ |
|||
private static final Logger log = LoggerFactory.getLogger(UserDetailsServiceImpl.class); |
|||
|
|||
@Autowired |
|||
private ISysUserService userService; |
|||
|
|||
@Autowired |
|||
private SysPasswordService passwordService; |
|||
|
|||
@Autowired |
|||
private SysPermissionService permissionService; |
|||
|
|||
@Override |
|||
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException |
|||
{ |
|||
SysUser user = userService.selectUserByUserName(username); |
|||
if (StringUtils.isNull(user)) |
|||
{ |
|||
log.info("登录用户:{} 不存在.", username); |
|||
throw new ServiceException(MessageUtils.message("user.not.exists")); |
|||
} |
|||
else if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) |
|||
{ |
|||
log.info("登录用户:{} 已被删除.", username); |
|||
throw new ServiceException(MessageUtils.message("user.password.delete")); |
|||
} |
|||
else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) |
|||
{ |
|||
log.info("登录用户:{} 已被停用.", username); |
|||
throw new ServiceException(MessageUtils.message("user.blocked")); |
|||
} |
|||
|
|||
passwordService.validate(user); |
|||
|
|||
return createLoginUser(user); |
|||
} |
|||
|
|||
public UserDetails createLoginUser(SysUser user) |
|||
{ |
|||
return new LoginUser(user.getUserId(), user.getDeptId(), user, permissionService.getMenuPermission(user)); |
|||
} |
|||
} |
@ -0,0 +1,40 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" |
|||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
|||
<parent> |
|||
<artifactId>lzbi</artifactId> |
|||
<groupId>com.lzbi</groupId> |
|||
<version>3.8.6</version> |
|||
</parent> |
|||
<modelVersion>4.0.0</modelVersion> |
|||
|
|||
<artifactId>lzbi-generator</artifactId> |
|||
|
|||
<description> |
|||
generator代码生成 |
|||
</description> |
|||
|
|||
<dependencies> |
|||
|
|||
<!--velocity代码生成使用模板 --> |
|||
<dependency> |
|||
<groupId>org.apache.velocity</groupId> |
|||
<artifactId>velocity-engine-core</artifactId> |
|||
</dependency> |
|||
|
|||
<!-- collections工具类 --> |
|||
<dependency> |
|||
<groupId>commons-collections</groupId> |
|||
<artifactId>commons-collections</artifactId> |
|||
</dependency> |
|||
|
|||
<!-- 通用工具--> |
|||
<dependency> |
|||
<groupId>com.lzbi</groupId> |
|||
<artifactId>lzbi-common</artifactId> |
|||
</dependency> |
|||
|
|||
</dependencies> |
|||
|
|||
</project> |
@ -0,0 +1,73 @@ |
|||
package com.lzbi.generator.config; |
|||
|
|||
import org.springframework.beans.factory.annotation.Value; |
|||
import org.springframework.boot.context.properties.ConfigurationProperties; |
|||
import org.springframework.context.annotation.PropertySource; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
/** |
|||
* 读取代码生成相关配置 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
@Component |
|||
@ConfigurationProperties(prefix = "gen") |
|||
@PropertySource(value = { "classpath:generator.yml" }) |
|||
public class GenConfig |
|||
{ |
|||
/** 作者 */ |
|||
public static String author; |
|||
|
|||
/** 生成包路径 */ |
|||
public static String packageName; |
|||
|
|||
/** 自动去除表前缀,默认是false */ |
|||
public static boolean autoRemovePre; |
|||
|
|||
/** 表前缀(类名不会包含表前缀) */ |
|||
public static String tablePrefix; |
|||
|
|||
public static String getAuthor() |
|||
{ |
|||
return author; |
|||
} |
|||
|
|||
@Value("${author}") |
|||
public void setAuthor(String author) |
|||
{ |
|||
GenConfig.author = author; |
|||
} |
|||
|
|||
public static String getPackageName() |
|||
{ |
|||
return packageName; |
|||
} |
|||
|
|||
@Value("${packageName}") |
|||
public void setPackageName(String packageName) |
|||
{ |
|||
GenConfig.packageName = packageName; |
|||
} |
|||
|
|||
public static boolean getAutoRemovePre() |
|||
{ |
|||
return autoRemovePre; |
|||
} |
|||
|
|||
@Value("${autoRemovePre}") |
|||
public void setAutoRemovePre(boolean autoRemovePre) |
|||
{ |
|||
GenConfig.autoRemovePre = autoRemovePre; |
|||
} |
|||
|
|||
public static String getTablePrefix() |
|||
{ |
|||
return tablePrefix; |
|||
} |
|||
|
|||
@Value("${tablePrefix}") |
|||
public void setTablePrefix(String tablePrefix) |
|||
{ |
|||
GenConfig.tablePrefix = tablePrefix; |
|||
} |
|||
} |
@ -0,0 +1,214 @@ |
|||
package com.lzbi.generator.controller; |
|||
|
|||
import java.io.IOException; |
|||
import java.util.HashMap; |
|||
import java.util.List; |
|||
import java.util.Map; |
|||
import javax.servlet.http.HttpServletResponse; |
|||
import org.apache.commons.io.IOUtils; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.security.access.prepost.PreAuthorize; |
|||
import org.springframework.validation.annotation.Validated; |
|||
import org.springframework.web.bind.annotation.DeleteMapping; |
|||
import org.springframework.web.bind.annotation.GetMapping; |
|||
import org.springframework.web.bind.annotation.PathVariable; |
|||
import org.springframework.web.bind.annotation.PostMapping; |
|||
import org.springframework.web.bind.annotation.PutMapping; |
|||
import org.springframework.web.bind.annotation.RequestBody; |
|||
import org.springframework.web.bind.annotation.RequestMapping; |
|||
import org.springframework.web.bind.annotation.RestController; |
|||
import com.lzbi.common.annotation.Log; |
|||
import com.lzbi.common.core.controller.BaseController; |
|||
import com.lzbi.common.core.domain.AjaxResult; |
|||
import com.lzbi.common.core.page.TableDataInfo; |
|||
import com.lzbi.common.core.text.Convert; |
|||
import com.lzbi.common.enums.BusinessType; |
|||
import com.lzbi.generator.domain.GenTable; |
|||
import com.lzbi.generator.domain.GenTableColumn; |
|||
import com.lzbi.generator.service.IGenTableColumnService; |
|||
import com.lzbi.generator.service.IGenTableService; |
|||
|
|||
/** |
|||
* 代码生成 操作处理 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
@RestController |
|||
@RequestMapping("/tool/gen") |
|||
public class GenController extends BaseController |
|||
{ |
|||
@Autowired |
|||
private IGenTableService genTableService; |
|||
|
|||
@Autowired |
|||
private IGenTableColumnService genTableColumnService; |
|||
|
|||
/** |
|||
* 查询代码生成列表 |
|||
*/ |
|||
@PreAuthorize("@ss.hasPermi('tool:gen:list')") |
|||
@GetMapping("/list") |
|||
public TableDataInfo genList(GenTable genTable) |
|||
{ |
|||
startPage(); |
|||
List<GenTable> list = genTableService.selectGenTableList(genTable); |
|||
return getDataTable(list); |
|||
} |
|||
|
|||
/** |
|||
* 修改代码生成业务 |
|||
*/ |
|||
@PreAuthorize("@ss.hasPermi('tool:gen:query')") |
|||
@GetMapping(value = "/{tableId}") |
|||
public AjaxResult getInfo(@PathVariable Long tableId) |
|||
{ |
|||
GenTable table = genTableService.selectGenTableById(tableId); |
|||
List<GenTable> tables = genTableService.selectGenTableAll(); |
|||
List<GenTableColumn> list = genTableColumnService.selectGenTableColumnListByTableId(tableId); |
|||
Map<String, Object> map = new HashMap<String, Object>(); |
|||
map.put("info", table); |
|||
map.put("rows", list); |
|||
map.put("tables", tables); |
|||
return success(map); |
|||
} |
|||
|
|||
/** |
|||
* 查询数据库列表 |
|||
*/ |
|||
@PreAuthorize("@ss.hasPermi('tool:gen:list')") |
|||
@GetMapping("/db/list") |
|||
public TableDataInfo dataList(GenTable genTable) |
|||
{ |
|||
startPage(); |
|||
List<GenTable> list = genTableService.selectDbTableList(genTable); |
|||
return getDataTable(list); |
|||
} |
|||
|
|||
/** |
|||
* 查询数据表字段列表 |
|||
*/ |
|||
@PreAuthorize("@ss.hasPermi('tool:gen:list')") |
|||
@GetMapping(value = "/column/{tableId}") |
|||
public TableDataInfo columnList(Long tableId) |
|||
{ |
|||
TableDataInfo dataInfo = new TableDataInfo(); |
|||
List<GenTableColumn> list = genTableColumnService.selectGenTableColumnListByTableId(tableId); |
|||
dataInfo.setRows(list); |
|||
dataInfo.setTotal(list.size()); |
|||
return dataInfo; |
|||
} |
|||
|
|||
/** |
|||
* 导入表结构(保存) |
|||
*/ |
|||
@PreAuthorize("@ss.hasPermi('tool:gen:import')") |
|||
@Log(title = "代码生成", businessType = BusinessType.IMPORT) |
|||
@PostMapping("/importTable") |
|||
public AjaxResult importTableSave(String tables) |
|||
{ |
|||
String[] tableNames = Convert.toStrArray(tables); |
|||
// 查询表信息
|
|||
List<GenTable> tableList = genTableService.selectDbTableListByNames(tableNames); |
|||
genTableService.importGenTable(tableList); |
|||
return success(); |
|||
} |
|||
|
|||
/** |
|||
* 修改保存代码生成业务 |
|||
*/ |
|||
@PreAuthorize("@ss.hasPermi('tool:gen:edit')") |
|||
@Log(title = "代码生成", businessType = BusinessType.UPDATE) |
|||
@PutMapping |
|||
public AjaxResult editSave(@Validated @RequestBody GenTable genTable) |
|||
{ |
|||
genTableService.validateEdit(genTable); |
|||
genTableService.updateGenTable(genTable); |
|||
return success(); |
|||
} |
|||
|
|||
/** |
|||
* 删除代码生成 |
|||
*/ |
|||
@PreAuthorize("@ss.hasPermi('tool:gen:remove')") |
|||
@Log(title = "代码生成", businessType = BusinessType.DELETE) |
|||
@DeleteMapping("/{tableIds}") |
|||
public AjaxResult remove(@PathVariable Long[] tableIds) |
|||
{ |
|||
genTableService.deleteGenTableByIds(tableIds); |
|||
return success(); |
|||
} |
|||
|
|||
/** |
|||
* 预览代码 |
|||
*/ |
|||
@PreAuthorize("@ss.hasPermi('tool:gen:preview')") |
|||
@GetMapping("/preview/{tableId}") |
|||
public AjaxResult preview(@PathVariable("tableId") Long tableId) throws IOException |
|||
{ |
|||
Map<String, String> dataMap = genTableService.previewCode(tableId); |
|||
return success(dataMap); |
|||
} |
|||
|
|||
/** |
|||
* 生成代码(下载方式) |
|||
*/ |
|||
@PreAuthorize("@ss.hasPermi('tool:gen:code')") |
|||
@Log(title = "代码生成", businessType = BusinessType.GENCODE) |
|||
@GetMapping("/download/{tableName}") |
|||
public void download(HttpServletResponse response, @PathVariable("tableName") String tableName) throws IOException |
|||
{ |
|||
byte[] data = genTableService.downloadCode(tableName); |
|||
genCode(response, data); |
|||
} |
|||
|
|||
/** |
|||
* 生成代码(自定义路径) |
|||
*/ |
|||
@PreAuthorize("@ss.hasPermi('tool:gen:code')") |
|||
@Log(title = "代码生成", businessType = BusinessType.GENCODE) |
|||
@GetMapping("/genCode/{tableName}") |
|||
public AjaxResult genCode(@PathVariable("tableName") String tableName) |
|||
{ |
|||
genTableService.generatorCode(tableName); |
|||
return success(); |
|||
} |
|||
|
|||
/** |
|||
* 同步数据库 |
|||
*/ |
|||
@PreAuthorize("@ss.hasPermi('tool:gen:edit')") |
|||
@Log(title = "代码生成", businessType = BusinessType.UPDATE) |
|||
@GetMapping("/synchDb/{tableName}") |
|||
public AjaxResult synchDb(@PathVariable("tableName") String tableName) |
|||
{ |
|||
genTableService.synchDb(tableName); |
|||
return success(); |
|||
} |
|||
|
|||
/** |
|||
* 批量生成代码 |
|||
*/ |
|||
@PreAuthorize("@ss.hasPermi('tool:gen:code')") |
|||
@Log(title = "代码生成", businessType = BusinessType.GENCODE) |
|||
@GetMapping("/batchGenCode") |
|||
public void batchGenCode(HttpServletResponse response, String tables) throws IOException |
|||
{ |
|||
String[] tableNames = Convert.toStrArray(tables); |
|||
byte[] data = genTableService.downloadCode(tableNames); |
|||
genCode(response, data); |
|||
} |
|||
|
|||
/** |
|||
* 生成zip文件 |
|||
*/ |
|||
private void genCode(HttpServletResponse response, byte[] data) throws IOException |
|||
{ |
|||
response.reset(); |
|||
response.addHeader("Access-Control-Allow-Origin", "*"); |
|||
response.addHeader("Access-Control-Expose-Headers", "Content-Disposition"); |
|||
response.setHeader("Content-Disposition", "attachment; filename=\"ruoyi.zip\""); |
|||
response.addHeader("Content-Length", "" + data.length); |
|||
response.setContentType("application/octet-stream; charset=UTF-8"); |
|||
IOUtils.write(data, response.getOutputStream()); |
|||
} |
|||
} |
@ -0,0 +1,372 @@ |
|||
package com.lzbi.generator.domain; |
|||
|
|||
import java.util.List; |
|||
import javax.validation.Valid; |
|||
import javax.validation.constraints.NotBlank; |
|||
import org.apache.commons.lang3.ArrayUtils; |
|||
import com.lzbi.common.constant.GenConstants; |
|||
import com.lzbi.common.core.domain.BaseEntity; |
|||
import com.lzbi.common.utils.StringUtils; |
|||
|
|||
/** |
|||
* 业务表 gen_table |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
public class GenTable extends BaseEntity |
|||
{ |
|||
private static final long serialVersionUID = 1L; |
|||
|
|||
/** 编号 */ |
|||
private Long tableId; |
|||
|
|||
/** 表名称 */ |
|||
@NotBlank(message = "表名称不能为空") |
|||
private String tableName; |
|||
|
|||
/** 表描述 */ |
|||
@NotBlank(message = "表描述不能为空") |
|||
private String tableComment; |
|||
|
|||
/** 关联父表的表名 */ |
|||
private String subTableName; |
|||
|
|||
/** 本表关联父表的外键名 */ |
|||
private String subTableFkName; |
|||
|
|||
/** 实体类名称(首字母大写) */ |
|||
@NotBlank(message = "实体类名称不能为空") |
|||
private String className; |
|||
|
|||
/** 使用的模板(crud单表操作 tree树表操作 sub主子表操作) */ |
|||
private String tplCategory; |
|||
|
|||
/** 生成包路径 */ |
|||
@NotBlank(message = "生成包路径不能为空") |
|||
private String packageName; |
|||
|
|||
/** 生成模块名 */ |
|||
@NotBlank(message = "生成模块名不能为空") |
|||
private String moduleName; |
|||
|
|||
/** 生成业务名 */ |
|||
@NotBlank(message = "生成业务名不能为空") |
|||
private String businessName; |
|||
|
|||
/** 生成功能名 */ |
|||
@NotBlank(message = "生成功能名不能为空") |
|||
private String functionName; |
|||
|
|||
/** 生成作者 */ |
|||
@NotBlank(message = "作者不能为空") |
|||
private String functionAuthor; |
|||
|
|||
/** 生成代码方式(0zip压缩包 1自定义路径) */ |
|||
private String genType; |
|||
|
|||
/** 生成路径(不填默认项目路径) */ |
|||
private String genPath; |
|||
|
|||
/** 主键信息 */ |
|||
private GenTableColumn pkColumn; |
|||
|
|||
/** 子表信息 */ |
|||
private GenTable subTable; |
|||
|
|||
/** 表列信息 */ |
|||
@Valid |
|||
private List<GenTableColumn> columns; |
|||
|
|||
/** 其它生成选项 */ |
|||
private String options; |
|||
|
|||
/** 树编码字段 */ |
|||
private String treeCode; |
|||
|
|||
/** 树父编码字段 */ |
|||
private String treeParentCode; |
|||
|
|||
/** 树名称字段 */ |
|||
private String treeName; |
|||
|
|||
/** 上级菜单ID字段 */ |
|||
private String parentMenuId; |
|||
|
|||
/** 上级菜单名称字段 */ |
|||
private String parentMenuName; |
|||
|
|||
public Long getTableId() |
|||
{ |
|||
return tableId; |
|||
} |
|||
|
|||
public void setTableId(Long tableId) |
|||
{ |
|||
this.tableId = tableId; |
|||
} |
|||
|
|||
public String getTableName() |
|||
{ |
|||
return tableName; |
|||
} |
|||
|
|||
public void setTableName(String tableName) |
|||
{ |
|||
this.tableName = tableName; |
|||
} |
|||
|
|||
public String getTableComment() |
|||
{ |
|||
return tableComment; |
|||
} |
|||
|
|||
public void setTableComment(String tableComment) |
|||
{ |
|||
this.tableComment = tableComment; |
|||
} |
|||
|
|||
public String getSubTableName() |
|||
{ |
|||
return subTableName; |
|||
} |
|||
|
|||
public void setSubTableName(String subTableName) |
|||
{ |
|||
this.subTableName = subTableName; |
|||
} |
|||
|
|||
public String getSubTableFkName() |
|||
{ |
|||
return subTableFkName; |
|||
} |
|||
|
|||
public void setSubTableFkName(String subTableFkName) |
|||
{ |
|||
this.subTableFkName = subTableFkName; |
|||
} |
|||
|
|||
public String getClassName() |
|||
{ |
|||
return className; |
|||
} |
|||
|
|||
public void setClassName(String className) |
|||
{ |
|||
this.className = className; |
|||
} |
|||
|
|||
public String getTplCategory() |
|||
{ |
|||
return tplCategory; |
|||
} |
|||
|
|||
public void setTplCategory(String tplCategory) |
|||
{ |
|||
this.tplCategory = tplCategory; |
|||
} |
|||
|
|||
public String getPackageName() |
|||
{ |
|||
return packageName; |
|||
} |
|||
|
|||
public void setPackageName(String packageName) |
|||
{ |
|||
this.packageName = packageName; |
|||
} |
|||
|
|||
public String getModuleName() |
|||
{ |
|||
return moduleName; |
|||
} |
|||
|
|||
public void setModuleName(String moduleName) |
|||
{ |
|||
this.moduleName = moduleName; |
|||
} |
|||
|
|||
public String getBusinessName() |
|||
{ |
|||
return businessName; |
|||
} |
|||
|
|||
public void setBusinessName(String businessName) |
|||
{ |
|||
this.businessName = businessName; |
|||
} |
|||
|
|||
public String getFunctionName() |
|||
{ |
|||
return functionName; |
|||
} |
|||
|
|||
public void setFunctionName(String functionName) |
|||
{ |
|||
this.functionName = functionName; |
|||
} |
|||
|
|||
public String getFunctionAuthor() |
|||
{ |
|||
return functionAuthor; |
|||
} |
|||
|
|||
public void setFunctionAuthor(String functionAuthor) |
|||
{ |
|||
this.functionAuthor = functionAuthor; |
|||
} |
|||
|
|||
public String getGenType() |
|||
{ |
|||
return genType; |
|||
} |
|||
|
|||
public void setGenType(String genType) |
|||
{ |
|||
this.genType = genType; |
|||
} |
|||
|
|||
public String getGenPath() |
|||
{ |
|||
return genPath; |
|||
} |
|||
|
|||
public void setGenPath(String genPath) |
|||
{ |
|||
this.genPath = genPath; |
|||
} |
|||
|
|||
public GenTableColumn getPkColumn() |
|||
{ |
|||
return pkColumn; |
|||
} |
|||
|
|||
public void setPkColumn(GenTableColumn pkColumn) |
|||
{ |
|||
this.pkColumn = pkColumn; |
|||
} |
|||
|
|||
public GenTable getSubTable() |
|||
{ |
|||
return subTable; |
|||
} |
|||
|
|||
public void setSubTable(GenTable subTable) |
|||
{ |
|||
this.subTable = subTable; |
|||
} |
|||
|
|||
public List<GenTableColumn> getColumns() |
|||
{ |
|||
return columns; |
|||
} |
|||
|
|||
public void setColumns(List<GenTableColumn> columns) |
|||
{ |
|||
this.columns = columns; |
|||
} |
|||
|
|||
public String getOptions() |
|||
{ |
|||
return options; |
|||
} |
|||
|
|||
public void setOptions(String options) |
|||
{ |
|||
this.options = options; |
|||
} |
|||
|
|||
public String getTreeCode() |
|||
{ |
|||
return treeCode; |
|||
} |
|||
|
|||
public void setTreeCode(String treeCode) |
|||
{ |
|||
this.treeCode = treeCode; |
|||
} |
|||
|
|||
public String getTreeParentCode() |
|||
{ |
|||
return treeParentCode; |
|||
} |
|||
|
|||
public void setTreeParentCode(String treeParentCode) |
|||
{ |
|||
this.treeParentCode = treeParentCode; |
|||
} |
|||
|
|||
public String getTreeName() |
|||
{ |
|||
return treeName; |
|||
} |
|||
|
|||
public void setTreeName(String treeName) |
|||
{ |
|||
this.treeName = treeName; |
|||
} |
|||
|
|||
public String getParentMenuId() |
|||
{ |
|||
return parentMenuId; |
|||
} |
|||
|
|||
public void setParentMenuId(String parentMenuId) |
|||
{ |
|||
this.parentMenuId = parentMenuId; |
|||
} |
|||
|
|||
public String getParentMenuName() |
|||
{ |
|||
return parentMenuName; |
|||
} |
|||
|
|||
public void setParentMenuName(String parentMenuName) |
|||
{ |
|||
this.parentMenuName = parentMenuName; |
|||
} |
|||
|
|||
public boolean isSub() |
|||
{ |
|||
return isSub(this.tplCategory); |
|||
} |
|||
|
|||
public static boolean isSub(String tplCategory) |
|||
{ |
|||
return tplCategory != null && StringUtils.equals(GenConstants.TPL_SUB, tplCategory); |
|||
} |
|||
|
|||
public boolean isTree() |
|||
{ |
|||
return isTree(this.tplCategory); |
|||
} |
|||
|
|||
public static boolean isTree(String tplCategory) |
|||
{ |
|||
return tplCategory != null && StringUtils.equals(GenConstants.TPL_TREE, tplCategory); |
|||
} |
|||
|
|||
public boolean isCrud() |
|||
{ |
|||
return isCrud(this.tplCategory); |
|||
} |
|||
|
|||
public static boolean isCrud(String tplCategory) |
|||
{ |
|||
return tplCategory != null && StringUtils.equals(GenConstants.TPL_CRUD, tplCategory); |
|||
} |
|||
|
|||
public boolean isSuperColumn(String javaField) |
|||
{ |
|||
return isSuperColumn(this.tplCategory, javaField); |
|||
} |
|||
|
|||
public static boolean isSuperColumn(String tplCategory, String javaField) |
|||
{ |
|||
if (isTree(tplCategory)) |
|||
{ |
|||
return StringUtils.equalsAnyIgnoreCase(javaField, |
|||
ArrayUtils.addAll(GenConstants.TREE_ENTITY, GenConstants.BASE_ENTITY)); |
|||
} |
|||
return StringUtils.equalsAnyIgnoreCase(javaField, GenConstants.BASE_ENTITY); |
|||
} |
|||
} |
@ -0,0 +1,373 @@ |
|||
package com.lzbi.generator.domain; |
|||
|
|||
import javax.validation.constraints.NotBlank; |
|||
import com.lzbi.common.core.domain.BaseEntity; |
|||
import com.lzbi.common.utils.StringUtils; |
|||
|
|||
/** |
|||
* 代码生成业务字段表 gen_table_column |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
public class GenTableColumn extends BaseEntity |
|||
{ |
|||
private static final long serialVersionUID = 1L; |
|||
|
|||
/** 编号 */ |
|||
private Long columnId; |
|||
|
|||
/** 归属表编号 */ |
|||
private Long tableId; |
|||
|
|||
/** 列名称 */ |
|||
private String columnName; |
|||
|
|||
/** 列描述 */ |
|||
private String columnComment; |
|||
|
|||
/** 列类型 */ |
|||
private String columnType; |
|||
|
|||
/** JAVA类型 */ |
|||
private String javaType; |
|||
|
|||
/** JAVA字段名 */ |
|||
@NotBlank(message = "Java属性不能为空") |
|||
private String javaField; |
|||
|
|||
/** 是否主键(1是) */ |
|||
private String isPk; |
|||
|
|||
/** 是否自增(1是) */ |
|||
private String isIncrement; |
|||
|
|||
/** 是否必填(1是) */ |
|||
private String isRequired; |
|||
|
|||
/** 是否为插入字段(1是) */ |
|||
private String isInsert; |
|||
|
|||
/** 是否编辑字段(1是) */ |
|||
private String isEdit; |
|||
|
|||
/** 是否列表字段(1是) */ |
|||
private String isList; |
|||
|
|||
/** 是否查询字段(1是) */ |
|||
private String isQuery; |
|||
|
|||
/** 查询方式(EQ等于、NE不等于、GT大于、LT小于、LIKE模糊、BETWEEN范围) */ |
|||
private String queryType; |
|||
|
|||
/** 显示类型(input文本框、textarea文本域、select下拉框、checkbox复选框、radio单选框、datetime日期控件、image图片上传控件、upload文件上传控件、editor富文本控件) */ |
|||
private String htmlType; |
|||
|
|||
/** 字典类型 */ |
|||
private String dictType; |
|||
|
|||
/** 排序 */ |
|||
private Integer sort; |
|||
|
|||
public void setColumnId(Long columnId) |
|||
{ |
|||
this.columnId = columnId; |
|||
} |
|||
|
|||
public Long getColumnId() |
|||
{ |
|||
return columnId; |
|||
} |
|||
|
|||
public void setTableId(Long tableId) |
|||
{ |
|||
this.tableId = tableId; |
|||
} |
|||
|
|||
public Long getTableId() |
|||
{ |
|||
return tableId; |
|||
} |
|||
|
|||
public void setColumnName(String columnName) |
|||
{ |
|||
this.columnName = columnName; |
|||
} |
|||
|
|||
public String getColumnName() |
|||
{ |
|||
return columnName; |
|||
} |
|||
|
|||
public void setColumnComment(String columnComment) |
|||
{ |
|||
this.columnComment = columnComment; |
|||
} |
|||
|
|||
public String getColumnComment() |
|||
{ |
|||
return columnComment; |
|||
} |
|||
|
|||
public void setColumnType(String columnType) |
|||
{ |
|||
this.columnType = columnType; |
|||
} |
|||
|
|||
public String getColumnType() |
|||
{ |
|||
return columnType; |
|||
} |
|||
|
|||
public void setJavaType(String javaType) |
|||
{ |
|||
this.javaType = javaType; |
|||
} |
|||
|
|||
public String getJavaType() |
|||
{ |
|||
return javaType; |
|||
} |
|||
|
|||
public void setJavaField(String javaField) |
|||
{ |
|||
this.javaField = javaField; |
|||
} |
|||
|
|||
public String getJavaField() |
|||
{ |
|||
return javaField; |
|||
} |
|||
|
|||
public String getCapJavaField() |
|||
{ |
|||
return StringUtils.capitalize(javaField); |
|||
} |
|||
|
|||
public void setIsPk(String isPk) |
|||
{ |
|||
this.isPk = isPk; |
|||
} |
|||
|
|||
public String getIsPk() |
|||
{ |
|||
return isPk; |
|||
} |
|||
|
|||
public boolean isPk() |
|||
{ |
|||
return isPk(this.isPk); |
|||
} |
|||
|
|||
public boolean isPk(String isPk) |
|||
{ |
|||
return isPk != null && StringUtils.equals("1", isPk); |
|||
} |
|||
|
|||
public String getIsIncrement() |
|||
{ |
|||
return isIncrement; |
|||
} |
|||
|
|||
public void setIsIncrement(String isIncrement) |
|||
{ |
|||
this.isIncrement = isIncrement; |
|||
} |
|||
|
|||
public boolean isIncrement() |
|||
{ |
|||
return isIncrement(this.isIncrement); |
|||
} |
|||
|
|||
public boolean isIncrement(String isIncrement) |
|||
{ |
|||
return isIncrement != null && StringUtils.equals("1", isIncrement); |
|||
} |
|||
|
|||
public void setIsRequired(String isRequired) |
|||
{ |
|||
this.isRequired = isRequired; |
|||
} |
|||
|
|||
public String getIsRequired() |
|||
{ |
|||
return isRequired; |
|||
} |
|||
|
|||
public boolean isRequired() |
|||
{ |
|||
return isRequired(this.isRequired); |
|||
} |
|||
|
|||
public boolean isRequired(String isRequired) |
|||
{ |
|||
return isRequired != null && StringUtils.equals("1", isRequired); |
|||
} |
|||
|
|||
public void setIsInsert(String isInsert) |
|||
{ |
|||
this.isInsert = isInsert; |
|||
} |
|||
|
|||
public String getIsInsert() |
|||
{ |
|||
return isInsert; |
|||
} |
|||
|
|||
public boolean isInsert() |
|||
{ |
|||
return isInsert(this.isInsert); |
|||
} |
|||
|
|||
public boolean isInsert(String isInsert) |
|||
{ |
|||
return isInsert != null && StringUtils.equals("1", isInsert); |
|||
} |
|||
|
|||
public void setIsEdit(String isEdit) |
|||
{ |
|||
this.isEdit = isEdit; |
|||
} |
|||
|
|||
public String getIsEdit() |
|||
{ |
|||
return isEdit; |
|||
} |
|||
|
|||
public boolean isEdit() |
|||
{ |
|||
return isInsert(this.isEdit); |
|||
} |
|||
|
|||
public boolean isEdit(String isEdit) |
|||
{ |
|||
return isEdit != null && StringUtils.equals("1", isEdit); |
|||
} |
|||
|
|||
public void setIsList(String isList) |
|||
{ |
|||
this.isList = isList; |
|||
} |
|||
|
|||
public String getIsList() |
|||
{ |
|||
return isList; |
|||
} |
|||
|
|||
public boolean isList() |
|||
{ |
|||
return isList(this.isList); |
|||
} |
|||
|
|||
public boolean isList(String isList) |
|||
{ |
|||
return isList != null && StringUtils.equals("1", isList); |
|||
} |
|||
|
|||
public void setIsQuery(String isQuery) |
|||
{ |
|||
this.isQuery = isQuery; |
|||
} |
|||
|
|||
public String getIsQuery() |
|||
{ |
|||
return isQuery; |
|||
} |
|||
|
|||
public boolean isQuery() |
|||
{ |
|||
return isQuery(this.isQuery); |
|||
} |
|||
|
|||
public boolean isQuery(String isQuery) |
|||
{ |
|||
return isQuery != null && StringUtils.equals("1", isQuery); |
|||
} |
|||
|
|||
public void setQueryType(String queryType) |
|||
{ |
|||
this.queryType = queryType; |
|||
} |
|||
|
|||
public String getQueryType() |
|||
{ |
|||
return queryType; |
|||
} |
|||
|
|||
public String getHtmlType() |
|||
{ |
|||
return htmlType; |
|||
} |
|||
|
|||
public void setHtmlType(String htmlType) |
|||
{ |
|||
this.htmlType = htmlType; |
|||
} |
|||
|
|||
public void setDictType(String dictType) |
|||
{ |
|||
this.dictType = dictType; |
|||
} |
|||
|
|||
public String getDictType() |
|||
{ |
|||
return dictType; |
|||
} |
|||
|
|||
public void setSort(Integer sort) |
|||
{ |
|||
this.sort = sort; |
|||
} |
|||
|
|||
public Integer getSort() |
|||
{ |
|||
return sort; |
|||
} |
|||
|
|||
public boolean isSuperColumn() |
|||
{ |
|||
return isSuperColumn(this.javaField); |
|||
} |
|||
|
|||
public static boolean isSuperColumn(String javaField) |
|||
{ |
|||
return StringUtils.equalsAnyIgnoreCase(javaField, |
|||
// BaseEntity
|
|||
"createBy", "createTime", "updateBy", "updateTime", "remark", |
|||
// TreeEntity
|
|||
"parentName", "parentId", "orderNum", "ancestors"); |
|||
} |
|||
|
|||
public boolean isUsableColumn() |
|||
{ |
|||
return isUsableColumn(javaField); |
|||
} |
|||
|
|||
public static boolean isUsableColumn(String javaField) |
|||
{ |
|||
// isSuperColumn()中的名单用于避免生成多余Domain属性,若某些属性在生成页面时需要用到不能忽略,则放在此处白名单
|
|||
return StringUtils.equalsAnyIgnoreCase(javaField, "parentId", "orderNum", "remark"); |
|||
} |
|||
|
|||
public String readConverterExp() |
|||
{ |
|||
String remarks = StringUtils.substringBetween(this.columnComment, "(", ")"); |
|||
StringBuffer sb = new StringBuffer(); |
|||
if (StringUtils.isNotEmpty(remarks)) |
|||
{ |
|||
for (String value : remarks.split(" ")) |
|||
{ |
|||
if (StringUtils.isNotEmpty(value)) |
|||
{ |
|||
Object startStr = value.subSequence(0, 1); |
|||
String endStr = value.substring(1); |
|||
sb.append("").append(startStr).append("=").append(endStr).append(","); |
|||
} |
|||
} |
|||
return sb.deleteCharAt(sb.length() - 1).toString(); |
|||
} |
|||
else |
|||
{ |
|||
return this.columnComment; |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,60 @@ |
|||
package com.lzbi.generator.mapper; |
|||
|
|||
import java.util.List; |
|||
import com.lzbi.generator.domain.GenTableColumn; |
|||
|
|||
/** |
|||
* 业务字段 数据层 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
public interface GenTableColumnMapper |
|||
{ |
|||
/** |
|||
* 根据表名称查询列信息 |
|||
* |
|||
* @param tableName 表名称 |
|||
* @return 列信息 |
|||
*/ |
|||
public List<GenTableColumn> selectDbTableColumnsByName(String tableName); |
|||
|
|||
/** |
|||
* 查询业务字段列表 |
|||
* |
|||
* @param tableId 业务字段编号 |
|||
* @return 业务字段集合 |
|||
*/ |
|||
public List<GenTableColumn> selectGenTableColumnListByTableId(Long tableId); |
|||
|
|||
/** |
|||
* 新增业务字段 |
|||
* |
|||
* @param genTableColumn 业务字段信息 |
|||
* @return 结果 |
|||
*/ |
|||
public int insertGenTableColumn(GenTableColumn genTableColumn); |
|||
|
|||
/** |
|||
* 修改业务字段 |
|||
* |
|||
* @param genTableColumn 业务字段信息 |
|||
* @return 结果 |
|||
*/ |
|||
public int updateGenTableColumn(GenTableColumn genTableColumn); |
|||
|
|||
/** |
|||
* 删除业务字段 |
|||
* |
|||
* @param genTableColumns 列数据 |
|||
* @return 结果 |
|||
*/ |
|||
public int deleteGenTableColumns(List<GenTableColumn> genTableColumns); |
|||
|
|||
/** |
|||
* 批量删除业务字段 |
|||
* |
|||
* @param ids 需要删除的数据ID |
|||
* @return 结果 |
|||
*/ |
|||
public int deleteGenTableColumnByIds(Long[] ids); |
|||
} |
@ -0,0 +1,83 @@ |
|||
package com.lzbi.generator.mapper; |
|||
|
|||
import java.util.List; |
|||
import com.lzbi.generator.domain.GenTable; |
|||
|
|||
/** |
|||
* 业务 数据层 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
public interface GenTableMapper |
|||
{ |
|||
/** |
|||
* 查询业务列表 |
|||
* |
|||
* @param genTable 业务信息 |
|||
* @return 业务集合 |
|||
*/ |
|||
public List<GenTable> selectGenTableList(GenTable genTable); |
|||
|
|||
/** |
|||
* 查询据库列表 |
|||
* |
|||
* @param genTable 业务信息 |
|||
* @return 数据库表集合 |
|||
*/ |
|||
public List<GenTable> selectDbTableList(GenTable genTable); |
|||
|
|||
/** |
|||
* 查询据库列表 |
|||
* |
|||
* @param tableNames 表名称组 |
|||
* @return 数据库表集合 |
|||
*/ |
|||
public List<GenTable> selectDbTableListByNames(String[] tableNames); |
|||
|
|||
/** |
|||
* 查询所有表信息 |
|||
* |
|||
* @return 表信息集合 |
|||
*/ |
|||
public List<GenTable> selectGenTableAll(); |
|||
|
|||
/** |
|||
* 查询表ID业务信息 |
|||
* |
|||
* @param id 业务ID |
|||
* @return 业务信息 |
|||
*/ |
|||
public GenTable selectGenTableById(Long id); |
|||
|
|||
/** |
|||
* 查询表名称业务信息 |
|||
* |
|||
* @param tableName 表名称 |
|||
* @return 业务信息 |
|||
*/ |
|||
public GenTable selectGenTableByName(String tableName); |
|||
|
|||
/** |
|||
* 新增业务 |
|||
* |
|||
* @param genTable 业务信息 |
|||
* @return 结果 |
|||
*/ |
|||
public int insertGenTable(GenTable genTable); |
|||
|
|||
/** |
|||
* 修改业务 |
|||
* |
|||
* @param genTable 业务信息 |
|||
* @return 结果 |
|||
*/ |
|||
public int updateGenTable(GenTable genTable); |
|||
|
|||
/** |
|||
* 批量删除业务 |
|||
* |
|||
* @param ids 需要删除的数据ID |
|||
* @return 结果 |
|||
*/ |
|||
public int deleteGenTableByIds(Long[] ids); |
|||
} |
@ -0,0 +1,68 @@ |
|||
package com.lzbi.generator.service; |
|||
|
|||
import java.util.List; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.stereotype.Service; |
|||
import com.lzbi.common.core.text.Convert; |
|||
import com.lzbi.generator.domain.GenTableColumn; |
|||
import com.lzbi.generator.mapper.GenTableColumnMapper; |
|||
|
|||
/** |
|||
* 业务字段 服务层实现 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
@Service |
|||
public class GenTableColumnServiceImpl implements IGenTableColumnService |
|||
{ |
|||
@Autowired |
|||
private GenTableColumnMapper genTableColumnMapper; |
|||
|
|||
/** |
|||
* 查询业务字段列表 |
|||
* |
|||
* @param tableId 业务字段编号 |
|||
* @return 业务字段集合 |
|||
*/ |
|||
@Override |
|||
public List<GenTableColumn> selectGenTableColumnListByTableId(Long tableId) |
|||
{ |
|||
return genTableColumnMapper.selectGenTableColumnListByTableId(tableId); |
|||
} |
|||
|
|||
/** |
|||
* 新增业务字段 |
|||
* |
|||
* @param genTableColumn 业务字段信息 |
|||
* @return 结果 |
|||
*/ |
|||
@Override |
|||
public int insertGenTableColumn(GenTableColumn genTableColumn) |
|||
{ |
|||
return genTableColumnMapper.insertGenTableColumn(genTableColumn); |
|||
} |
|||
|
|||
/** |
|||
* 修改业务字段 |
|||
* |
|||
* @param genTableColumn 业务字段信息 |
|||
* @return 结果 |
|||
*/ |
|||
@Override |
|||
public int updateGenTableColumn(GenTableColumn genTableColumn) |
|||
{ |
|||
return genTableColumnMapper.updateGenTableColumn(genTableColumn); |
|||
} |
|||
|
|||
/** |
|||
* 删除业务字段对象 |
|||
* |
|||
* @param ids 需要删除的数据ID |
|||
* @return 结果 |
|||
*/ |
|||
@Override |
|||
public int deleteGenTableColumnByIds(String ids) |
|||
{ |
|||
return genTableColumnMapper.deleteGenTableColumnByIds(Convert.toLongArray(ids)); |
|||
} |
|||
} |
@ -0,0 +1,521 @@ |
|||
package com.lzbi.generator.service; |
|||
|
|||
import java.io.ByteArrayOutputStream; |
|||
import java.io.File; |
|||
import java.io.IOException; |
|||
import java.io.StringWriter; |
|||
import java.util.LinkedHashMap; |
|||
import java.util.List; |
|||
import java.util.Map; |
|||
import java.util.function.Function; |
|||
import java.util.stream.Collectors; |
|||
import java.util.zip.ZipEntry; |
|||
import java.util.zip.ZipOutputStream; |
|||
import org.apache.commons.io.FileUtils; |
|||
import org.apache.commons.io.IOUtils; |
|||
import org.apache.velocity.Template; |
|||
import org.apache.velocity.VelocityContext; |
|||
import org.apache.velocity.app.Velocity; |
|||
import org.slf4j.Logger; |
|||
import org.slf4j.LoggerFactory; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.stereotype.Service; |
|||
import org.springframework.transaction.annotation.Transactional; |
|||
import com.alibaba.fastjson2.JSON; |
|||
import com.alibaba.fastjson2.JSONObject; |
|||
import com.lzbi.common.constant.Constants; |
|||
import com.lzbi.common.constant.GenConstants; |
|||
import com.lzbi.common.core.text.CharsetKit; |
|||
import com.lzbi.common.exception.ServiceException; |
|||
import com.lzbi.common.utils.SecurityUtils; |
|||
import com.lzbi.common.utils.StringUtils; |
|||
import com.lzbi.generator.domain.GenTable; |
|||
import com.lzbi.generator.domain.GenTableColumn; |
|||
import com.lzbi.generator.mapper.GenTableColumnMapper; |
|||
import com.lzbi.generator.mapper.GenTableMapper; |
|||
import com.lzbi.generator.util.GenUtils; |
|||
import com.lzbi.generator.util.VelocityInitializer; |
|||
import com.lzbi.generator.util.VelocityUtils; |
|||
|
|||
/** |
|||
* 业务 服务层实现 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
@Service |
|||
public class GenTableServiceImpl implements IGenTableService |
|||
{ |
|||
private static final Logger log = LoggerFactory.getLogger(GenTableServiceImpl.class); |
|||
|
|||
@Autowired |
|||
private GenTableMapper genTableMapper; |
|||
|
|||
@Autowired |
|||
private GenTableColumnMapper genTableColumnMapper; |
|||
|
|||
/** |
|||
* 查询业务信息 |
|||
* |
|||
* @param id 业务ID |
|||
* @return 业务信息 |
|||
*/ |
|||
@Override |
|||
public GenTable selectGenTableById(Long id) |
|||
{ |
|||
GenTable genTable = genTableMapper.selectGenTableById(id); |
|||
setTableFromOptions(genTable); |
|||
return genTable; |
|||
} |
|||
|
|||
/** |
|||
* 查询业务列表 |
|||
* |
|||
* @param genTable 业务信息 |
|||
* @return 业务集合 |
|||
*/ |
|||
@Override |
|||
public List<GenTable> selectGenTableList(GenTable genTable) |
|||
{ |
|||
return genTableMapper.selectGenTableList(genTable); |
|||
} |
|||
|
|||
/** |
|||
* 查询据库列表 |
|||
* |
|||
* @param genTable 业务信息 |
|||
* @return 数据库表集合 |
|||
*/ |
|||
@Override |
|||
public List<GenTable> selectDbTableList(GenTable genTable) |
|||
{ |
|||
return genTableMapper.selectDbTableList(genTable); |
|||
} |
|||
|
|||
/** |
|||
* 查询据库列表 |
|||
* |
|||
* @param tableNames 表名称组 |
|||
* @return 数据库表集合 |
|||
*/ |
|||
@Override |
|||
public List<GenTable> selectDbTableListByNames(String[] tableNames) |
|||
{ |
|||
return genTableMapper.selectDbTableListByNames(tableNames); |
|||
} |
|||
|
|||
/** |
|||
* 查询所有表信息 |
|||
* |
|||
* @return 表信息集合 |
|||
*/ |
|||
@Override |
|||
public List<GenTable> selectGenTableAll() |
|||
{ |
|||
return genTableMapper.selectGenTableAll(); |
|||
} |
|||
|
|||
/** |
|||
* 修改业务 |
|||
* |
|||
* @param genTable 业务信息 |
|||
* @return 结果 |
|||
*/ |
|||
@Override |
|||
@Transactional |
|||
public void updateGenTable(GenTable genTable) |
|||
{ |
|||
String options = JSON.toJSONString(genTable.getParams()); |
|||
genTable.setOptions(options); |
|||
int row = genTableMapper.updateGenTable(genTable); |
|||
if (row > 0) |
|||
{ |
|||
for (GenTableColumn cenTableColumn : genTable.getColumns()) |
|||
{ |
|||
genTableColumnMapper.updateGenTableColumn(cenTableColumn); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 删除业务对象 |
|||
* |
|||
* @param tableIds 需要删除的数据ID |
|||
* @return 结果 |
|||
*/ |
|||
@Override |
|||
@Transactional |
|||
public void deleteGenTableByIds(Long[] tableIds) |
|||
{ |
|||
genTableMapper.deleteGenTableByIds(tableIds); |
|||
genTableColumnMapper.deleteGenTableColumnByIds(tableIds); |
|||
} |
|||
|
|||
/** |
|||
* 导入表结构 |
|||
* |
|||
* @param tableList 导入表列表 |
|||
*/ |
|||
@Override |
|||
@Transactional |
|||
public void importGenTable(List<GenTable> tableList) |
|||
{ |
|||
String operName = SecurityUtils.getUsername(); |
|||
try |
|||
{ |
|||
for (GenTable table : tableList) |
|||
{ |
|||
String tableName = table.getTableName(); |
|||
GenUtils.initTable(table, operName); |
|||
int row = genTableMapper.insertGenTable(table); |
|||
if (row > 0) |
|||
{ |
|||
// 保存列信息
|
|||
List<GenTableColumn> genTableColumns = genTableColumnMapper.selectDbTableColumnsByName(tableName); |
|||
for (GenTableColumn column : genTableColumns) |
|||
{ |
|||
GenUtils.initColumnField(column, table); |
|||
genTableColumnMapper.insertGenTableColumn(column); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
throw new ServiceException("导入失败:" + e.getMessage()); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 预览代码 |
|||
* |
|||
* @param tableId 表编号 |
|||
* @return 预览数据列表 |
|||
*/ |
|||
@Override |
|||
public Map<String, String> previewCode(Long tableId) |
|||
{ |
|||
Map<String, String> dataMap = new LinkedHashMap<>(); |
|||
// 查询表信息
|
|||
GenTable table = genTableMapper.selectGenTableById(tableId); |
|||
// 设置主子表信息
|
|||
setSubTable(table); |
|||
// 设置主键列信息
|
|||
setPkColumn(table); |
|||
VelocityInitializer.initVelocity(); |
|||
|
|||
VelocityContext context = VelocityUtils.prepareContext(table); |
|||
|
|||
// 获取模板列表
|
|||
List<String> templates = VelocityUtils.getTemplateList(table.getTplCategory()); |
|||
for (String template : templates) |
|||
{ |
|||
// 渲染模板
|
|||
StringWriter sw = new StringWriter(); |
|||
Template tpl = Velocity.getTemplate(template, Constants.UTF8); |
|||
tpl.merge(context, sw); |
|||
dataMap.put(template, sw.toString()); |
|||
} |
|||
return dataMap; |
|||
} |
|||
|
|||
/** |
|||
* 生成代码(下载方式) |
|||
* |
|||
* @param tableName 表名称 |
|||
* @return 数据 |
|||
*/ |
|||
@Override |
|||
public byte[] downloadCode(String tableName) |
|||
{ |
|||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); |
|||
ZipOutputStream zip = new ZipOutputStream(outputStream); |
|||
generatorCode(tableName, zip); |
|||
IOUtils.closeQuietly(zip); |
|||
return outputStream.toByteArray(); |
|||
} |
|||
|
|||
/** |
|||
* 生成代码(自定义路径) |
|||
* |
|||
* @param tableName 表名称 |
|||
*/ |
|||
@Override |
|||
public void generatorCode(String tableName) |
|||
{ |
|||
// 查询表信息
|
|||
GenTable table = genTableMapper.selectGenTableByName(tableName); |
|||
// 设置主子表信息
|
|||
setSubTable(table); |
|||
// 设置主键列信息
|
|||
setPkColumn(table); |
|||
|
|||
VelocityInitializer.initVelocity(); |
|||
|
|||
VelocityContext context = VelocityUtils.prepareContext(table); |
|||
|
|||
// 获取模板列表
|
|||
List<String> templates = VelocityUtils.getTemplateList(table.getTplCategory()); |
|||
for (String template : templates) |
|||
{ |
|||
if (!StringUtils.containsAny(template, "sql.vm", "api.js.vm", "index.vue.vm", "index-tree.vue.vm")) |
|||
{ |
|||
// 渲染模板
|
|||
StringWriter sw = new StringWriter(); |
|||
Template tpl = Velocity.getTemplate(template, Constants.UTF8); |
|||
tpl.merge(context, sw); |
|||
try |
|||
{ |
|||
String path = getGenPath(table, template); |
|||
FileUtils.writeStringToFile(new File(path), sw.toString(), CharsetKit.UTF_8); |
|||
} |
|||
catch (IOException e) |
|||
{ |
|||
throw new ServiceException("渲染模板失败,表名:" + table.getTableName()); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 同步数据库 |
|||
* |
|||
* @param tableName 表名称 |
|||
*/ |
|||
@Override |
|||
@Transactional |
|||
public void synchDb(String tableName) |
|||
{ |
|||
GenTable table = genTableMapper.selectGenTableByName(tableName); |
|||
List<GenTableColumn> tableColumns = table.getColumns(); |
|||
Map<String, GenTableColumn> tableColumnMap = tableColumns.stream().collect(Collectors.toMap(GenTableColumn::getColumnName, Function.identity())); |
|||
|
|||
List<GenTableColumn> dbTableColumns = genTableColumnMapper.selectDbTableColumnsByName(tableName); |
|||
if (StringUtils.isEmpty(dbTableColumns)) |
|||
{ |
|||
throw new ServiceException("同步数据失败,原表结构不存在"); |
|||
} |
|||
List<String> dbTableColumnNames = dbTableColumns.stream().map(GenTableColumn::getColumnName).collect(Collectors.toList()); |
|||
|
|||
dbTableColumns.forEach(column -> { |
|||
GenUtils.initColumnField(column, table); |
|||
if (tableColumnMap.containsKey(column.getColumnName())) |
|||
{ |
|||
GenTableColumn prevColumn = tableColumnMap.get(column.getColumnName()); |
|||
column.setColumnId(prevColumn.getColumnId()); |
|||
if (column.isList()) |
|||
{ |
|||
// 如果是列表,继续保留查询方式/字典类型选项
|
|||
column.setDictType(prevColumn.getDictType()); |
|||
column.setQueryType(prevColumn.getQueryType()); |
|||
} |
|||
if (StringUtils.isNotEmpty(prevColumn.getIsRequired()) && !column.isPk() |
|||
&& (column.isInsert() || column.isEdit()) |
|||
&& ((column.isUsableColumn()) || (!column.isSuperColumn()))) |
|||
{ |
|||
// 如果是(新增/修改&非主键/非忽略及父属性),继续保留必填/显示类型选项
|
|||
column.setIsRequired(prevColumn.getIsRequired()); |
|||
column.setHtmlType(prevColumn.getHtmlType()); |
|||
} |
|||
genTableColumnMapper.updateGenTableColumn(column); |
|||
} |
|||
else |
|||
{ |
|||
genTableColumnMapper.insertGenTableColumn(column); |
|||
} |
|||
}); |
|||
|
|||
List<GenTableColumn> delColumns = tableColumns.stream().filter(column -> !dbTableColumnNames.contains(column.getColumnName())).collect(Collectors.toList()); |
|||
if (StringUtils.isNotEmpty(delColumns)) |
|||
{ |
|||
genTableColumnMapper.deleteGenTableColumns(delColumns); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 批量生成代码(下载方式) |
|||
* |
|||
* @param tableNames 表数组 |
|||
* @return 数据 |
|||
*/ |
|||
@Override |
|||
public byte[] downloadCode(String[] tableNames) |
|||
{ |
|||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); |
|||
ZipOutputStream zip = new ZipOutputStream(outputStream); |
|||
for (String tableName : tableNames) |
|||
{ |
|||
generatorCode(tableName, zip); |
|||
} |
|||
IOUtils.closeQuietly(zip); |
|||
return outputStream.toByteArray(); |
|||
} |
|||
|
|||
/** |
|||
* 查询表信息并生成代码 |
|||
*/ |
|||
private void generatorCode(String tableName, ZipOutputStream zip) |
|||
{ |
|||
// 查询表信息
|
|||
GenTable table = genTableMapper.selectGenTableByName(tableName); |
|||
// 设置主子表信息
|
|||
setSubTable(table); |
|||
// 设置主键列信息
|
|||
setPkColumn(table); |
|||
|
|||
VelocityInitializer.initVelocity(); |
|||
|
|||
VelocityContext context = VelocityUtils.prepareContext(table); |
|||
|
|||
// 获取模板列表
|
|||
List<String> templates = VelocityUtils.getTemplateList(table.getTplCategory()); |
|||
for (String template : templates) |
|||
{ |
|||
// 渲染模板
|
|||
StringWriter sw = new StringWriter(); |
|||
Template tpl = Velocity.getTemplate(template, Constants.UTF8); |
|||
tpl.merge(context, sw); |
|||
try |
|||
{ |
|||
// 添加到zip
|
|||
zip.putNextEntry(new ZipEntry(VelocityUtils.getFileName(template, table))); |
|||
IOUtils.write(sw.toString(), zip, Constants.UTF8); |
|||
IOUtils.closeQuietly(sw); |
|||
zip.flush(); |
|||
zip.closeEntry(); |
|||
} |
|||
catch (IOException e) |
|||
{ |
|||
log.error("渲染模板失败,表名:" + table.getTableName(), e); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 修改保存参数校验 |
|||
* |
|||
* @param genTable 业务信息 |
|||
*/ |
|||
@Override |
|||
public void validateEdit(GenTable genTable) |
|||
{ |
|||
if (GenConstants.TPL_TREE.equals(genTable.getTplCategory())) |
|||
{ |
|||
String options = JSON.toJSONString(genTable.getParams()); |
|||
JSONObject paramsObj = JSON.parseObject(options); |
|||
if (StringUtils.isEmpty(paramsObj.getString(GenConstants.TREE_CODE))) |
|||
{ |
|||
throw new ServiceException("树编码字段不能为空"); |
|||
} |
|||
else if (StringUtils.isEmpty(paramsObj.getString(GenConstants.TREE_PARENT_CODE))) |
|||
{ |
|||
throw new ServiceException("树父编码字段不能为空"); |
|||
} |
|||
else if (StringUtils.isEmpty(paramsObj.getString(GenConstants.TREE_NAME))) |
|||
{ |
|||
throw new ServiceException("树名称字段不能为空"); |
|||
} |
|||
else if (GenConstants.TPL_SUB.equals(genTable.getTplCategory())) |
|||
{ |
|||
if (StringUtils.isEmpty(genTable.getSubTableName())) |
|||
{ |
|||
throw new ServiceException("关联子表的表名不能为空"); |
|||
} |
|||
else if (StringUtils.isEmpty(genTable.getSubTableFkName())) |
|||
{ |
|||
throw new ServiceException("子表关联的外键名不能为空"); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 设置主键列信息 |
|||
* |
|||
* @param table 业务表信息 |
|||
*/ |
|||
public void setPkColumn(GenTable table) |
|||
{ |
|||
for (GenTableColumn column : table.getColumns()) |
|||
{ |
|||
if (column.isPk()) |
|||
{ |
|||
table.setPkColumn(column); |
|||
break; |
|||
} |
|||
} |
|||
if (StringUtils.isNull(table.getPkColumn())) |
|||
{ |
|||
table.setPkColumn(table.getColumns().get(0)); |
|||
} |
|||
if (GenConstants.TPL_SUB.equals(table.getTplCategory())) |
|||
{ |
|||
for (GenTableColumn column : table.getSubTable().getColumns()) |
|||
{ |
|||
if (column.isPk()) |
|||
{ |
|||
table.getSubTable().setPkColumn(column); |
|||
break; |
|||
} |
|||
} |
|||
if (StringUtils.isNull(table.getSubTable().getPkColumn())) |
|||
{ |
|||
table.getSubTable().setPkColumn(table.getSubTable().getColumns().get(0)); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 设置主子表信息 |
|||
* |
|||
* @param table 业务表信息 |
|||
*/ |
|||
public void setSubTable(GenTable table) |
|||
{ |
|||
String subTableName = table.getSubTableName(); |
|||
if (StringUtils.isNotEmpty(subTableName)) |
|||
{ |
|||
table.setSubTable(genTableMapper.selectGenTableByName(subTableName)); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 设置代码生成其他选项值 |
|||
* |
|||
* @param genTable 设置后的生成对象 |
|||
*/ |
|||
public void setTableFromOptions(GenTable genTable) |
|||
{ |
|||
JSONObject paramsObj = JSON.parseObject(genTable.getOptions()); |
|||
if (StringUtils.isNotNull(paramsObj)) |
|||
{ |
|||
String treeCode = paramsObj.getString(GenConstants.TREE_CODE); |
|||
String treeParentCode = paramsObj.getString(GenConstants.TREE_PARENT_CODE); |
|||
String treeName = paramsObj.getString(GenConstants.TREE_NAME); |
|||
String parentMenuId = paramsObj.getString(GenConstants.PARENT_MENU_ID); |
|||
String parentMenuName = paramsObj.getString(GenConstants.PARENT_MENU_NAME); |
|||
|
|||
genTable.setTreeCode(treeCode); |
|||
genTable.setTreeParentCode(treeParentCode); |
|||
genTable.setTreeName(treeName); |
|||
genTable.setParentMenuId(parentMenuId); |
|||
genTable.setParentMenuName(parentMenuName); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 获取代码生成地址 |
|||
* |
|||
* @param table 业务表信息 |
|||
* @param template 模板文件路径 |
|||
* @return 生成地址 |
|||
*/ |
|||
public static String getGenPath(GenTable table, String template) |
|||
{ |
|||
String genPath = table.getGenPath(); |
|||
if (StringUtils.equals(genPath, "/")) |
|||
{ |
|||
return System.getProperty("user.dir") + File.separator + "src" + File.separator + VelocityUtils.getFileName(template, table); |
|||
} |
|||
return genPath + File.separator + VelocityUtils.getFileName(template, table); |
|||
} |
|||
} |
@ -0,0 +1,44 @@ |
|||
package com.lzbi.generator.service; |
|||
|
|||
import java.util.List; |
|||
import com.lzbi.generator.domain.GenTableColumn; |
|||
|
|||
/** |
|||
* 业务字段 服务层 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
public interface IGenTableColumnService |
|||
{ |
|||
/** |
|||
* 查询业务字段列表 |
|||
* |
|||
* @param tableId 业务字段编号 |
|||
* @return 业务字段集合 |
|||
*/ |
|||
public List<GenTableColumn> selectGenTableColumnListByTableId(Long tableId); |
|||
|
|||
/** |
|||
* 新增业务字段 |
|||
* |
|||
* @param genTableColumn 业务字段信息 |
|||
* @return 结果 |
|||
*/ |
|||
public int insertGenTableColumn(GenTableColumn genTableColumn); |
|||
|
|||
/** |
|||
* 修改业务字段 |
|||
* |
|||
* @param genTableColumn 业务字段信息 |
|||
* @return 结果 |
|||
*/ |
|||
public int updateGenTableColumn(GenTableColumn genTableColumn); |
|||
|
|||
/** |
|||
* 删除业务字段信息 |
|||
* |
|||
* @param ids 需要删除的数据ID |
|||
* @return 结果 |
|||
*/ |
|||
public int deleteGenTableColumnByIds(String ids); |
|||
} |
@ -0,0 +1,121 @@ |
|||
package com.lzbi.generator.service; |
|||
|
|||
import java.util.List; |
|||
import java.util.Map; |
|||
import com.lzbi.generator.domain.GenTable; |
|||
|
|||
/** |
|||
* 业务 服务层 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
public interface IGenTableService |
|||
{ |
|||
/** |
|||
* 查询业务列表 |
|||
* |
|||
* @param genTable 业务信息 |
|||
* @return 业务集合 |
|||
*/ |
|||
public List<GenTable> selectGenTableList(GenTable genTable); |
|||
|
|||
/** |
|||
* 查询据库列表 |
|||
* |
|||
* @param genTable 业务信息 |
|||
* @return 数据库表集合 |
|||
*/ |
|||
public List<GenTable> selectDbTableList(GenTable genTable); |
|||
|
|||
/** |
|||
* 查询据库列表 |
|||
* |
|||
* @param tableNames 表名称组 |
|||
* @return 数据库表集合 |
|||
*/ |
|||
public List<GenTable> selectDbTableListByNames(String[] tableNames); |
|||
|
|||
/** |
|||
* 查询所有表信息 |
|||
* |
|||
* @return 表信息集合 |
|||
*/ |
|||
public List<GenTable> selectGenTableAll(); |
|||
|
|||
/** |
|||
* 查询业务信息 |
|||
* |
|||
* @param id 业务ID |
|||
* @return 业务信息 |
|||
*/ |
|||
public GenTable selectGenTableById(Long id); |
|||
|
|||
/** |
|||
* 修改业务 |
|||
* |
|||
* @param genTable 业务信息 |
|||
* @return 结果 |
|||
*/ |
|||
public void updateGenTable(GenTable genTable); |
|||
|
|||
/** |
|||
* 删除业务信息 |
|||
* |
|||
* @param tableIds 需要删除的表数据ID |
|||
* @return 结果 |
|||
*/ |
|||
public void deleteGenTableByIds(Long[] tableIds); |
|||
|
|||
/** |
|||
* 导入表结构 |
|||
* |
|||
* @param tableList 导入表列表 |
|||
*/ |
|||
public void importGenTable(List<GenTable> tableList); |
|||
|
|||
/** |
|||
* 预览代码 |
|||
* |
|||
* @param tableId 表编号 |
|||
* @return 预览数据列表 |
|||
*/ |
|||
public Map<String, String> previewCode(Long tableId); |
|||
|
|||
/** |
|||
* 生成代码(下载方式) |
|||
* |
|||
* @param tableName 表名称 |
|||
* @return 数据 |
|||
*/ |
|||
public byte[] downloadCode(String tableName); |
|||
|
|||
/** |
|||
* 生成代码(自定义路径) |
|||
* |
|||
* @param tableName 表名称 |
|||
* @return 数据 |
|||
*/ |
|||
public void generatorCode(String tableName); |
|||
|
|||
/** |
|||
* 同步数据库 |
|||
* |
|||
* @param tableName 表名称 |
|||
*/ |
|||
public void synchDb(String tableName); |
|||
|
|||
/** |
|||
* 批量生成代码(下载方式) |
|||
* |
|||
* @param tableNames 表数组 |
|||
* @return 数据 |
|||
*/ |
|||
public byte[] downloadCode(String[] tableNames); |
|||
|
|||
/** |
|||
* 修改保存参数校验 |
|||
* |
|||
* @param genTable 业务信息 |
|||
*/ |
|||
public void validateEdit(GenTable genTable); |
|||
} |
@ -0,0 +1,257 @@ |
|||
package com.lzbi.generator.util; |
|||
|
|||
import java.util.Arrays; |
|||
import org.apache.commons.lang3.RegExUtils; |
|||
import com.lzbi.common.constant.GenConstants; |
|||
import com.lzbi.common.utils.StringUtils; |
|||
import com.lzbi.generator.config.GenConfig; |
|||
import com.lzbi.generator.domain.GenTable; |
|||
import com.lzbi.generator.domain.GenTableColumn; |
|||
|
|||
/** |
|||
* 代码生成器 工具类 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
public class GenUtils |
|||
{ |
|||
/** |
|||
* 初始化表信息 |
|||
*/ |
|||
public static void initTable(GenTable genTable, String operName) |
|||
{ |
|||
genTable.setClassName(convertClassName(genTable.getTableName())); |
|||
genTable.setPackageName(GenConfig.getPackageName()); |
|||
genTable.setModuleName(getModuleName(GenConfig.getPackageName())); |
|||
genTable.setBusinessName(getBusinessName(genTable.getTableName())); |
|||
genTable.setFunctionName(replaceText(genTable.getTableComment())); |
|||
genTable.setFunctionAuthor(GenConfig.getAuthor()); |
|||
genTable.setCreateBy(operName); |
|||
} |
|||
|
|||
/** |
|||
* 初始化列属性字段 |
|||
*/ |
|||
public static void initColumnField(GenTableColumn column, GenTable table) |
|||
{ |
|||
String dataType = getDbType(column.getColumnType()); |
|||
String columnName = column.getColumnName(); |
|||
column.setTableId(table.getTableId()); |
|||
column.setCreateBy(table.getCreateBy()); |
|||
// 设置java字段名
|
|||
column.setJavaField(StringUtils.toCamelCase(columnName)); |
|||
// 设置默认类型
|
|||
column.setJavaType(GenConstants.TYPE_STRING); |
|||
column.setQueryType(GenConstants.QUERY_EQ); |
|||
|
|||
if (arraysContains(GenConstants.COLUMNTYPE_STR, dataType) || arraysContains(GenConstants.COLUMNTYPE_TEXT, dataType)) |
|||
{ |
|||
// 字符串长度超过500设置为文本域
|
|||
Integer columnLength = getColumnLength(column.getColumnType()); |
|||
String htmlType = columnLength >= 500 || arraysContains(GenConstants.COLUMNTYPE_TEXT, dataType) ? GenConstants.HTML_TEXTAREA : GenConstants.HTML_INPUT; |
|||
column.setHtmlType(htmlType); |
|||
} |
|||
else if (arraysContains(GenConstants.COLUMNTYPE_TIME, dataType)) |
|||
{ |
|||
column.setJavaType(GenConstants.TYPE_DATE); |
|||
column.setHtmlType(GenConstants.HTML_DATETIME); |
|||
} |
|||
else if (arraysContains(GenConstants.COLUMNTYPE_NUMBER, dataType)) |
|||
{ |
|||
column.setHtmlType(GenConstants.HTML_INPUT); |
|||
|
|||
// 如果是浮点型 统一用BigDecimal
|
|||
String[] str = StringUtils.split(StringUtils.substringBetween(column.getColumnType(), "(", ")"), ","); |
|||
if (str != null && str.length == 2 && Integer.parseInt(str[1]) > 0) |
|||
{ |
|||
column.setJavaType(GenConstants.TYPE_BIGDECIMAL); |
|||
} |
|||
// 如果是整形
|
|||
else if (str != null && str.length == 1 && Integer.parseInt(str[0]) <= 10) |
|||
{ |
|||
column.setJavaType(GenConstants.TYPE_INTEGER); |
|||
} |
|||
// 长整形
|
|||
else |
|||
{ |
|||
column.setJavaType(GenConstants.TYPE_LONG); |
|||
} |
|||
} |
|||
|
|||
// 插入字段(默认所有字段都需要插入)
|
|||
column.setIsInsert(GenConstants.REQUIRE); |
|||
|
|||
// 编辑字段
|
|||
if (!arraysContains(GenConstants.COLUMNNAME_NOT_EDIT, columnName) && !column.isPk()) |
|||
{ |
|||
column.setIsEdit(GenConstants.REQUIRE); |
|||
} |
|||
// 列表字段
|
|||
if (!arraysContains(GenConstants.COLUMNNAME_NOT_LIST, columnName) && !column.isPk()) |
|||
{ |
|||
column.setIsList(GenConstants.REQUIRE); |
|||
} |
|||
// 查询字段
|
|||
if (!arraysContains(GenConstants.COLUMNNAME_NOT_QUERY, columnName) && !column.isPk()) |
|||
{ |
|||
column.setIsQuery(GenConstants.REQUIRE); |
|||
} |
|||
|
|||
// 查询字段类型
|
|||
if (StringUtils.endsWithIgnoreCase(columnName, "name")) |
|||
{ |
|||
column.setQueryType(GenConstants.QUERY_LIKE); |
|||
} |
|||
// 状态字段设置单选框
|
|||
if (StringUtils.endsWithIgnoreCase(columnName, "status")) |
|||
{ |
|||
column.setHtmlType(GenConstants.HTML_RADIO); |
|||
} |
|||
// 类型&性别字段设置下拉框
|
|||
else if (StringUtils.endsWithIgnoreCase(columnName, "type") |
|||
|| StringUtils.endsWithIgnoreCase(columnName, "sex")) |
|||
{ |
|||
column.setHtmlType(GenConstants.HTML_SELECT); |
|||
} |
|||
// 图片字段设置图片上传控件
|
|||
else if (StringUtils.endsWithIgnoreCase(columnName, "image")) |
|||
{ |
|||
column.setHtmlType(GenConstants.HTML_IMAGE_UPLOAD); |
|||
} |
|||
// 文件字段设置文件上传控件
|
|||
else if (StringUtils.endsWithIgnoreCase(columnName, "file")) |
|||
{ |
|||
column.setHtmlType(GenConstants.HTML_FILE_UPLOAD); |
|||
} |
|||
// 内容字段设置富文本控件
|
|||
else if (StringUtils.endsWithIgnoreCase(columnName, "content")) |
|||
{ |
|||
column.setHtmlType(GenConstants.HTML_EDITOR); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 校验数组是否包含指定值 |
|||
* |
|||
* @param arr 数组 |
|||
* @param targetValue 值 |
|||
* @return 是否包含 |
|||
*/ |
|||
public static boolean arraysContains(String[] arr, String targetValue) |
|||
{ |
|||
return Arrays.asList(arr).contains(targetValue); |
|||
} |
|||
|
|||
/** |
|||
* 获取模块名 |
|||
* |
|||
* @param packageName 包名 |
|||
* @return 模块名 |
|||
*/ |
|||
public static String getModuleName(String packageName) |
|||
{ |
|||
int lastIndex = packageName.lastIndexOf("."); |
|||
int nameLength = packageName.length(); |
|||
return StringUtils.substring(packageName, lastIndex + 1, nameLength); |
|||
} |
|||
|
|||
/** |
|||
* 获取业务名 |
|||
* |
|||
* @param tableName 表名 |
|||
* @return 业务名 |
|||
*/ |
|||
public static String getBusinessName(String tableName) |
|||
{ |
|||
int lastIndex = tableName.lastIndexOf("_"); |
|||
int nameLength = tableName.length(); |
|||
return StringUtils.substring(tableName, lastIndex + 1, nameLength); |
|||
} |
|||
|
|||
/** |
|||
* 表名转换成Java类名 |
|||
* |
|||
* @param tableName 表名称 |
|||
* @return 类名 |
|||
*/ |
|||
public static String convertClassName(String tableName) |
|||
{ |
|||
boolean autoRemovePre = GenConfig.getAutoRemovePre(); |
|||
String tablePrefix = GenConfig.getTablePrefix(); |
|||
if (autoRemovePre && StringUtils.isNotEmpty(tablePrefix)) |
|||
{ |
|||
String[] searchList = StringUtils.split(tablePrefix, ","); |
|||
tableName = replaceFirst(tableName, searchList); |
|||
} |
|||
return StringUtils.convertToCamelCase(tableName); |
|||
} |
|||
|
|||
/** |
|||
* 批量替换前缀 |
|||
* |
|||
* @param replacementm 替换值 |
|||
* @param searchList 替换列表 |
|||
* @return |
|||
*/ |
|||
public static String replaceFirst(String replacementm, String[] searchList) |
|||
{ |
|||
String text = replacementm; |
|||
for (String searchString : searchList) |
|||
{ |
|||
if (replacementm.startsWith(searchString)) |
|||
{ |
|||
text = replacementm.replaceFirst(searchString, ""); |
|||
break; |
|||
} |
|||
} |
|||
return text; |
|||
} |
|||
|
|||
/** |
|||
* 关键字替换 |
|||
* |
|||
* @param text 需要被替换的名字 |
|||
* @return 替换后的名字 |
|||
*/ |
|||
public static String replaceText(String text) |
|||
{ |
|||
return RegExUtils.replaceAll(text, "(?:表|若依)", ""); |
|||
} |
|||
|
|||
/** |
|||
* 获取数据库类型字段 |
|||
* |
|||
* @param columnType 列类型 |
|||
* @return 截取后的列类型 |
|||
*/ |
|||
public static String getDbType(String columnType) |
|||
{ |
|||
if (StringUtils.indexOf(columnType, "(") > 0) |
|||
{ |
|||
return StringUtils.substringBefore(columnType, "("); |
|||
} |
|||
else |
|||
{ |
|||
return columnType; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 获取字段长度 |
|||
* |
|||
* @param columnType 列类型 |
|||
* @return 截取后的列类型 |
|||
*/ |
|||
public static Integer getColumnLength(String columnType) |
|||
{ |
|||
if (StringUtils.indexOf(columnType, "(") > 0) |
|||
{ |
|||
String length = StringUtils.substringBetween(columnType, "(", ")"); |
|||
return Integer.valueOf(length); |
|||
} |
|||
else |
|||
{ |
|||
return 0; |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,34 @@ |
|||
package com.lzbi.generator.util; |
|||
|
|||
import java.util.Properties; |
|||
import org.apache.velocity.app.Velocity; |
|||
import com.lzbi.common.constant.Constants; |
|||
|
|||
/** |
|||
* VelocityEngine工厂 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
public class VelocityInitializer |
|||
{ |
|||
/** |
|||
* 初始化vm方法 |
|||
*/ |
|||
public static void initVelocity() |
|||
{ |
|||
Properties p = new Properties(); |
|||
try |
|||
{ |
|||
// 加载classpath目录下的vm文件
|
|||
p.setProperty("resource.loader.file.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader"); |
|||
// 定义字符集
|
|||
p.setProperty(Velocity.INPUT_ENCODING, Constants.UTF8); |
|||
// 初始化Velocity引擎,指定配置Properties
|
|||
Velocity.init(p); |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
throw new RuntimeException(e); |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,402 @@ |
|||
package com.lzbi.generator.util; |
|||
|
|||
import java.util.ArrayList; |
|||
import java.util.HashSet; |
|||
import java.util.List; |
|||
import java.util.Set; |
|||
import org.apache.velocity.VelocityContext; |
|||
import com.alibaba.fastjson2.JSON; |
|||
import com.alibaba.fastjson2.JSONObject; |
|||
import com.lzbi.common.constant.GenConstants; |
|||
import com.lzbi.common.utils.DateUtils; |
|||
import com.lzbi.common.utils.StringUtils; |
|||
import com.lzbi.generator.domain.GenTable; |
|||
import com.lzbi.generator.domain.GenTableColumn; |
|||
|
|||
/** |
|||
* 模板处理工具类 |
|||
* |
|||
* @author ruoyi |
|||
*/ |
|||
public class VelocityUtils |
|||
{ |
|||
/** 项目空间路径 */ |
|||
private static final String PROJECT_PATH = "main/java"; |
|||
|
|||
/** mybatis空间路径 */ |
|||
private static final String MYBATIS_PATH = "main/resources/mapper"; |
|||
|
|||
/** 默认上级菜单,系统工具 */ |
|||
private static final String DEFAULT_PARENT_MENU_ID = "3"; |
|||
|
|||
/** |
|||
* 设置模板变量信息 |
|||
* |
|||
* @return 模板列表 |
|||
*/ |
|||
public static VelocityContext prepareContext(GenTable genTable) |
|||
{ |
|||
String moduleName = genTable.getModuleName(); |
|||
String businessName = genTable.getBusinessName(); |
|||
String packageName = genTable.getPackageName(); |
|||
String tplCategory = genTable.getTplCategory(); |
|||
String functionName = genTable.getFunctionName(); |
|||
|
|||
VelocityContext velocityContext = new VelocityContext(); |
|||
velocityContext.put("tplCategory", genTable.getTplCategory()); |
|||
velocityContext.put("tableName", genTable.getTableName()); |
|||
velocityContext.put("functionName", StringUtils.isNotEmpty(functionName) ? functionName : "【请填写功能名称】"); |
|||
velocityContext.put("ClassName", genTable.getClassName()); |
|||
velocityContext.put("className", StringUtils.uncapitalize(genTable.getClassName())); |
|||
velocityContext.put("moduleName", genTable.getModuleName()); |
|||
velocityContext.put("BusinessName", StringUtils.capitalize(genTable.getBusinessName())); |
|||
velocityContext.put("businessName", genTable.getBusinessName()); |
|||
velocityContext.put("basePackage", getPackagePrefix(packageName)); |
|||
velocityContext.put("packageName", packageName); |
|||
velocityContext.put("author", genTable.getFunctionAuthor()); |
|||
velocityContext.put("datetime", DateUtils.getDate()); |
|||
velocityContext.put("pkColumn", genTable.getPkColumn()); |
|||
velocityContext.put("importList", getImportList(genTable)); |
|||
velocityContext.put("permissionPrefix", getPermissionPrefix(moduleName, businessName)); |
|||
velocityContext.put("columns", genTable.getColumns()); |
|||
velocityContext.put("table", genTable); |
|||
velocityContext.put("dicts", getDicts(genTable)); |
|||
setMenuVelocityContext(velocityContext, genTable); |
|||
if (GenConstants.TPL_TREE.equals(tplCategory)) |
|||
{ |
|||
setTreeVelocityContext(velocityContext, genTable); |
|||
} |
|||
if (GenConstants.TPL_SUB.equals(tplCategory)) |
|||
{ |
|||
setSubVelocityContext(velocityContext, genTable); |
|||
} |
|||
return velocityContext; |
|||
} |
|||
|
|||
public static void setMenuVelocityContext(VelocityContext context, GenTable genTable) |
|||
{ |
|||
String options = genTable.getOptions(); |
|||
JSONObject paramsObj = JSON.parseObject(options); |
|||
String parentMenuId = getParentMenuId(paramsObj); |
|||
context.put("parentMenuId", parentMenuId); |
|||
} |
|||
|
|||
public static void setTreeVelocityContext(VelocityContext context, GenTable genTable) |
|||
{ |
|||
String options = genTable.getOptions(); |
|||
JSONObject paramsObj = JSON.parseObject(options); |
|||
String treeCode = getTreecode(paramsObj); |
|||
String treeParentCode = getTreeParentCode(paramsObj); |
|||
String treeName = getTreeName(paramsObj); |
|||
|
|||
context.put("treeCode", treeCode); |
|||
context.put("treeParentCode", treeParentCode); |
|||
context.put("treeName", treeName); |
|||
context.put("expandColumn", getExpandColumn(genTable)); |
|||
if (paramsObj.containsKey(GenConstants.TREE_PARENT_CODE)) |
|||
{ |
|||
context.put("tree_parent_code", paramsObj.getString(GenConstants.TREE_PARENT_CODE)); |
|||
} |
|||
if (paramsObj.containsKey(GenConstants.TREE_NAME)) |
|||
{ |
|||
context.put("tree_name", paramsObj.getString(GenConstants.TREE_NAME)); |
|||
} |
|||
} |
|||
|
|||
public static void setSubVelocityContext(VelocityContext context, GenTable genTable) |
|||
{ |
|||
GenTable subTable = genTable.getSubTable(); |
|||
String subTableName = genTable.getSubTableName(); |
|||
String subTableFkName = genTable.getSubTableFkName(); |
|||
String subClassName = genTable.getSubTable().getClassName(); |
|||
String subTableFkClassName = StringUtils.convertToCamelCase(subTableFkName); |
|||
|
|||
context.put("subTable", subTable); |
|||
context.put("subTableName", subTableName); |
|||
context.put("subTableFkName", subTableFkName); |
|||
context.put("subTableFkClassName", subTableFkClassName); |
|||
context.put("subTableFkclassName", StringUtils.uncapitalize(subTableFkClassName)); |
|||
context.put("subClassName", subClassName); |
|||
context.put("subclassName", StringUtils.uncapitalize(subClassName)); |
|||
context.put("subImportList", getImportList(genTable.getSubTable())); |
|||
} |
|||
|
|||
/** |
|||
* 获取模板信息 |
|||
* |
|||
* @return 模板列表 |
|||
*/ |
|||
public static List<String> getTemplateList(String tplCategory) |
|||
{ |
|||
List<String> templates = new ArrayList<String>(); |
|||
templates.add("vm/java/domain.java.vm"); |
|||
templates.add("vm/java/mapper.java.vm"); |
|||
templates.add("vm/java/service.java.vm"); |
|||
templates.add("vm/java/serviceImpl.java.vm"); |
|||
templates.add("vm/java/controller.java.vm"); |
|||
templates.add("vm/xml/mapper.xml.vm"); |
|||
templates.add("vm/sql/sql.vm"); |
|||
templates.add("vm/js/api.js.vm"); |
|||
if (GenConstants.TPL_CRUD.equals(tplCategory)) |
|||
{ |
|||
templates.add("vm/vue/index.vue.vm"); |
|||
} |
|||
else if (GenConstants.TPL_TREE.equals(tplCategory)) |
|||
{ |
|||
templates.add("vm/vue/index-tree.vue.vm"); |
|||
} |
|||
else if (GenConstants.TPL_SUB.equals(tplCategory)) |
|||
{ |
|||
templates.add("vm/vue/index.vue.vm"); |
|||
templates.add("vm/java/sub-domain.java.vm"); |
|||
} |
|||
return templates; |
|||
} |
|||
|
|||
/** |
|||
* 获取文件名 |
|||
*/ |
|||
public static String getFileName(String template, GenTable genTable) |
|||
{ |
|||
// 文件名称
|
|||
String fileName = ""; |
|||
// 包路径
|
|||
String packageName = genTable.getPackageName(); |
|||
// 模块名
|
|||
String moduleName = genTable.getModuleName(); |
|||
// 大写类名
|
|||
String className = genTable.getClassName(); |
|||
// 业务名称
|
|||
String businessName = genTable.getBusinessName(); |
|||
|
|||
String javaPath = PROJECT_PATH + "/" + StringUtils.replace(packageName, ".", "/"); |
|||
String mybatisPath = MYBATIS_PATH + "/" + moduleName; |
|||
String vuePath = "vue"; |
|||
|
|||
if (template.contains("domain.java.vm")) |
|||
{ |
|||
fileName = StringUtils.format("{}/domain/{}.java", javaPath, className); |
|||
} |
|||
if (template.contains("sub-domain.java.vm") && StringUtils.equals(GenConstants.TPL_SUB, genTable.getTplCategory())) |
|||
{ |
|||
fileName = StringUtils.format("{}/domain/{}.java", javaPath, genTable.getSubTable().getClassName()); |
|||
} |
|||
else if (template.contains("mapper.java.vm")) |
|||
{ |
|||
fileName = StringUtils.format("{}/mapper/{}Mapper.java", javaPath, className); |
|||
} |
|||
else if (template.contains("service.java.vm")) |
|||
{ |
|||
fileName = StringUtils.format("{}/service/I{}Service.java", javaPath, className); |
|||
} |
|||
else if (template.contains("serviceImpl.java.vm")) |
|||
{ |
|||
fileName = StringUtils.format("{}/service/impl/{}ServiceImpl.java", javaPath, className); |
|||
} |
|||
else if (template.contains("controller.java.vm")) |
|||
{ |
|||
fileName = StringUtils.format("{}/controller/{}Controller.java", javaPath, className); |
|||
} |
|||
else if (template.contains("mapper.xml.vm")) |
|||
{ |
|||
fileName = StringUtils.format("{}/{}Mapper.xml", mybatisPath, className); |
|||
} |
|||
else if (template.contains("sql.vm")) |
|||
{ |
|||
fileName = businessName + "Menu.sql"; |
|||
} |
|||
else if (template.contains("api.js.vm")) |
|||
{ |
|||
fileName = StringUtils.format("{}/api/{}/{}.js", vuePath, moduleName, businessName); |
|||
} |
|||
else if (template.contains("index.vue.vm")) |
|||
{ |
|||
fileName = StringUtils.format("{}/views/{}/{}/index.vue", vuePath, moduleName, businessName); |
|||
} |
|||
else if (template.contains("index-tree.vue.vm")) |
|||
{ |
|||
fileName = StringUtils.format("{}/views/{}/{}/index.vue", vuePath, moduleName, businessName); |
|||
} |
|||
return fileName; |
|||
} |
|||
|
|||
/** |
|||
* 获取包前缀 |
|||
* |
|||
* @param packageName 包名称 |
|||
* @return 包前缀名称 |
|||
*/ |
|||
public static String getPackagePrefix(String packageName) |
|||
{ |
|||
int lastIndex = packageName.lastIndexOf("."); |
|||
return StringUtils.substring(packageName, 0, lastIndex); |
|||
} |
|||
|
|||
/** |
|||
* 根据列类型获取导入包 |
|||
* |
|||
* @param genTable 业务表对象 |
|||
* @return 返回需要导入的包列表 |
|||
*/ |
|||
public static HashSet<String> getImportList(GenTable genTable) |
|||
{ |
|||
List<GenTableColumn> columns = genTable.getColumns(); |
|||
GenTable subGenTable = genTable.getSubTable(); |
|||
HashSet<String> importList = new HashSet<String>(); |
|||
if (StringUtils.isNotNull(subGenTable)) |
|||
{ |
|||
importList.add("java.util.List"); |
|||
} |
|||
for (GenTableColumn column : columns) |
|||
{ |
|||
if (!column.isSuperColumn() && GenConstants.TYPE_DATE.equals(column.getJavaType())) |
|||
{ |
|||
importList.add("java.util.Date"); |
|||
importList.add("com.fasterxml.jackson.annotation.JsonFormat"); |
|||
} |
|||
else if (!column.isSuperColumn() && GenConstants.TYPE_BIGDECIMAL.equals(column.getJavaType())) |
|||
{ |
|||
importList.add("java.math.BigDecimal"); |
|||
} |
|||
} |
|||
return importList; |
|||
} |
|||
|
|||
/** |
|||
* 根据列类型获取字典组 |
|||
* |
|||
* @param genTable 业务表对象 |
|||
* @return 返回字典组 |
|||
*/ |
|||
public static String getDicts(GenTable genTable) |
|||
{ |
|||
List<GenTableColumn> columns = genTable.getColumns(); |
|||
Set<String> dicts = new HashSet<String>(); |
|||
addDicts(dicts, columns); |
|||
if (StringUtils.isNotNull(genTable.getSubTable())) |
|||
{ |
|||
List<GenTableColumn> subColumns = genTable.getSubTable().getColumns(); |
|||
addDicts(dicts, subColumns); |
|||
} |
|||
return StringUtils.join(dicts, ", "); |
|||
} |
|||
|
|||
/** |
|||
* 添加字典列表 |
|||
* |
|||
* @param dicts 字典列表 |
|||
* @param columns 列集合 |
|||
*/ |
|||
public static void addDicts(Set<String> dicts, List<GenTableColumn> columns) |
|||
{ |
|||
for (GenTableColumn column : columns) |
|||
{ |
|||
if (!column.isSuperColumn() && StringUtils.isNotEmpty(column.getDictType()) && StringUtils.equalsAny( |
|||
column.getHtmlType(), |
|||
new String[] { GenConstants.HTML_SELECT, GenConstants.HTML_RADIO, GenConstants.HTML_CHECKBOX })) |
|||
{ |
|||
dicts.add("'" + column.getDictType() + "'"); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 获取权限前缀 |
|||
* |
|||
* @param moduleName 模块名称 |
|||
* @param businessName 业务名称 |
|||
* @return 返回权限前缀 |
|||
*/ |
|||
public static String getPermissionPrefix(String moduleName, String businessName) |
|||
{ |
|||
return StringUtils.format("{}:{}", moduleName, businessName); |
|||
} |
|||
|
|||
/** |
|||
* 获取上级菜单ID字段 |
|||
* |
|||
* @param paramsObj 生成其他选项 |
|||
* @return 上级菜单ID字段 |
|||
*/ |
|||
public static String getParentMenuId(JSONObject paramsObj) |
|||
{ |
|||
if (StringUtils.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.PARENT_MENU_ID) |
|||
&& StringUtils.isNotEmpty(paramsObj.getString(GenConstants.PARENT_MENU_ID))) |
|||
{ |
|||
return paramsObj.getString(GenConstants.PARENT_MENU_ID); |
|||
} |
|||
return DEFAULT_PARENT_MENU_ID; |
|||
} |
|||
|
|||
/** |
|||
* 获取树编码 |
|||
* |
|||
* @param paramsObj 生成其他选项 |
|||
* @return 树编码 |
|||
*/ |
|||
public static String getTreecode(JSONObject paramsObj) |
|||
{ |
|||
if (paramsObj.containsKey(GenConstants.TREE_CODE)) |
|||
{ |
|||
return StringUtils.toCamelCase(paramsObj.getString(GenConstants.TREE_CODE)); |
|||
} |
|||
return StringUtils.EMPTY; |
|||
} |
|||
|
|||
/** |
|||
* 获取树父编码 |
|||
* |
|||
* @param paramsObj 生成其他选项 |
|||
* @return 树父编码 |
|||
*/ |
|||
public static String getTreeParentCode(JSONObject paramsObj) |
|||
{ |
|||
if (paramsObj.containsKey(GenConstants.TREE_PARENT_CODE)) |
|||
{ |
|||
return StringUtils.toCamelCase(paramsObj.getString(GenConstants.TREE_PARENT_CODE)); |
|||
} |
|||
return StringUtils.EMPTY; |
|||
} |
|||
|
|||
/** |
|||
* 获取树名称 |
|||
* |
|||
* @param paramsObj 生成其他选项 |
|||
* @return 树名称 |
|||
*/ |
|||
public static String getTreeName(JSONObject paramsObj) |
|||
{ |
|||
if (paramsObj.containsKey(GenConstants.TREE_NAME)) |
|||
{ |
|||
return StringUtils.toCamelCase(paramsObj.getString(GenConstants.TREE_NAME)); |
|||
} |
|||
return StringUtils.EMPTY; |
|||
} |
|||
|
|||
/** |
|||
* 获取需要在哪一列上面显示展开按钮 |
|||
* |
|||
* @param genTable 业务表对象 |
|||
* @return 展开按钮列序号 |
|||
*/ |
|||
public static int getExpandColumn(GenTable genTable) |
|||
{ |
|||
String options = genTable.getOptions(); |
|||
JSONObject paramsObj = JSON.parseObject(options); |
|||
String treeName = paramsObj.getString(GenConstants.TREE_NAME); |
|||
int num = 0; |
|||
for (GenTableColumn column : genTable.getColumns()) |
|||
{ |
|||
if (column.isList()) |
|||
{ |
|||
num++; |
|||
String columnName = column.getColumnName(); |
|||
if (columnName.equals(treeName)) |
|||
{ |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
return num; |
|||
} |
|||
} |
@ -0,0 +1,10 @@ |
|||
# 代码生成 |
|||
gen: |
|||
# 作者 |
|||
author: ruoyi |
|||
# 默认生成包路径 system 需改成自己的模块名称 如 system monitor tool |
|||
packageName: com.lzbi.system |
|||
# 自动去除表前缀,默认是false |
|||
autoRemovePre: false |
|||
# 表前缀(生成类名不会包含表前缀,多个用逗号分隔) |
|||
tablePrefix: sys_ |
@ -0,0 +1,127 @@ |
|||
<?xml version="1.0" encoding="UTF-8" ?> |
|||
<!DOCTYPE mapper |
|||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" |
|||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
|||
<mapper namespace="com.lzbi.generator.mapper.GenTableColumnMapper"> |
|||
|
|||
<resultMap type="GenTableColumn" id="GenTableColumnResult"> |
|||
<id property="columnId" column="column_id" /> |
|||
<result property="tableId" column="table_id" /> |
|||
<result property="columnName" column="column_name" /> |
|||
<result property="columnComment" column="column_comment" /> |
|||
<result property="columnType" column="column_type" /> |
|||
<result property="javaType" column="java_type" /> |
|||
<result property="javaField" column="java_field" /> |
|||
<result property="isPk" column="is_pk" /> |
|||
<result property="isIncrement" column="is_increment" /> |
|||
<result property="isRequired" column="is_required" /> |
|||
<result property="isInsert" column="is_insert" /> |
|||
<result property="isEdit" column="is_edit" /> |
|||
<result property="isList" column="is_list" /> |
|||
<result property="isQuery" column="is_query" /> |
|||
<result property="queryType" column="query_type" /> |
|||
<result property="htmlType" column="html_type" /> |
|||
<result property="dictType" column="dict_type" /> |
|||
<result property="sort" column="sort" /> |
|||
<result property="createBy" column="create_by" /> |
|||
<result property="createTime" column="create_time" /> |
|||
<result property="updateBy" column="update_by" /> |
|||
<result property="updateTime" column="update_time" /> |
|||
</resultMap> |
|||
|
|||
<sql id="selectGenTableColumnVo"> |
|||
select column_id, table_id, column_name, column_comment, column_type, java_type, java_field, is_pk, is_increment, is_required, is_insert, is_edit, is_list, is_query, query_type, html_type, dict_type, sort, create_by, create_time, update_by, update_time from gen_table_column |
|||
</sql> |
|||
|
|||
<select id="selectGenTableColumnListByTableId" parameterType="Long" resultMap="GenTableColumnResult"> |
|||
<include refid="selectGenTableColumnVo"/> |
|||
where table_id = #{tableId} |
|||
order by sort |
|||
</select> |
|||
|
|||
<select id="selectDbTableColumnsByName" parameterType="String" resultMap="GenTableColumnResult"> |
|||
select column_name, (case when (is_nullable = 'no' <![CDATA[ && ]]> column_key != 'PRI') then '1' else null end) as is_required, (case when column_key = 'PRI' then '1' else '0' end) as is_pk, ordinal_position as sort, column_comment, (case when extra = 'auto_increment' then '1' else '0' end) as is_increment, column_type |
|||
from information_schema.columns where table_schema = (select database()) and table_name = (#{tableName}) |
|||
order by ordinal_position |
|||
</select> |
|||
|
|||
<insert id="insertGenTableColumn" parameterType="GenTableColumn" useGeneratedKeys="true" keyProperty="columnId"> |
|||
insert into gen_table_column ( |
|||
<if test="tableId != null and tableId != ''">table_id,</if> |
|||
<if test="columnName != null and columnName != ''">column_name,</if> |
|||
<if test="columnComment != null and columnComment != ''">column_comment,</if> |
|||
<if test="columnType != null and columnType != ''">column_type,</if> |
|||
<if test="javaType != null and javaType != ''">java_type,</if> |
|||
<if test="javaField != null and javaField != ''">java_field,</if> |
|||
<if test="isPk != null and isPk != ''">is_pk,</if> |
|||
<if test="isIncrement != null and isIncrement != ''">is_increment,</if> |
|||
<if test="isRequired != null and isRequired != ''">is_required,</if> |
|||
<if test="isInsert != null and isInsert != ''">is_insert,</if> |
|||
<if test="isEdit != null and isEdit != ''">is_edit,</if> |
|||
<if test="isList != null and isList != ''">is_list,</if> |
|||
<if test="isQuery != null and isQuery != ''">is_query,</if> |
|||
<if test="queryType != null and queryType != ''">query_type,</if> |
|||
<if test="htmlType != null and htmlType != ''">html_type,</if> |
|||
<if test="dictType != null and dictType != ''">dict_type,</if> |
|||
<if test="sort != null">sort,</if> |
|||
<if test="createBy != null and createBy != ''">create_by,</if> |
|||
create_time |
|||
)values( |
|||
<if test="tableId != null and tableId != ''">#{tableId},</if> |
|||
<if test="columnName != null and columnName != ''">#{columnName},</if> |
|||
<if test="columnComment != null and columnComment != ''">#{columnComment},</if> |
|||
<if test="columnType != null and columnType != ''">#{columnType},</if> |
|||
<if test="javaType != null and javaType != ''">#{javaType},</if> |
|||
<if test="javaField != null and javaField != ''">#{javaField},</if> |
|||
<if test="isPk != null and isPk != ''">#{isPk},</if> |
|||
<if test="isIncrement != null and isIncrement != ''">#{isIncrement},</if> |
|||
<if test="isRequired != null and isRequired != ''">#{isRequired},</if> |
|||
<if test="isInsert != null and isInsert != ''">#{isInsert},</if> |
|||
<if test="isEdit != null and isEdit != ''">#{isEdit},</if> |
|||
<if test="isList != null and isList != ''">#{isList},</if> |
|||
<if test="isQuery != null and isQuery != ''">#{isQuery},</if> |
|||
<if test="queryType != null and queryType != ''">#{queryType},</if> |
|||
<if test="htmlType != null and htmlType != ''">#{htmlType},</if> |
|||
<if test="dictType != null and dictType != ''">#{dictType},</if> |
|||
<if test="sort != null">#{sort},</if> |
|||
<if test="createBy != null and createBy != ''">#{createBy},</if> |
|||
sysdate() |
|||
) |
|||
</insert> |
|||
|
|||
<update id="updateGenTableColumn" parameterType="GenTableColumn"> |
|||
update gen_table_column |
|||
<set> |
|||
<if test="columnComment != null">column_comment = #{columnComment},</if> |
|||
<if test="javaType != null">java_type = #{javaType},</if> |
|||
<if test="javaField != null">java_field = #{javaField},</if> |
|||
<if test="isInsert != null">is_insert = #{isInsert},</if> |
|||
<if test="isEdit != null">is_edit = #{isEdit},</if> |
|||
<if test="isList != null">is_list = #{isList},</if> |
|||
<if test="isQuery != null">is_query = #{isQuery},</if> |
|||
<if test="isRequired != null">is_required = #{isRequired},</if> |
|||
<if test="queryType != null">query_type = #{queryType},</if> |
|||
<if test="htmlType != null">html_type = #{htmlType},</if> |
|||
<if test="dictType != null">dict_type = #{dictType},</if> |
|||
<if test="sort != null">sort = #{sort},</if> |
|||
<if test="updateBy != null">update_by = #{updateBy},</if> |
|||
update_time = sysdate() |
|||
</set> |
|||
where column_id = #{columnId} |
|||
</update> |
|||
|
|||
<delete id="deleteGenTableColumnByIds" parameterType="Long"> |
|||
delete from gen_table_column where table_id in |
|||
<foreach collection="array" item="tableId" open="(" separator="," close=")"> |
|||
#{tableId} |
|||
</foreach> |
|||
</delete> |
|||
|
|||
<delete id="deleteGenTableColumns"> |
|||
delete from gen_table_column where column_id in |
|||
<foreach collection="list" item="item" open="(" separator="," close=")"> |
|||
#{item.columnId} |
|||
</foreach> |
|||
</delete> |
|||
|
|||
</mapper> |
@ -0,0 +1,202 @@ |
|||
<?xml version="1.0" encoding="UTF-8" ?> |
|||
<!DOCTYPE mapper |
|||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" |
|||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
|||
<mapper namespace="com.lzbi.generator.mapper.GenTableMapper"> |
|||
|
|||
<resultMap type="GenTable" id="GenTableResult"> |
|||
<id property="tableId" column="table_id" /> |
|||
<result property="tableName" column="table_name" /> |
|||
<result property="tableComment" column="table_comment" /> |
|||
<result property="subTableName" column="sub_table_name" /> |
|||
<result property="subTableFkName" column="sub_table_fk_name" /> |
|||
<result property="className" column="class_name" /> |
|||
<result property="tplCategory" column="tpl_category" /> |
|||
<result property="packageName" column="package_name" /> |
|||
<result property="moduleName" column="module_name" /> |
|||
<result property="businessName" column="business_name" /> |
|||
<result property="functionName" column="function_name" /> |
|||
<result property="functionAuthor" column="function_author" /> |
|||
<result property="genType" column="gen_type" /> |
|||
<result property="genPath" column="gen_path" /> |
|||
<result property="options" column="options" /> |
|||
<result property="createBy" column="create_by" /> |
|||
<result property="createTime" column="create_time" /> |
|||
<result property="updateBy" column="update_by" /> |
|||
<result property="updateTime" column="update_time" /> |
|||
<result property="remark" column="remark" /> |
|||
<collection property="columns" javaType="java.util.List" resultMap="GenTableColumnResult" /> |
|||
</resultMap> |
|||
|
|||
<resultMap type="GenTableColumn" id="GenTableColumnResult"> |
|||
<id property="columnId" column="column_id" /> |
|||
<result property="tableId" column="table_id" /> |
|||
<result property="columnName" column="column_name" /> |
|||
<result property="columnComment" column="column_comment" /> |
|||
<result property="columnType" column="column_type" /> |
|||
<result property="javaType" column="java_type" /> |
|||
<result property="javaField" column="java_field" /> |
|||
<result property="isPk" column="is_pk" /> |
|||
<result property="isIncrement" column="is_increment" /> |
|||
<result property="isRequired" column="is_required" /> |
|||
<result property="isInsert" column="is_insert" /> |
|||
<result property="isEdit" column="is_edit" /> |
|||
<result property="isList" column="is_list" /> |
|||
<result property="isQuery" column="is_query" /> |
|||
<result property="queryType" column="query_type" /> |
|||
<result property="htmlType" column="html_type" /> |
|||
<result property="dictType" column="dict_type" /> |
|||
<result property="sort" column="sort" /> |
|||
<result property="createBy" column="create_by" /> |
|||
<result property="createTime" column="create_time" /> |
|||
<result property="updateBy" column="update_by" /> |
|||
<result property="updateTime" column="update_time" /> |
|||
</resultMap> |
|||
|
|||
<sql id="selectGenTableVo"> |
|||
select table_id, table_name, table_comment, sub_table_name, sub_table_fk_name, class_name, tpl_category, package_name, module_name, business_name, function_name, function_author, gen_type, gen_path, options, create_by, create_time, update_by, update_time, remark from gen_table |
|||
</sql> |
|||
|
|||
<select id="selectGenTableList" parameterType="GenTable" resultMap="GenTableResult"> |
|||
<include refid="selectGenTableVo"/> |
|||
<where> |
|||
<if test="tableName != null and tableName != ''"> |
|||
AND lower(table_name) like lower(concat('%', #{tableName}, '%')) |
|||
</if> |
|||
<if test="tableComment != null and tableComment != ''"> |
|||
AND lower(table_comment) like lower(concat('%', #{tableComment}, '%')) |
|||
</if> |
|||
<if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 --> |
|||
AND date_format(create_time,'%y%m%d') >= date_format(#{params.beginTime},'%y%m%d') |
|||
</if> |
|||
<if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 --> |
|||
AND date_format(create_time,'%y%m%d') <= date_format(#{params.endTime},'%y%m%d') |
|||
</if> |
|||
</where> |
|||
</select> |
|||
|
|||
<select id="selectDbTableList" parameterType="GenTable" resultMap="GenTableResult"> |
|||
select table_name, table_comment, create_time, update_time from information_schema.tables |
|||
where table_schema = (select database()) |
|||
AND table_name NOT LIKE 'qrtz_%' AND table_name NOT LIKE 'gen_%' |
|||
AND table_name NOT IN (select table_name from gen_table) |
|||
<if test="tableName != null and tableName != ''"> |
|||
AND lower(table_name) like lower(concat('%', #{tableName}, '%')) |
|||
</if> |
|||
<if test="tableComment != null and tableComment != ''"> |
|||
AND lower(table_comment) like lower(concat('%', #{tableComment}, '%')) |
|||
</if> |
|||
<if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 --> |
|||
AND date_format(create_time,'%y%m%d') >= date_format(#{params.beginTime},'%y%m%d') |
|||
</if> |
|||
<if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 --> |
|||
AND date_format(create_time,'%y%m%d') <= date_format(#{params.endTime},'%y%m%d') |
|||
</if> |
|||
order by create_time desc |
|||
</select> |
|||
|
|||
<select id="selectDbTableListByNames" resultMap="GenTableResult"> |
|||
select table_name, table_comment, create_time, update_time from information_schema.tables |
|||
where table_name NOT LIKE 'qrtz_%' and table_name NOT LIKE 'gen_%' and table_schema = (select database()) |
|||
and table_name in |
|||
<foreach collection="array" item="name" open="(" separator="," close=")"> |
|||
#{name} |
|||
</foreach> |
|||
</select> |
|||
|
|||
<select id="selectTableByName" parameterType="String" resultMap="GenTableResult"> |
|||
select table_name, table_comment, create_time, update_time from information_schema.tables |
|||
where table_comment <![CDATA[ <> ]]> '' and table_schema = (select database()) |
|||
and table_name = #{tableName} |
|||
</select> |
|||
|
|||
<select id="selectGenTableById" parameterType="Long" resultMap="GenTableResult"> |
|||
SELECT t.table_id, t.table_name, t.table_comment, t.sub_table_name, t.sub_table_fk_name, t.class_name, t.tpl_category, t.package_name, t.module_name, t.business_name, t.function_name, t.function_author, t.gen_type, t.gen_path, t.options, t.remark, |
|||
c.column_id, c.column_name, c.column_comment, c.column_type, c.java_type, c.java_field, c.is_pk, c.is_increment, c.is_required, c.is_insert, c.is_edit, c.is_list, c.is_query, c.query_type, c.html_type, c.dict_type, c.sort |
|||
FROM gen_table t |
|||
LEFT JOIN gen_table_column c ON t.table_id = c.table_id |
|||
where t.table_id = #{tableId} order by c.sort |
|||
</select> |
|||
|
|||
<select id="selectGenTableByName" parameterType="String" resultMap="GenTableResult"> |
|||
SELECT t.table_id, t.table_name, t.table_comment, t.sub_table_name, t.sub_table_fk_name, t.class_name, t.tpl_category, t.package_name, t.module_name, t.business_name, t.function_name, t.function_author, t.gen_type, t.gen_path, t.options, t.remark, |
|||
c.column_id, c.column_name, c.column_comment, c.column_type, c.java_type, c.java_field, c.is_pk, c.is_increment, c.is_required, c.is_insert, c.is_edit, c.is_list, c.is_query, c.query_type, c.html_type, c.dict_type, c.sort |
|||
FROM gen_table t |
|||
LEFT JOIN gen_table_column c ON t.table_id = c.table_id |
|||
where t.table_name = #{tableName} order by c.sort |
|||
</select> |
|||
|
|||
<select id="selectGenTableAll" parameterType="String" resultMap="GenTableResult"> |
|||
SELECT t.table_id, t.table_name, t.table_comment, t.sub_table_name, t.sub_table_fk_name, t.class_name, t.tpl_category, t.package_name, t.module_name, t.business_name, t.function_name, t.function_author, t.options, t.remark, |
|||
c.column_id, c.column_name, c.column_comment, c.column_type, c.java_type, c.java_field, c.is_pk, c.is_increment, c.is_required, c.is_insert, c.is_edit, c.is_list, c.is_query, c.query_type, c.html_type, c.dict_type, c.sort |
|||
FROM gen_table t |
|||
LEFT JOIN gen_table_column c ON t.table_id = c.table_id |
|||
order by c.sort |
|||
</select> |
|||
|
|||
<insert id="insertGenTable" parameterType="GenTable" useGeneratedKeys="true" keyProperty="tableId"> |
|||
insert into gen_table ( |
|||
<if test="tableName != null">table_name,</if> |
|||
<if test="tableComment != null and tableComment != ''">table_comment,</if> |
|||
<if test="className != null and className != ''">class_name,</if> |
|||
<if test="tplCategory != null and tplCategory != ''">tpl_category,</if> |
|||
<if test="packageName != null and packageName != ''">package_name,</if> |
|||
<if test="moduleName != null and moduleName != ''">module_name,</if> |
|||
<if test="businessName != null and businessName != ''">business_name,</if> |
|||
<if test="functionName != null and functionName != ''">function_name,</if> |
|||
<if test="functionAuthor != null and functionAuthor != ''">function_author,</if> |
|||
<if test="genType != null and genType != ''">gen_type,</if> |
|||
<if test="genPath != null and genPath != ''">gen_path,</if> |
|||
<if test="remark != null and remark != ''">remark,</if> |
|||
<if test="createBy != null and createBy != ''">create_by,</if> |
|||
create_time |
|||
)values( |
|||
<if test="tableName != null">#{tableName},</if> |
|||
<if test="tableComment != null and tableComment != ''">#{tableComment},</if> |
|||
<if test="className != null and className != ''">#{className},</if> |
|||
<if test="tplCategory != null and tplCategory != ''">#{tplCategory},</if> |
|||
<if test="packageName != null and packageName != ''">#{packageName},</if> |
|||
<if test="moduleName != null and moduleName != ''">#{moduleName},</if> |
|||
<if test="businessName != null and businessName != ''">#{businessName},</if> |
|||
<if test="functionName != null and functionName != ''">#{functionName},</if> |
|||
<if test="functionAuthor != null and functionAuthor != ''">#{functionAuthor},</if> |
|||
<if test="genType != null and genType != ''">#{genType},</if> |
|||
<if test="genPath != null and genPath != ''">#{genPath},</if> |
|||
<if test="remark != null and remark != ''">#{remark},</if> |
|||
<if test="createBy != null and createBy != ''">#{createBy},</if> |
|||
sysdate() |
|||
) |
|||
</insert> |
|||
|
|||
<update id="updateGenTable" parameterType="GenTable"> |
|||
update gen_table |
|||
<set> |
|||
<if test="tableName != null">table_name = #{tableName},</if> |
|||
<if test="tableComment != null and tableComment != ''">table_comment = #{tableComment},</if> |
|||
<if test="subTableName != null">sub_table_name = #{subTableName},</if> |
|||
<if test="subTableFkName != null">sub_table_fk_name = #{subTableFkName},</if> |
|||
<if test="className != null and className != ''">class_name = #{className},</if> |
|||
<if test="functionAuthor != null and functionAuthor != ''">function_author = #{functionAuthor},</if> |
|||
<if test="genType != null and genType != ''">gen_type = #{genType},</if> |
|||
<if test="genPath != null and genPath != ''">gen_path = #{genPath},</if> |
|||
<if test="tplCategory != null and tplCategory != ''">tpl_category = #{tplCategory},</if> |
|||
<if test="packageName != null and packageName != ''">package_name = #{packageName},</if> |
|||
<if test="moduleName != null and moduleName != ''">module_name = #{moduleName},</if> |
|||
<if test="businessName != null and businessName != ''">business_name = #{businessName},</if> |
|||
<if test="functionName != null and functionName != ''">function_name = #{functionName},</if> |
|||
<if test="options != null and options != ''">options = #{options},</if> |
|||
<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if> |
|||
<if test="remark != null">remark = #{remark},</if> |
|||
update_time = sysdate() |
|||
</set> |
|||
where table_id = #{tableId} |
|||
</update> |
|||
|
|||
<delete id="deleteGenTableByIds" parameterType="Long"> |
|||
delete from gen_table where table_id in |
|||
<foreach collection="array" item="tableId" open="(" separator="," close=")"> |
|||
#{tableId} |
|||
</foreach> |
|||
</delete> |
|||
|
|||
</mapper> |
@ -0,0 +1,115 @@ |
|||
package ${packageName}.controller; |
|||
|
|||
import java.util.List; |
|||
import javax.servlet.http.HttpServletResponse; |
|||
import org.springframework.security.access.prepost.PreAuthorize; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.web.bind.annotation.GetMapping; |
|||
import org.springframework.web.bind.annotation.PostMapping; |
|||
import org.springframework.web.bind.annotation.PutMapping; |
|||
import org.springframework.web.bind.annotation.DeleteMapping; |
|||
import org.springframework.web.bind.annotation.PathVariable; |
|||
import org.springframework.web.bind.annotation.RequestBody; |
|||
import org.springframework.web.bind.annotation.RequestMapping; |
|||
import org.springframework.web.bind.annotation.RestController; |
|||
import com.lzbi.common.annotation.Log; |
|||
import com.lzbi.common.core.controller.BaseController; |
|||
import com.lzbi.common.core.domain.AjaxResult; |
|||
import com.lzbi.common.enums.BusinessType; |
|||
import ${packageName}.domain.${ClassName}; |
|||
import ${packageName}.service.I${ClassName}Service; |
|||
import com.lzbi.common.utils.poi.ExcelUtil; |
|||
#if($table.crud || $table.sub) |
|||
import com.lzbi.common.core.page.TableDataInfo; |
|||
#elseif($table.tree) |
|||
#end |
|||
|
|||
/** |
|||
* ${functionName}Controller |
|||
* |
|||
* @author ${author} |
|||
* @date ${datetime} |
|||
*/ |
|||
@RestController |
|||
@RequestMapping("/${moduleName}/${businessName}") |
|||
public class ${ClassName}Controller extends BaseController |
|||
{ |
|||
@Autowired |
|||
private I${ClassName}Service ${className}Service; |
|||
|
|||
/** |
|||
* 查询${functionName}列表 |
|||
*/ |
|||
@PreAuthorize("@ss.hasPermi('${permissionPrefix}:list')") |
|||
@GetMapping("/list") |
|||
#if($table.crud || $table.sub) |
|||
public TableDataInfo list(${ClassName} ${className}) |
|||
{ |
|||
startPage(); |
|||
List<${ClassName}> list = ${className}Service.select${ClassName}List(${className}); |
|||
return getDataTable(list); |
|||
} |
|||
#elseif($table.tree) |
|||
public AjaxResult list(${ClassName} ${className}) |
|||
{ |
|||
List<${ClassName}> list = ${className}Service.select${ClassName}List(${className}); |
|||
return success(list); |
|||
} |
|||
#end |
|||
|
|||
/** |
|||
* 导出${functionName}列表 |
|||
*/ |
|||
@PreAuthorize("@ss.hasPermi('${permissionPrefix}:export')") |
|||
@Log(title = "${functionName}", businessType = BusinessType.EXPORT) |
|||
@PostMapping("/export") |
|||
public void export(HttpServletResponse response, ${ClassName} ${className}) |
|||
{ |
|||
List<${ClassName}> list = ${className}Service.select${ClassName}List(${className}); |
|||
ExcelUtil<${ClassName}> util = new ExcelUtil<${ClassName}>(${ClassName}.class); |
|||
util.exportExcel(response, list, "${functionName}数据"); |
|||
} |
|||
|
|||
/** |
|||
* 获取${functionName}详细信息 |
|||
*/ |
|||
@PreAuthorize("@ss.hasPermi('${permissionPrefix}:query')") |
|||
@GetMapping(value = "/{${pkColumn.javaField}}") |
|||
public AjaxResult getInfo(@PathVariable("${pkColumn.javaField}") ${pkColumn.javaType} ${pkColumn.javaField}) |
|||
{ |
|||
return success(${className}Service.select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaField})); |
|||
} |
|||
|
|||
/** |
|||
* 新增${functionName} |
|||
*/ |
|||
@PreAuthorize("@ss.hasPermi('${permissionPrefix}:add')") |
|||
@Log(title = "${functionName}", businessType = BusinessType.INSERT) |
|||
@PostMapping |
|||
public AjaxResult add(@RequestBody ${ClassName} ${className}) |
|||
{ |
|||
return toAjax(${className}Service.insert${ClassName}(${className})); |
|||
} |
|||
|
|||
/** |
|||
* 修改${functionName} |
|||
*/ |
|||
@PreAuthorize("@ss.hasPermi('${permissionPrefix}:edit')") |
|||
@Log(title = "${functionName}", businessType = BusinessType.UPDATE) |
|||
@PutMapping |
|||
public AjaxResult edit(@RequestBody ${ClassName} ${className}) |
|||
{ |
|||
return toAjax(${className}Service.update${ClassName}(${className})); |
|||
} |
|||
|
|||
/** |
|||
* 删除${functionName} |
|||
*/ |
|||
@PreAuthorize("@ss.hasPermi('${permissionPrefix}:remove')") |
|||
@Log(title = "${functionName}", businessType = BusinessType.DELETE) |
|||
@DeleteMapping("/{${pkColumn.javaField}s}") |
|||
public AjaxResult remove(@PathVariable ${pkColumn.javaType}[] ${pkColumn.javaField}s) |
|||
{ |
|||
return toAjax(${className}Service.delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaField}s)); |
|||
} |
|||
} |
@ -0,0 +1,105 @@ |
|||
package ${packageName}.domain; |
|||
|
|||
#foreach ($import in $importList) |
|||
import ${import}; |
|||
#end |
|||
import org.apache.commons.lang3.builder.ToStringBuilder; |
|||
import org.apache.commons.lang3.builder.ToStringStyle; |
|||
import com.lzbi.common.annotation.Excel; |
|||
#if($table.crud || $table.sub) |
|||
import com.lzbi.common.core.domain.BaseEntity; |
|||
#elseif($table.tree) |
|||
import com.lzbi.common.core.domain.TreeEntity; |
|||
#end |
|||
|
|||
/** |
|||
* ${functionName}对象 ${tableName} |
|||
* |
|||
* @author ${author} |
|||
* @date ${datetime} |
|||
*/ |
|||
#if($table.crud || $table.sub) |
|||
#set($Entity="BaseEntity") |
|||
#elseif($table.tree) |
|||
#set($Entity="TreeEntity") |
|||
#end |
|||
public class ${ClassName} extends ${Entity} |
|||
{ |
|||
private static final long serialVersionUID = 1L; |
|||
|
|||
#foreach ($column in $columns) |
|||
#if(!$table.isSuperColumn($column.javaField)) |
|||
/** $column.columnComment */ |
|||
#if($column.list) |
|||
#set($parentheseIndex=$column.columnComment.indexOf("(")) |
|||
#if($parentheseIndex != -1) |
|||
#set($comment=$column.columnComment.substring(0, $parentheseIndex)) |
|||
#else |
|||
#set($comment=$column.columnComment) |
|||
#end |
|||
#if($parentheseIndex != -1) |
|||
@Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()") |
|||
#elseif($column.javaType == 'Date') |
|||
@JsonFormat(pattern = "yyyy-MM-dd") |
|||
@Excel(name = "${comment}", width = 30, dateFormat = "yyyy-MM-dd") |
|||
#else |
|||
@Excel(name = "${comment}") |
|||
#end |
|||
#end |
|||
private $column.javaType $column.javaField; |
|||
|
|||
#end |
|||
#end |
|||
#if($table.sub) |
|||
/** $table.subTable.functionName信息 */ |
|||
private List<${subClassName}> ${subclassName}List; |
|||
|
|||
#end |
|||
#foreach ($column in $columns) |
|||
#if(!$table.isSuperColumn($column.javaField)) |
|||
#if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]")) |
|||
#set($AttrName=$column.javaField) |
|||
#else |
|||
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) |
|||
#end |
|||
public void set${AttrName}($column.javaType $column.javaField) |
|||
{ |
|||
this.$column.javaField = $column.javaField; |
|||
} |
|||
|
|||
public $column.javaType get${AttrName}() |
|||
{ |
|||
return $column.javaField; |
|||
} |
|||
#end |
|||
#end |
|||
|
|||
#if($table.sub) |
|||
public List<${subClassName}> get${subClassName}List() |
|||
{ |
|||
return ${subclassName}List; |
|||
} |
|||
|
|||
public void set${subClassName}List(List<${subClassName}> ${subclassName}List) |
|||
{ |
|||
this.${subclassName}List = ${subclassName}List; |
|||
} |
|||
|
|||
#end |
|||
@Override |
|||
public String toString() { |
|||
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) |
|||
#foreach ($column in $columns) |
|||
#if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]")) |
|||
#set($AttrName=$column.javaField) |
|||
#else |
|||
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) |
|||
#end |
|||
.append("${column.javaField}", get${AttrName}()) |
|||
#end |
|||
#if($table.sub) |
|||
.append("${subclassName}List", get${subClassName}List()) |
|||
#end |
|||
.toString(); |
|||
} |
|||
} |
@ -0,0 +1,91 @@ |
|||
package ${packageName}.mapper; |
|||
|
|||
import java.util.List; |
|||
import ${packageName}.domain.${ClassName}; |
|||
#if($table.sub) |
|||
import ${packageName}.domain.${subClassName}; |
|||
#end |
|||
|
|||
/** |
|||
* ${functionName}Mapper接口 |
|||
* |
|||
* @author ${author} |
|||
* @date ${datetime} |
|||
*/ |
|||
public interface ${ClassName}Mapper |
|||
{ |
|||
/** |
|||
* 查询${functionName} |
|||
* |
|||
* @param ${pkColumn.javaField} ${functionName}主键 |
|||
* @return ${functionName} |
|||
*/ |
|||
public ${ClassName} select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField}); |
|||
|
|||
/** |
|||
* 查询${functionName}列表 |
|||
* |
|||
* @param ${className} ${functionName} |
|||
* @return ${functionName}集合 |
|||
*/ |
|||
public List<${ClassName}> select${ClassName}List(${ClassName} ${className}); |
|||
|
|||
/** |
|||
* 新增${functionName} |
|||
* |
|||
* @param ${className} ${functionName} |
|||
* @return 结果 |
|||
*/ |
|||
public int insert${ClassName}(${ClassName} ${className}); |
|||
|
|||
/** |
|||
* 修改${functionName} |
|||
* |
|||
* @param ${className} ${functionName} |
|||
* @return 结果 |
|||
*/ |
|||
public int update${ClassName}(${ClassName} ${className}); |
|||
|
|||
/** |
|||
* 删除${functionName} |
|||
* |
|||
* @param ${pkColumn.javaField} ${functionName}主键 |
|||
* @return 结果 |
|||
*/ |
|||
public int delete${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField}); |
|||
|
|||
/** |
|||
* 批量删除${functionName} |
|||
* |
|||
* @param ${pkColumn.javaField}s 需要删除的数据主键集合 |
|||
* @return 结果 |
|||
*/ |
|||
public int delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaType}[] ${pkColumn.javaField}s); |
|||
#if($table.sub) |
|||
|
|||
/** |
|||
* 批量删除${subTable.functionName} |
|||
* |
|||
* @param ${pkColumn.javaField}s 需要删除的数据主键集合 |
|||
* @return 结果 |
|||
*/ |
|||
public int delete${subClassName}By${subTableFkClassName}s(${pkColumn.javaType}[] ${pkColumn.javaField}s); |
|||
|
|||
/** |
|||
* 批量新增${subTable.functionName} |
|||
* |
|||
* @param ${subclassName}List ${subTable.functionName}列表 |
|||
* @return 结果 |
|||
*/ |
|||
public int batch${subClassName}(List<${subClassName}> ${subclassName}List); |
|||
|
|||
|
|||
/** |
|||
* 通过${functionName}主键删除${subTable.functionName}信息 |
|||
* |
|||
* @param ${pkColumn.javaField} ${functionName}ID |
|||
* @return 结果 |
|||
*/ |
|||
public int delete${subClassName}By${subTableFkClassName}(${pkColumn.javaType} ${pkColumn.javaField}); |
|||
#end |
|||
} |
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue