系统更新
This commit is contained in:
46
ruoyi-common/pom.xml
Normal file
46
ruoyi-common/pom.xml
Normal file
@@ -0,0 +1,46 @@
|
||||
<?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>ruoyi-vue-plus</artifactId>
|
||||
<groupId>org.dromara</groupId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<modules>
|
||||
<module>ruoyi-common-bom</module>
|
||||
<module>ruoyi-common-social</module>
|
||||
<module>ruoyi-common-core</module>
|
||||
<module>ruoyi-common-doc</module>
|
||||
<module>ruoyi-common-excel</module>
|
||||
<module>ruoyi-common-idempotent</module>
|
||||
<module>ruoyi-common-job</module>
|
||||
<module>ruoyi-common-log</module>
|
||||
<module>ruoyi-common-mail</module>
|
||||
<module>ruoyi-common-mybatis</module>
|
||||
<module>ruoyi-common-oss</module>
|
||||
<module>ruoyi-common-ratelimiter</module>
|
||||
<module>ruoyi-common-redis</module>
|
||||
<module>ruoyi-common-satoken</module>
|
||||
<module>ruoyi-common-security</module>
|
||||
<module>ruoyi-common-sms</module>
|
||||
<module>ruoyi-common-web</module>
|
||||
<module>ruoyi-common-translation</module>
|
||||
<module>ruoyi-common-sensitive</module>
|
||||
<module>ruoyi-common-json</module>
|
||||
<module>ruoyi-common-encrypt</module>
|
||||
<module>ruoyi-common-tenant</module>
|
||||
<module>ruoyi-common-websocket</module>
|
||||
<module>ruoyi-common-sse</module>
|
||||
</modules>
|
||||
|
||||
<artifactId>ruoyi-common</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<description>
|
||||
common 通用模块
|
||||
</description>
|
||||
|
||||
</project>
|
||||
185
ruoyi-common/ruoyi-common-bom/pom.xml
Normal file
185
ruoyi-common/ruoyi-common-bom/pom.xml
Normal file
@@ -0,0 +1,185 @@
|
||||
<?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">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-bom</artifactId>
|
||||
<version>${revision}</version>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<description>
|
||||
ruoyi-common-bom common依赖项
|
||||
</description>
|
||||
|
||||
<properties>
|
||||
<revision>5.2.3</revision>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<!-- 核心模块 -->
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-core</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 接口模块 -->
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-doc</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- excel -->
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-excel</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 幂等 -->
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-idempotent</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 调度模块 -->
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-job</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 日志记录 -->
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-log</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 邮件服务 -->
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-mail</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 数据库服务 -->
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-mybatis</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- OSS -->
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-oss</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 限流 -->
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-ratelimiter</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 缓存服务 -->
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-redis</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- satoken -->
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-satoken</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 安全模块 -->
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-security</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 短信模块 -->
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-sms</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-social</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- web服务 -->
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-web</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 翻译模块 -->
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-translation</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 脱敏模块 -->
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-sensitive</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 序列化模块 -->
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-json</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 数据库加解密模块 -->
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-encrypt</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 租户模块 -->
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-tenant</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- WebSocket模块 -->
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-websocket</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- SSE模块 -->
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-sse</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
</project>
|
||||
99
ruoyi-common/ruoyi-common-core/pom.xml
Normal file
99
ruoyi-common/ruoyi-common-core/pom.xml
Normal file
@@ -0,0 +1,99 @@
|
||||
<?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>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>ruoyi-common-core</artifactId>
|
||||
|
||||
<description>
|
||||
ruoyi-common-core 核心模块
|
||||
</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>
|
||||
|
||||
<!-- 自定义验证注解 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!--常用工具类 -->
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- servlet包 -->
|
||||
<dependency>
|
||||
<groupId>jakarta.servlet</groupId>
|
||||
<artifactId>jakarta.servlet-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-http</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-extra</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 自动生成YML配置关联JSON文件 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-properties-migrator</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.github.linpeilie</groupId>
|
||||
<artifactId>mapstruct-plus-spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 离线IP地址定位库 -->
|
||||
<dependency>
|
||||
<groupId>org.lionsoul</groupId>
|
||||
<artifactId>ip2region</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,17 @@
|
||||
package org.dromara.common.core.config;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.context.annotation.EnableAspectJAutoProxy;
|
||||
import org.springframework.scheduling.annotation.EnableAsync;
|
||||
|
||||
/**
|
||||
* 程序注解配置
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@AutoConfiguration
|
||||
@EnableAspectJAutoProxy
|
||||
@EnableAsync(proxyTargetClass = true)
|
||||
public class ApplicationConfig {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package org.dromara.common.core.config;
|
||||
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import org.dromara.common.core.utils.SpringUtils;
|
||||
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.core.task.VirtualThreadTaskExecutor;
|
||||
import org.springframework.scheduling.annotation.AsyncConfigurer;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
* 异步配置
|
||||
* <p>
|
||||
* 如果未使用虚拟线程则生效
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@AutoConfiguration
|
||||
public class AsyncConfig implements AsyncConfigurer {
|
||||
|
||||
/**
|
||||
* 自定义 @Async 注解使用系统线程池
|
||||
*/
|
||||
@Override
|
||||
public Executor getAsyncExecutor() {
|
||||
if(SpringUtils.isVirtual()) {
|
||||
return new VirtualThreadTaskExecutor("async-");
|
||||
}
|
||||
return SpringUtils.getBean("scheduledExecutorService");
|
||||
}
|
||||
|
||||
/**
|
||||
* 异步执行异常处理
|
||||
*/
|
||||
@Override
|
||||
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
|
||||
return (throwable, method, objects) -> {
|
||||
throwable.printStackTrace();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Exception message - ").append(throwable.getMessage())
|
||||
.append(", Method name - ").append(method.getName());
|
||||
if (ArrayUtil.isNotEmpty(objects)) {
|
||||
sb.append(", Parameter value - ").append(Arrays.toString(objects));
|
||||
}
|
||||
throw new ServiceException(sb.toString());
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package org.dromara.common.core.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 读取项目相关配置
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
|
||||
@Data
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "ruoyi")
|
||||
public class RuoYiConfig {
|
||||
|
||||
/**
|
||||
* 项目名称
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 版本
|
||||
*/
|
||||
private String version;
|
||||
|
||||
/**
|
||||
* 版权年份
|
||||
*/
|
||||
private String copyrightYear;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package org.dromara.common.core.config;
|
||||
|
||||
import jakarta.annotation.PreDestroy;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
|
||||
import org.dromara.common.core.config.properties.ThreadPoolProperties;
|
||||
import org.dromara.common.core.utils.Threads;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
|
||||
/**
|
||||
* 线程池配置
|
||||
*
|
||||
* @author Lion Li
|
||||
**/
|
||||
@Slf4j
|
||||
@AutoConfiguration
|
||||
@EnableConfigurationProperties(ThreadPoolProperties.class)
|
||||
public class ThreadPoolConfig {
|
||||
|
||||
/**
|
||||
* 核心线程数 = cpu 核心数 + 1
|
||||
*/
|
||||
private final int core = Runtime.getRuntime().availableProcessors() + 1;
|
||||
|
||||
private ScheduledExecutorService scheduledExecutorService;
|
||||
|
||||
@Bean(name = "threadPoolTaskExecutor")
|
||||
@ConditionalOnProperty(prefix = "thread-pool", name = "enabled", havingValue = "true")
|
||||
public ThreadPoolTaskExecutor threadPoolTaskExecutor(ThreadPoolProperties threadPoolProperties) {
|
||||
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
||||
executor.setCorePoolSize(core);
|
||||
executor.setMaxPoolSize(core * 2);
|
||||
executor.setQueueCapacity(threadPoolProperties.getQueueCapacity());
|
||||
executor.setKeepAliveSeconds(threadPoolProperties.getKeepAliveSeconds());
|
||||
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
|
||||
return executor;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行周期性或定时任务
|
||||
*/
|
||||
@Bean(name = "scheduledExecutorService")
|
||||
protected ScheduledExecutorService scheduledExecutorService() {
|
||||
ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(core,
|
||||
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);
|
||||
}
|
||||
};
|
||||
this.scheduledExecutorService = scheduledThreadPoolExecutor;
|
||||
return scheduledThreadPoolExecutor;
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁事件
|
||||
*/
|
||||
@PreDestroy
|
||||
public void destroy() {
|
||||
try {
|
||||
log.info("====关闭后台任务任务线程池====");
|
||||
Threads.shutdownAndAwaitTermination(scheduledExecutorService);
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package org.dromara.common.core.config;
|
||||
|
||||
import jakarta.validation.Validator;
|
||||
import org.hibernate.validator.HibernateValidator;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* 校验框架配置类
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@AutoConfiguration
|
||||
public class ValidatorConfig {
|
||||
|
||||
/**
|
||||
* 配置校验框架 快速返回模式
|
||||
*/
|
||||
@Bean
|
||||
public Validator validator(MessageSource messageSource) {
|
||||
try (LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean()) {
|
||||
// 国际化
|
||||
factoryBean.setValidationMessageSource(messageSource);
|
||||
// 设置使用 HibernateValidator 校验器
|
||||
factoryBean.setProviderClass(HibernateValidator.class);
|
||||
Properties properties = new Properties();
|
||||
// 设置 快速异常返回
|
||||
properties.setProperty("hibernate.validator.fail_fast", "true");
|
||||
factoryBean.setValidationProperties(properties);
|
||||
// 加载配置
|
||||
factoryBean.afterPropertiesSet();
|
||||
return factoryBean.getValidator();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package org.dromara.common.core.config.properties;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* 线程池 配置属性
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@Data
|
||||
@ConfigurationProperties(prefix = "thread-pool")
|
||||
public class ThreadPoolProperties {
|
||||
|
||||
/**
|
||||
* 是否开启线程池
|
||||
*/
|
||||
private boolean enabled;
|
||||
|
||||
/**
|
||||
* 队列最大长度
|
||||
*/
|
||||
private int queueCapacity;
|
||||
|
||||
/**
|
||||
* 线程池维护线程所允许的空闲时间
|
||||
*/
|
||||
private int keepAliveSeconds;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package org.dromara.common.core.constant;
|
||||
|
||||
/**
|
||||
* 缓存的key 常量
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
public interface CacheConstants {
|
||||
|
||||
/**
|
||||
* 在线用户 redis key
|
||||
*/
|
||||
String ONLINE_TOKEN_KEY = "online_tokens:";
|
||||
|
||||
/**
|
||||
* 参数管理 cache key
|
||||
*/
|
||||
String SYS_CONFIG_KEY = "sys_config:";
|
||||
|
||||
/**
|
||||
* 字典管理 cache key
|
||||
*/
|
||||
String SYS_DICT_KEY = "sys_dict:";
|
||||
|
||||
/**
|
||||
* 登录账户密码错误次数 redis key
|
||||
*/
|
||||
String PWD_ERR_CNT_KEY = "pwd_err_cnt:";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package org.dromara.common.core.constant;
|
||||
|
||||
/**
|
||||
* 缓存组名称常量
|
||||
* <p>
|
||||
* key 格式为 cacheNames#ttl#maxIdleTime#maxSize
|
||||
* <p>
|
||||
* ttl 过期时间 如果设置为0则不过期 默认为0
|
||||
* maxIdleTime 最大空闲时间 根据LRU算法清理空闲数据 如果设置为0则不检测 默认为0
|
||||
* maxSize 组最大长度 根据LRU算法清理溢出数据 如果设置为0则无限长 默认为0
|
||||
* <p>
|
||||
* 例子: test#60s、test#0#60s、test#0#1m#1000、test#1h#0#500
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
public interface CacheNames {
|
||||
|
||||
/**
|
||||
* 演示案例
|
||||
*/
|
||||
String DEMO_CACHE = "demo:cache#60s#10m#20";
|
||||
|
||||
/**
|
||||
* 系统配置
|
||||
*/
|
||||
String SYS_CONFIG = "sys_config";
|
||||
|
||||
/**
|
||||
* 数据字典
|
||||
*/
|
||||
String SYS_DICT = "sys_dict";
|
||||
|
||||
/**
|
||||
* 租户
|
||||
*/
|
||||
String SYS_TENANT = GlobalConstants.GLOBAL_REDIS_KEY + "sys_tenant#30d";
|
||||
|
||||
/**
|
||||
* 客户端
|
||||
*/
|
||||
String SYS_CLIENT = GlobalConstants.GLOBAL_REDIS_KEY + "sys_client#30d";
|
||||
|
||||
/**
|
||||
* 用户账户
|
||||
*/
|
||||
String SYS_USER_NAME = "sys_user_name#30d";
|
||||
|
||||
/**
|
||||
* 用户名称
|
||||
*/
|
||||
String SYS_NICKNAME = "sys_nickname#30d";
|
||||
|
||||
/**
|
||||
* 部门
|
||||
*/
|
||||
String SYS_DEPT = "sys_dept#30d";
|
||||
|
||||
/**
|
||||
* OSS内容
|
||||
*/
|
||||
String SYS_OSS = "sys_oss#30d";
|
||||
|
||||
/**
|
||||
* 角色自定义权限
|
||||
*/
|
||||
String SYS_ROLE_CUSTOM = "sys_role_custom#30d";
|
||||
|
||||
/**
|
||||
* 部门及以下权限
|
||||
*/
|
||||
String SYS_DEPT_AND_CHILD = "sys_dept_and_child#30d";
|
||||
|
||||
/**
|
||||
* OSS配置
|
||||
*/
|
||||
String SYS_OSS_CONFIG = GlobalConstants.GLOBAL_REDIS_KEY + "sys_oss_config";
|
||||
|
||||
/**
|
||||
* 在线用户
|
||||
*/
|
||||
String ONLINE_TOKEN = "online_tokens";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package org.dromara.common.core.constant;
|
||||
|
||||
/**
|
||||
* 通用常量信息
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public interface Constants {
|
||||
|
||||
/**
|
||||
* UTF-8 字符集
|
||||
*/
|
||||
String UTF8 = "UTF-8";
|
||||
|
||||
/**
|
||||
* GBK 字符集
|
||||
*/
|
||||
String GBK = "GBK";
|
||||
|
||||
/**
|
||||
* www主域
|
||||
*/
|
||||
String WWW = "www.";
|
||||
|
||||
/**
|
||||
* http请求
|
||||
*/
|
||||
String HTTP = "http://";
|
||||
|
||||
/**
|
||||
* https请求
|
||||
*/
|
||||
String HTTPS = "https://";
|
||||
|
||||
/**
|
||||
* 通用成功标识
|
||||
*/
|
||||
String SUCCESS = "0";
|
||||
|
||||
/**
|
||||
* 通用失败标识
|
||||
*/
|
||||
String FAIL = "1";
|
||||
|
||||
/**
|
||||
* 登录成功
|
||||
*/
|
||||
String LOGIN_SUCCESS = "Success";
|
||||
|
||||
/**
|
||||
* 注销
|
||||
*/
|
||||
String LOGOUT = "Logout";
|
||||
|
||||
/**
|
||||
* 注册
|
||||
*/
|
||||
String REGISTER = "Register";
|
||||
|
||||
/**
|
||||
* 登录失败
|
||||
*/
|
||||
String LOGIN_FAIL = "Error";
|
||||
|
||||
/**
|
||||
* 验证码有效期(分钟)
|
||||
*/
|
||||
Integer CAPTCHA_EXPIRATION = 2;
|
||||
|
||||
/**
|
||||
* 顶级部门id
|
||||
*/
|
||||
Long TOP_PARENT_ID = 0L;
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package org.dromara.common.core.constant;
|
||||
|
||||
/**
|
||||
* 全局的key常量 (业务无关的key)
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
public interface GlobalConstants {
|
||||
|
||||
/**
|
||||
* 全局 redis key (业务无关的key)
|
||||
*/
|
||||
String GLOBAL_REDIS_KEY = "global:";
|
||||
|
||||
/**
|
||||
* 验证码 redis key
|
||||
*/
|
||||
String CAPTCHA_CODE_KEY = GLOBAL_REDIS_KEY + "captcha_codes:";
|
||||
|
||||
/**
|
||||
* 防重提交 redis key
|
||||
*/
|
||||
String REPEAT_SUBMIT_KEY = GLOBAL_REDIS_KEY + "repeat_submit:";
|
||||
|
||||
/**
|
||||
* 限流 redis key
|
||||
*/
|
||||
String RATE_LIMIT_KEY = GLOBAL_REDIS_KEY + "rate_limit:";
|
||||
|
||||
/**
|
||||
* 三方认证 redis key
|
||||
*/
|
||||
String SOCIAL_AUTH_CODE_KEY = GLOBAL_REDIS_KEY + "social_auth_codes:";
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package org.dromara.common.core.constant;
|
||||
|
||||
/**
|
||||
* 返回状态码
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
public interface HttpStatus {
|
||||
/**
|
||||
* 操作成功
|
||||
*/
|
||||
int SUCCESS = 200;
|
||||
|
||||
/**
|
||||
* 对象创建成功
|
||||
*/
|
||||
int CREATED = 201;
|
||||
|
||||
/**
|
||||
* 请求已经被接受
|
||||
*/
|
||||
int ACCEPTED = 202;
|
||||
|
||||
/**
|
||||
* 操作已经执行成功,但是没有返回数据
|
||||
*/
|
||||
int NO_CONTENT = 204;
|
||||
|
||||
/**
|
||||
* 资源已被移除
|
||||
*/
|
||||
int MOVED_PERM = 301;
|
||||
|
||||
/**
|
||||
* 重定向
|
||||
*/
|
||||
int SEE_OTHER = 303;
|
||||
|
||||
/**
|
||||
* 资源没有被修改
|
||||
*/
|
||||
int NOT_MODIFIED = 304;
|
||||
|
||||
/**
|
||||
* 参数列表错误(缺少,格式不匹配)
|
||||
*/
|
||||
int BAD_REQUEST = 400;
|
||||
|
||||
/**
|
||||
* 未授权
|
||||
*/
|
||||
int UNAUTHORIZED = 401;
|
||||
|
||||
/**
|
||||
* 访问受限,授权过期
|
||||
*/
|
||||
int FORBIDDEN = 403;
|
||||
|
||||
/**
|
||||
* 资源,服务未找到
|
||||
*/
|
||||
int NOT_FOUND = 404;
|
||||
|
||||
/**
|
||||
* 不允许的http方法
|
||||
*/
|
||||
int BAD_METHOD = 405;
|
||||
|
||||
/**
|
||||
* 资源冲突,或者资源被锁
|
||||
*/
|
||||
int CONFLICT = 409;
|
||||
|
||||
/**
|
||||
* 不支持的数据,媒体类型
|
||||
*/
|
||||
int UNSUPPORTED_TYPE = 415;
|
||||
|
||||
/**
|
||||
* 系统内部错误
|
||||
*/
|
||||
int ERROR = 500;
|
||||
|
||||
/**
|
||||
* 接口未实现
|
||||
*/
|
||||
int NOT_IMPLEMENTED = 501;
|
||||
|
||||
/**
|
||||
* 系统警告消息
|
||||
*/
|
||||
int WARN = 601;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package org.dromara.common.core.constant;
|
||||
|
||||
import cn.hutool.core.lang.RegexPool;
|
||||
|
||||
/**
|
||||
* 常用正则表达式字符串
|
||||
* <p>
|
||||
* 常用正则表达式集合,更多正则见: https://any86.github.io/any-rule/
|
||||
*
|
||||
* @author Feng
|
||||
*/
|
||||
public interface RegexConstants extends RegexPool {
|
||||
|
||||
/**
|
||||
* 字典类型必须以字母开头,且只能为(小写字母,数字,下滑线)
|
||||
*/
|
||||
String DICTIONARY_TYPE = "^[a-z][a-z0-9_]*$";
|
||||
|
||||
/**
|
||||
* 权限标识必须符合 tool:build:list 格式,或者空字符串
|
||||
*/
|
||||
String PERMISSION_STRING = "^(|^[a-zA-Z0-9_]+:[a-zA-Z0-9_]+:[a-zA-Z0-9_]+)$";
|
||||
|
||||
/**
|
||||
* 身份证号码(后6位)
|
||||
*/
|
||||
String ID_CARD_LAST_6 = "^(([0-2][1-9])|10|20|30|31)\\d{3}[0-9Xx]$";
|
||||
|
||||
/**
|
||||
* QQ号码
|
||||
*/
|
||||
String QQ_NUMBER = "^[1-9][0-9]\\d{4,9}$";
|
||||
|
||||
/**
|
||||
* 邮政编码
|
||||
*/
|
||||
String POSTAL_CODE = "^[1-9]\\d{5}$";
|
||||
|
||||
/**
|
||||
* 注册账号
|
||||
*/
|
||||
String ACCOUNT = "^[a-zA-Z][a-zA-Z0-9_]{4,15}$";
|
||||
|
||||
/**
|
||||
* 密码:包含至少8个字符,包括大写字母、小写字母、数字和特殊字符
|
||||
*/
|
||||
String PASSWORD = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]{8,}$";
|
||||
|
||||
/**
|
||||
* 通用状态(0表示正常,1表示停用)
|
||||
*/
|
||||
String STATUS = "^[01]$";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package org.dromara.common.core.constant;
|
||||
|
||||
/**
|
||||
* 用户常量信息
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
public interface SystemConstants {
|
||||
|
||||
/**
|
||||
* 正常状态
|
||||
*/
|
||||
String NORMAL = "0";
|
||||
|
||||
/**
|
||||
* 异常状态
|
||||
*/
|
||||
String DISABLE = "1";
|
||||
|
||||
/**
|
||||
* 是否为系统默认(是)
|
||||
*/
|
||||
String YES = "Y";
|
||||
|
||||
/**
|
||||
* 是否菜单外链(是)
|
||||
*/
|
||||
String YES_FRAME = "0";
|
||||
|
||||
/**
|
||||
* 是否菜单外链(否)
|
||||
*/
|
||||
String NO_FRAME = "1";
|
||||
|
||||
/**
|
||||
* 菜单类型(目录)
|
||||
*/
|
||||
String TYPE_DIR = "M";
|
||||
|
||||
/**
|
||||
* 菜单类型(菜单)
|
||||
*/
|
||||
String TYPE_MENU = "C";
|
||||
|
||||
/**
|
||||
* 菜单类型(按钮)
|
||||
*/
|
||||
String TYPE_BUTTON = "F";
|
||||
|
||||
/**
|
||||
* Layout组件标识
|
||||
*/
|
||||
String LAYOUT = "Layout";
|
||||
|
||||
/**
|
||||
* ParentView组件标识
|
||||
*/
|
||||
String PARENT_VIEW = "ParentView";
|
||||
|
||||
/**
|
||||
* InnerLink组件标识
|
||||
*/
|
||||
String INNER_LINK = "InnerLink";
|
||||
|
||||
/**
|
||||
* 超级管理员ID
|
||||
*/
|
||||
Long SUPER_ADMIN_ID = 1L;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package org.dromara.common.core.constant;
|
||||
|
||||
/**
|
||||
* 租户常量信息
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
public interface TenantConstants {
|
||||
|
||||
/**
|
||||
* 超级管理员ID
|
||||
*/
|
||||
Long SUPER_ADMIN_ID = 1L;
|
||||
|
||||
/**
|
||||
* 超级管理员角色 roleKey
|
||||
*/
|
||||
String SUPER_ADMIN_ROLE_KEY = "superadmin";
|
||||
|
||||
/**
|
||||
* 租户管理员角色 roleKey
|
||||
*/
|
||||
String TENANT_ADMIN_ROLE_KEY = "admin";
|
||||
|
||||
/**
|
||||
* 租户管理员角色名称
|
||||
*/
|
||||
String TENANT_ADMIN_ROLE_NAME = "管理员";
|
||||
|
||||
/**
|
||||
* 默认租户ID
|
||||
*/
|
||||
String DEFAULT_TENANT_ID = "000000";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
package org.dromara.common.core.domain;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.dromara.common.core.constant.HttpStatus;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 响应信息主体
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class R<T> implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 成功
|
||||
*/
|
||||
public static final int SUCCESS = 200;
|
||||
|
||||
/**
|
||||
* 失败
|
||||
*/
|
||||
public static final int FAIL = 500;
|
||||
|
||||
private int code;
|
||||
|
||||
private String msg;
|
||||
|
||||
private T data;
|
||||
|
||||
public static <T> R<T> ok() {
|
||||
return restResult(null, SUCCESS, "操作成功");
|
||||
}
|
||||
|
||||
public static <T> R<T> ok(T data) {
|
||||
return restResult(data, SUCCESS, "操作成功");
|
||||
}
|
||||
|
||||
public static <T> R<T> ok(String msg) {
|
||||
return restResult(null, SUCCESS, msg);
|
||||
}
|
||||
|
||||
public static <T> R<T> ok(String msg, T data) {
|
||||
return restResult(data, SUCCESS, msg);
|
||||
}
|
||||
|
||||
public static <T> R<T> fail() {
|
||||
return restResult(null, FAIL, "操作失败");
|
||||
}
|
||||
|
||||
public static <T> R<T> fail(String msg) {
|
||||
return restResult(null, FAIL, msg);
|
||||
}
|
||||
|
||||
public static <T> R<T> fail(T data) {
|
||||
return restResult(data, FAIL, "操作失败");
|
||||
}
|
||||
|
||||
public static <T> R<T> fail(String msg, T data) {
|
||||
return restResult(data, FAIL, msg);
|
||||
}
|
||||
|
||||
public static <T> R<T> fail(int code, String msg) {
|
||||
return restResult(null, code, msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回警告消息
|
||||
*
|
||||
* @param msg 返回内容
|
||||
* @return 警告消息
|
||||
*/
|
||||
public static <T> R<T> warn(String msg) {
|
||||
return restResult(null, HttpStatus.WARN, msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回警告消息
|
||||
*
|
||||
* @param msg 返回内容
|
||||
* @param data 数据对象
|
||||
* @return 警告消息
|
||||
*/
|
||||
public static <T> R<T> warn(String msg, T data) {
|
||||
return restResult(data, HttpStatus.WARN, msg);
|
||||
}
|
||||
|
||||
private static <T> R<T> restResult(T data, int code, String msg) {
|
||||
R<T> r = new R<>();
|
||||
r.setCode(code);
|
||||
r.setData(data);
|
||||
r.setMsg(msg);
|
||||
return r;
|
||||
}
|
||||
|
||||
public static <T> Boolean isError(R<T> ret) {
|
||||
return !isSuccess(ret);
|
||||
}
|
||||
|
||||
public static <T> Boolean isSuccess(R<T> ret) {
|
||||
return R.SUCCESS == ret.getCode();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package org.dromara.common.core.domain.dto;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* OSS对象
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class OssDTO implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 对象存储主键
|
||||
*/
|
||||
private Long ossId;
|
||||
|
||||
/**
|
||||
* 文件名
|
||||
*/
|
||||
private String fileName;
|
||||
|
||||
/**
|
||||
* 原名
|
||||
*/
|
||||
private String originalName;
|
||||
|
||||
/**
|
||||
* 文件后缀名
|
||||
*/
|
||||
private String fileSuffix;
|
||||
|
||||
/**
|
||||
* URL地址
|
||||
*/
|
||||
private String url;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package org.dromara.common.core.domain.dto;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 角色
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class RoleDTO implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 角色ID
|
||||
*/
|
||||
private Long roleId;
|
||||
|
||||
/**
|
||||
* 角色名称
|
||||
*/
|
||||
private String roleName;
|
||||
|
||||
/**
|
||||
* 角色权限
|
||||
*/
|
||||
private String roleKey;
|
||||
|
||||
/**
|
||||
* 数据范围(1:所有数据权限;2:自定义数据权限;3:本部门数据权限;4:本部门及以下数据权限;5:仅本人数据权限)
|
||||
*/
|
||||
private String dataScope;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package org.dromara.common.core.domain.dto;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
|
||||
/**
|
||||
* 用户
|
||||
*
|
||||
* @author Michelle.Chung
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class UserDTO implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 部门ID
|
||||
*/
|
||||
private Long deptId;
|
||||
|
||||
/**
|
||||
* 用户账号
|
||||
*/
|
||||
private String userName;
|
||||
|
||||
/**
|
||||
* 用户昵称
|
||||
*/
|
||||
private String nickName;
|
||||
|
||||
/**
|
||||
* 用户类型(sys_user系统用户)
|
||||
*/
|
||||
private String userType;
|
||||
|
||||
/**
|
||||
* 用户邮箱
|
||||
*/
|
||||
private String email;
|
||||
|
||||
/**
|
||||
* 手机号码
|
||||
*/
|
||||
private String phonenumber;
|
||||
|
||||
/**
|
||||
* 用户性别(0男 1女 2未知)
|
||||
*/
|
||||
private String sex;
|
||||
|
||||
/**
|
||||
* 帐号状态(0正常 1停用)
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private Date createTime;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package org.dromara.common.core.domain.dto;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 当前在线会话
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class UserOnlineDTO implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 会话编号
|
||||
*/
|
||||
private String tokenId;
|
||||
|
||||
/**
|
||||
* 部门名称
|
||||
*/
|
||||
private String deptName;
|
||||
|
||||
/**
|
||||
* 用户名称
|
||||
*/
|
||||
private String userName;
|
||||
|
||||
/**
|
||||
* 客户端
|
||||
*/
|
||||
private String clientKey;
|
||||
|
||||
/**
|
||||
* 设备类型
|
||||
*/
|
||||
private String deviceType;
|
||||
|
||||
/**
|
||||
* 登录IP地址
|
||||
*/
|
||||
private String ipaddr;
|
||||
|
||||
/**
|
||||
* 登录地址
|
||||
*/
|
||||
private String loginLocation;
|
||||
|
||||
/**
|
||||
* 浏览器类型
|
||||
*/
|
||||
private String browser;
|
||||
|
||||
/**
|
||||
* 操作系统
|
||||
*/
|
||||
private String os;
|
||||
|
||||
/**
|
||||
* 登录时间
|
||||
*/
|
||||
private Long loginTime;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package org.dromara.common.core.domain.event;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 总体流程监听
|
||||
*
|
||||
* @author may
|
||||
*/
|
||||
|
||||
@Data
|
||||
public class ProcessEvent implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 流程定义key
|
||||
*/
|
||||
private String key;
|
||||
|
||||
/**
|
||||
* 业务id
|
||||
*/
|
||||
private String businessKey;
|
||||
|
||||
/**
|
||||
* 状态
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 当为true时为申请人节点办理
|
||||
*/
|
||||
private boolean submit;
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package org.dromara.common.core.domain.event;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 流程办理监听
|
||||
*
|
||||
* @author may
|
||||
*/
|
||||
|
||||
@Data
|
||||
public class ProcessTaskEvent implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 流程定义key
|
||||
*/
|
||||
private String key;
|
||||
|
||||
/**
|
||||
* 审批节点key
|
||||
*/
|
||||
private String taskDefinitionKey;
|
||||
|
||||
/**
|
||||
* 任务id
|
||||
*/
|
||||
private String taskId;
|
||||
|
||||
/**
|
||||
* 业务id
|
||||
*/
|
||||
private String businessKey;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package org.dromara.common.core.domain.model;
|
||||
|
||||
import jakarta.validation.constraints.Email;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 邮件登录对象
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class EmailLoginBody extends LoginBody {
|
||||
|
||||
/**
|
||||
* 邮箱
|
||||
*/
|
||||
@NotBlank(message = "{user.email.not.blank}")
|
||||
@Email(message = "{user.email.not.valid}")
|
||||
private String email;
|
||||
|
||||
/**
|
||||
* 邮箱code
|
||||
*/
|
||||
@NotBlank(message = "{email.code.not.blank}")
|
||||
private String emailCode;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package org.dromara.common.core.domain.model;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 用户登录对象
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
|
||||
@Data
|
||||
public class LoginBody implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 客户端id
|
||||
*/
|
||||
@NotBlank(message = "{auth.clientid.not.blank}")
|
||||
private String clientId;
|
||||
|
||||
/**
|
||||
* 授权类型
|
||||
*/
|
||||
@NotBlank(message = "{auth.grant.type.not.blank}")
|
||||
private String grantType;
|
||||
|
||||
/**
|
||||
* 租户ID
|
||||
*/
|
||||
private String tenantId;
|
||||
|
||||
/**
|
||||
* 验证码
|
||||
*/
|
||||
private String code;
|
||||
|
||||
/**
|
||||
* 唯一标识
|
||||
*/
|
||||
private String uuid;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
package org.dromara.common.core.domain.model;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.dromara.common.core.domain.dto.RoleDTO;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 登录用户身份权限
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class LoginUser implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 租户ID
|
||||
*/
|
||||
private String tenantId;
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 部门ID
|
||||
*/
|
||||
private Long deptId;
|
||||
|
||||
/**
|
||||
* 部门类别编码
|
||||
*/
|
||||
private String deptCategory;
|
||||
|
||||
/**
|
||||
* 部门名
|
||||
*/
|
||||
private String deptName;
|
||||
|
||||
/**
|
||||
* 用户唯一标识
|
||||
*/
|
||||
private String token;
|
||||
|
||||
/**
|
||||
* 用户类型
|
||||
*/
|
||||
private String userType;
|
||||
|
||||
/**
|
||||
* 登录时间
|
||||
*/
|
||||
private Long loginTime;
|
||||
|
||||
/**
|
||||
* 过期时间
|
||||
*/
|
||||
private Long expireTime;
|
||||
|
||||
/**
|
||||
* 登录IP地址
|
||||
*/
|
||||
private String ipaddr;
|
||||
|
||||
/**
|
||||
* 登录地点
|
||||
*/
|
||||
private String loginLocation;
|
||||
|
||||
/**
|
||||
* 浏览器类型
|
||||
*/
|
||||
private String browser;
|
||||
|
||||
/**
|
||||
* 操作系统
|
||||
*/
|
||||
private String os;
|
||||
|
||||
/**
|
||||
* 菜单权限
|
||||
*/
|
||||
private Set<String> menuPermission;
|
||||
|
||||
/**
|
||||
* 角色权限
|
||||
*/
|
||||
private Set<String> rolePermission;
|
||||
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* 用户昵称
|
||||
*/
|
||||
private String nickname;
|
||||
|
||||
/**
|
||||
* 角色对象
|
||||
*/
|
||||
private List<RoleDTO> roles;
|
||||
|
||||
/**
|
||||
* 数据权限 当前角色ID
|
||||
*/
|
||||
private Long roleId;
|
||||
|
||||
/**
|
||||
* 客户端
|
||||
*/
|
||||
private String clientKey;
|
||||
|
||||
/**
|
||||
* 设备类型
|
||||
*/
|
||||
private String deviceType;
|
||||
|
||||
/**
|
||||
* 真实姓名
|
||||
*/
|
||||
private String realName;
|
||||
|
||||
/**
|
||||
* 身份(1=员工,2=技术,3=客服,4=分图员)
|
||||
*/
|
||||
private Integer identity;
|
||||
|
||||
/**
|
||||
* 获取登录id
|
||||
*/
|
||||
public String getLoginId() {
|
||||
if (userType == null) {
|
||||
throw new IllegalArgumentException("用户类型不能为空");
|
||||
}
|
||||
if (userId == null) {
|
||||
throw new IllegalArgumentException("用户ID不能为空");
|
||||
}
|
||||
return userType + ":" + userId;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package org.dromara.common.core.domain.model;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.hibernate.validator.constraints.Length;
|
||||
|
||||
/**
|
||||
* 密码登录对象
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class PasswordLoginBody extends LoginBody {
|
||||
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
@NotBlank(message = "{user.username.not.blank}")
|
||||
@Length(min = 2, max = 20, message = "{user.username.length.valid}")
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* 用户密码
|
||||
*/
|
||||
@NotBlank(message = "{user.password.not.blank}")
|
||||
@Length(min = 5, max = 20, message = "{user.password.length.valid}")
|
||||
private String password;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package org.dromara.common.core.domain.model;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.hibernate.validator.constraints.Length;
|
||||
|
||||
/**
|
||||
* 用户注册对象
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class RegisterBody extends LoginBody {
|
||||
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
@NotBlank(message = "{user.username.not.blank}")
|
||||
@Length(min = 2, max = 20, message = "{user.username.length.valid}")
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* 用户密码
|
||||
*/
|
||||
@NotBlank(message = "{user.password.not.blank}")
|
||||
@Length(min = 5, max = 20, message = "{user.password.length.valid}")
|
||||
private String password;
|
||||
|
||||
private String userType;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package org.dromara.common.core.domain.model;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 短信登录对象
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class SmsLoginBody extends LoginBody {
|
||||
|
||||
/**
|
||||
* 手机号
|
||||
*/
|
||||
@NotBlank(message = "{user.phonenumber.not.blank}")
|
||||
private String phonenumber;
|
||||
|
||||
/**
|
||||
* 短信code
|
||||
*/
|
||||
@NotBlank(message = "{sms.code.not.blank}")
|
||||
private String smsCode;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package org.dromara.common.core.domain.model;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 三方登录对象
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class SocialLoginBody extends LoginBody {
|
||||
|
||||
/**
|
||||
* 第三方登录平台
|
||||
*/
|
||||
@NotBlank(message = "{social.source.not.blank}")
|
||||
private String source;
|
||||
|
||||
/**
|
||||
* 第三方登录code
|
||||
*/
|
||||
@NotBlank(message = "{social.code.not.blank}")
|
||||
private String socialCode;
|
||||
|
||||
/**
|
||||
* 第三方登录socialState
|
||||
*/
|
||||
@NotBlank(message = "{social.state.not.blank}")
|
||||
private String socialState;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package org.dromara.common.core.domain.model;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 三方登录对象
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class XcxLoginBody extends LoginBody {
|
||||
|
||||
/**
|
||||
* 小程序id(多个小程序时使用)
|
||||
*/
|
||||
private String appid;
|
||||
|
||||
/**
|
||||
* 小程序code
|
||||
*/
|
||||
@NotBlank(message = "{xcx.code.not.blank}")
|
||||
private String xcxCode;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package org.dromara.common.core.domain.model;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
/**
|
||||
* 小程序登录用户身份权限
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@NoArgsConstructor
|
||||
public class XcxLoginUser extends LoginUser {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* openid
|
||||
*/
|
||||
private String openid;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
package org.dromara.common.core.enums;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 业务状态枚举
|
||||
*
|
||||
* @author may
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum BusinessStatusEnum {
|
||||
/**
|
||||
* 已撤销
|
||||
*/
|
||||
CANCEL("cancel", "已撤销"),
|
||||
/**
|
||||
* 草稿
|
||||
*/
|
||||
DRAFT("draft", "草稿"),
|
||||
/**
|
||||
* 待审核
|
||||
*/
|
||||
WAITING("waiting", "待审核"),
|
||||
/**
|
||||
* 已完成
|
||||
*/
|
||||
FINISH("finish", "已完成"),
|
||||
/**
|
||||
* 已作废
|
||||
*/
|
||||
INVALID("invalid", "已作废"),
|
||||
/**
|
||||
* 已退回
|
||||
*/
|
||||
BACK("back", "已退回"),
|
||||
/**
|
||||
* 已终止
|
||||
*/
|
||||
TERMINATION("termination", "已终止");
|
||||
|
||||
/**
|
||||
* 状态
|
||||
*/
|
||||
private final String status;
|
||||
|
||||
/**
|
||||
* 描述
|
||||
*/
|
||||
private final String desc;
|
||||
|
||||
/**
|
||||
* 获取业务状态
|
||||
*
|
||||
* @param status 状态
|
||||
*/
|
||||
public static String findByStatus(String status) {
|
||||
if (StringUtils.isBlank(status)) {
|
||||
return StrUtil.EMPTY;
|
||||
}
|
||||
return Arrays.stream(BusinessStatusEnum.values())
|
||||
.filter(statusEnum -> statusEnum.getStatus().equals(status))
|
||||
.findFirst()
|
||||
.map(BusinessStatusEnum::getDesc)
|
||||
.orElse(StrUtil.EMPTY);
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动流程校验
|
||||
*
|
||||
* @param status 状态
|
||||
*/
|
||||
public static void checkStartStatus(String status) {
|
||||
if (WAITING.getStatus().equals(status)) {
|
||||
throw new ServiceException("该单据已提交过申请,正在审批中!");
|
||||
} else if (FINISH.getStatus().equals(status)) {
|
||||
throw new ServiceException("该单据已完成申请!");
|
||||
} else if (INVALID.getStatus().equals(status)) {
|
||||
throw new ServiceException("该单据已作废!");
|
||||
} else if (TERMINATION.getStatus().equals(status)) {
|
||||
throw new ServiceException("该单据已终止!");
|
||||
} else if (StringUtils.isBlank(status)) {
|
||||
throw new ServiceException("流程状态为空!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 撤销流程校验
|
||||
*
|
||||
* @param status 状态
|
||||
*/
|
||||
public static void checkCancelStatus(String status) {
|
||||
if (CANCEL.getStatus().equals(status)) {
|
||||
throw new ServiceException("该单据已撤销!");
|
||||
} else if (FINISH.getStatus().equals(status)) {
|
||||
throw new ServiceException("该单据已完成申请!");
|
||||
} else if (INVALID.getStatus().equals(status)) {
|
||||
throw new ServiceException("该单据已作废!");
|
||||
} else if (TERMINATION.getStatus().equals(status)) {
|
||||
throw new ServiceException("该单据已终止!");
|
||||
} else if (BACK.getStatus().equals(status)) {
|
||||
throw new ServiceException("该单据已退回!");
|
||||
} else if (StringUtils.isBlank(status)) {
|
||||
throw new ServiceException("流程状态为空!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 驳回流程校验
|
||||
*
|
||||
* @param status 状态
|
||||
*/
|
||||
public static void checkBackStatus(String status) {
|
||||
if (BACK.getStatus().equals(status)) {
|
||||
throw new ServiceException("该单据已退回!");
|
||||
} else if (FINISH.getStatus().equals(status)) {
|
||||
throw new ServiceException("该单据已完成申请!");
|
||||
} else if (INVALID.getStatus().equals(status)) {
|
||||
throw new ServiceException("该单据已作废!");
|
||||
} else if (TERMINATION.getStatus().equals(status)) {
|
||||
throw new ServiceException("该单据已终止!");
|
||||
} else if (CANCEL.getStatus().equals(status)) {
|
||||
throw new ServiceException("该单据已撤销!");
|
||||
} else if (StringUtils.isBlank(status)) {
|
||||
throw new ServiceException("流程状态为空!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 作废,终止流程校验
|
||||
*
|
||||
* @param status 状态
|
||||
*/
|
||||
public static void checkInvalidStatus(String status) {
|
||||
if (FINISH.getStatus().equals(status)) {
|
||||
throw new ServiceException("该单据已完成申请!");
|
||||
} else if (INVALID.getStatus().equals(status)) {
|
||||
throw new ServiceException("该单据已作废!");
|
||||
} else if (TERMINATION.getStatus().equals(status)) {
|
||||
throw new ServiceException("该单据已终止!");
|
||||
} else if (StringUtils.isBlank(status)) {
|
||||
throw new ServiceException("流程状态为空!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
package org.dromara.common.core.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 设备类型
|
||||
* 针对一套 用户体系
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum DeviceType {
|
||||
|
||||
/**
|
||||
* pc端
|
||||
*/
|
||||
PC("pc"),
|
||||
|
||||
/**
|
||||
* app端
|
||||
*/
|
||||
APP("app"),
|
||||
|
||||
/**
|
||||
* 小程序端
|
||||
*/
|
||||
XCX("xcx"),
|
||||
|
||||
/**
|
||||
* social第三方端
|
||||
*/
|
||||
SOCIAL("social");
|
||||
|
||||
private final String device;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package org.dromara.common.core.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 登录类型
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum LoginType {
|
||||
|
||||
/**
|
||||
* 密码登录
|
||||
*/
|
||||
PASSWORD("user.password.retry.limit.exceed", "user.password.retry.limit.count"),
|
||||
|
||||
/**
|
||||
* 短信登录
|
||||
*/
|
||||
SMS("sms.code.retry.limit.exceed", "sms.code.retry.limit.count"),
|
||||
|
||||
/**
|
||||
* 邮箱登录
|
||||
*/
|
||||
EMAIL("email.code.retry.limit.exceed", "email.code.retry.limit.count"),
|
||||
|
||||
/**
|
||||
* 小程序登录
|
||||
*/
|
||||
XCX("", "");
|
||||
|
||||
/**
|
||||
* 登录重试超出限制提示
|
||||
*/
|
||||
final String retryLimitExceed;
|
||||
|
||||
/**
|
||||
* 登录重试限制计数提示
|
||||
*/
|
||||
final String retryLimitCount;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package org.dromara.common.core.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 用户状态
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum UserStatus {
|
||||
/**
|
||||
* 正常
|
||||
*/
|
||||
OK("0", "正常"),
|
||||
/**
|
||||
* 停用
|
||||
*/
|
||||
DISABLE("1", "停用"),
|
||||
/**
|
||||
* 删除
|
||||
*/
|
||||
DELETED("2", "删除");
|
||||
|
||||
private final String code;
|
||||
private final String info;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package org.dromara.common.core.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
|
||||
/**
|
||||
* 设备类型
|
||||
* 针对多套 用户体系
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum UserType {
|
||||
|
||||
/**
|
||||
* pc端
|
||||
*/
|
||||
SYS_USER("sys_user"),
|
||||
|
||||
/**
|
||||
* app端
|
||||
*/
|
||||
APP_USER("app_user");
|
||||
|
||||
private final String userType;
|
||||
|
||||
public static UserType getUserType(String str) {
|
||||
for (UserType value : values()) {
|
||||
if (StringUtils.contains(str, value.getUserType())) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
throw new RuntimeException("'UserType' not found By " + str);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package org.dromara.common.core.exception;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
/**
|
||||
* 业务异常
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public final class ServiceException extends RuntimeException {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 错误码
|
||||
*/
|
||||
private Integer code;
|
||||
|
||||
/**
|
||||
* 错误提示
|
||||
*/
|
||||
private String message;
|
||||
|
||||
/**
|
||||
* 错误明细,内部调试错误
|
||||
*/
|
||||
private String detailMessage;
|
||||
|
||||
public ServiceException(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public ServiceException(String message, Integer code) {
|
||||
this.message = message;
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public ServiceException setMessage(String message) {
|
||||
this.message = message;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ServiceException setDetailMessage(String detailMessage) {
|
||||
this.detailMessage = detailMessage;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package org.dromara.common.core.exception;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
/**
|
||||
* sse 特制异常
|
||||
*
|
||||
* @author LionLi
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public final class SseException extends RuntimeException {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 错误码
|
||||
*/
|
||||
private Integer code;
|
||||
|
||||
/**
|
||||
* 错误提示
|
||||
*/
|
||||
private String message;
|
||||
|
||||
/**
|
||||
* 错误明细,内部调试错误
|
||||
*/
|
||||
private String detailMessage;
|
||||
|
||||
public SseException(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public SseException(String message, Integer code) {
|
||||
this.message = message;
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public SseException setMessage(String message) {
|
||||
this.message = message;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SseException setDetailMessage(String detailMessage) {
|
||||
this.detailMessage = detailMessage;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package org.dromara.common.core.exception.base;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.dromara.common.core.utils.MessageUtils;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
/**
|
||||
* 基础异常
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class BaseException extends RuntimeException {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 所属模块
|
||||
*/
|
||||
private String module;
|
||||
|
||||
/**
|
||||
* 错误码
|
||||
*/
|
||||
private String code;
|
||||
|
||||
/**
|
||||
* 错误码对应的参数
|
||||
*/
|
||||
private Object[] args;
|
||||
|
||||
/**
|
||||
* 错误消息
|
||||
*/
|
||||
private String defaultMessage;
|
||||
|
||||
public BaseException(String module, String code, Object[] args) {
|
||||
this(module, code, args, null);
|
||||
}
|
||||
|
||||
public BaseException(String module, String defaultMessage) {
|
||||
this(module, null, null, defaultMessage);
|
||||
}
|
||||
|
||||
public BaseException(String code, Object[] args) {
|
||||
this(null, code, args, null);
|
||||
}
|
||||
|
||||
public BaseException(String defaultMessage) {
|
||||
this(null, null, null, defaultMessage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
String message = null;
|
||||
if (!StringUtils.isEmpty(code)) {
|
||||
message = MessageUtils.message(code, args);
|
||||
}
|
||||
if (message == null) {
|
||||
message = defaultMessage;
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package org.dromara.common.core.exception.file;
|
||||
|
||||
import org.dromara.common.core.exception.base.BaseException;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
/**
|
||||
* 文件信息异常类
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class FileException extends BaseException {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public FileException(String code, Object[] args) {
|
||||
super("file", code, args, null);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package org.dromara.common.core.exception.file;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
/**
|
||||
* 文件名称超长限制异常类
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class FileNameLengthLimitExceededException extends FileException {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public FileNameLengthLimitExceededException(int defaultFileNameLength) {
|
||||
super("upload.filename.exceed.length", new Object[]{defaultFileNameLength});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package org.dromara.common.core.exception.file;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
/**
|
||||
* 文件名大小限制异常类
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class FileSizeLimitExceededException extends FileException {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public FileSizeLimitExceededException(long defaultMaxSize) {
|
||||
super("upload.exceed.maxSize", new Object[]{defaultMaxSize});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package org.dromara.common.core.exception.user;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
/**
|
||||
* 验证码错误异常类
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class CaptchaException extends UserException {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public CaptchaException() {
|
||||
super("user.jcaptcha.error");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package org.dromara.common.core.exception.user;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
/**
|
||||
* 验证码失效异常类
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class CaptchaExpireException extends UserException {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public CaptchaExpireException() {
|
||||
super("user.jcaptcha.expire");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package org.dromara.common.core.exception.user;
|
||||
|
||||
import org.dromara.common.core.exception.base.BaseException;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
/**
|
||||
* 用户信息异常类
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class UserException extends BaseException {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public UserException(String code, Object... args) {
|
||||
super("user", code, args, null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package org.dromara.common.core.factory;
|
||||
|
||||
import cn.hutool.core.lang.PatternPool;
|
||||
import org.dromara.common.core.constant.RegexConstants;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* 正则表达式模式池工厂
|
||||
* <p>初始化的时候将正则表达式加入缓存池当中</p>
|
||||
* <p>提高正则表达式的性能,避免重复编译相同的正则表达式</p>
|
||||
*
|
||||
* @author 21001
|
||||
*/
|
||||
public class RegexPatternPoolFactory extends PatternPool {
|
||||
|
||||
/**
|
||||
* 字典类型必须以字母开头,且只能为(小写字母,数字,下滑线)
|
||||
*/
|
||||
public static final Pattern DICTIONARY_TYPE = get(RegexConstants.DICTIONARY_TYPE);
|
||||
|
||||
/**
|
||||
* 身份证号码(后6位)
|
||||
*/
|
||||
public static final Pattern ID_CARD_LAST_6 = get(RegexConstants.ID_CARD_LAST_6);
|
||||
|
||||
/**
|
||||
* QQ号码
|
||||
*/
|
||||
public static final Pattern QQ_NUMBER = get(RegexConstants.QQ_NUMBER);
|
||||
|
||||
/**
|
||||
* 邮政编码
|
||||
*/
|
||||
public static final Pattern POSTAL_CODE = get(RegexConstants.POSTAL_CODE);
|
||||
|
||||
/**
|
||||
* 注册账号
|
||||
*/
|
||||
public static final Pattern ACCOUNT = get(RegexConstants.ACCOUNT);
|
||||
|
||||
/**
|
||||
* 密码:包含至少8个字符,包括大写字母、小写字母、数字和特殊字符
|
||||
*/
|
||||
public static final Pattern PASSWORD = get(RegexConstants.PASSWORD);
|
||||
|
||||
/**
|
||||
* 通用状态(0表示正常,1表示停用)
|
||||
*/
|
||||
public static final Pattern STATUS = get(RegexConstants.STATUS);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package org.dromara.common.core.factory;
|
||||
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
|
||||
import org.springframework.core.env.PropertiesPropertySource;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
import org.springframework.core.io.support.DefaultPropertySourceFactory;
|
||||
import org.springframework.core.io.support.EncodedResource;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* yml 配置源工厂
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
public class YmlPropertySourceFactory extends DefaultPropertySourceFactory {
|
||||
|
||||
@Override
|
||||
public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {
|
||||
String sourceName = resource.getResource().getFilename();
|
||||
if (StringUtils.isNotBlank(sourceName) && StringUtils.endsWithAny(sourceName, ".yml", ".yaml")) {
|
||||
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
|
||||
factory.setResources(resource.getResource());
|
||||
factory.afterPropertiesSet();
|
||||
return new PropertiesPropertySource(sourceName, factory.getObject());
|
||||
}
|
||||
return super.createPropertySource(name, resource);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package org.dromara.common.core.service;
|
||||
|
||||
/**
|
||||
* 通用 参数配置服务
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
public interface ConfigService {
|
||||
|
||||
/**
|
||||
* 根据参数 key 获取参数值
|
||||
*
|
||||
* @param configKey 参数 key
|
||||
* @return 参数值
|
||||
*/
|
||||
String getConfigValue(String configKey);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package org.dromara.common.core.service;
|
||||
|
||||
/**
|
||||
* 通用 部门服务
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
public interface DeptService {
|
||||
|
||||
/**
|
||||
* 通过部门ID查询部门名称
|
||||
*
|
||||
* @param deptIds 部门ID串逗号分隔
|
||||
* @return 部门名称串逗号分隔
|
||||
*/
|
||||
String selectDeptNameByIds(String deptIds);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package org.dromara.common.core.service;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 通用 字典服务
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
public interface DictService {
|
||||
|
||||
/**
|
||||
* 分隔符
|
||||
*/
|
||||
String SEPARATOR = ",";
|
||||
|
||||
/**
|
||||
* 根据字典类型和字典值获取字典标签
|
||||
*
|
||||
* @param dictType 字典类型
|
||||
* @param dictValue 字典值
|
||||
* @return 字典标签
|
||||
*/
|
||||
default String getDictLabel(String dictType, String dictValue) {
|
||||
return getDictLabel(dictType, dictValue, SEPARATOR);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据字典类型和字典标签获取字典值
|
||||
*
|
||||
* @param dictType 字典类型
|
||||
* @param dictLabel 字典标签
|
||||
* @return 字典值
|
||||
*/
|
||||
default String getDictValue(String dictType, String dictLabel) {
|
||||
return getDictValue(dictType, dictLabel, SEPARATOR);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据字典类型和字典值获取字典标签
|
||||
*
|
||||
* @param dictType 字典类型
|
||||
* @param dictValue 字典值
|
||||
* @param separator 分隔符
|
||||
* @return 字典标签
|
||||
*/
|
||||
String getDictLabel(String dictType, String dictValue, String separator);
|
||||
|
||||
/**
|
||||
* 根据字典类型和字典标签获取字典值
|
||||
*
|
||||
* @param dictType 字典类型
|
||||
* @param dictLabel 字典标签
|
||||
* @param separator 分隔符
|
||||
* @return 字典值
|
||||
*/
|
||||
String getDictValue(String dictType, String dictLabel, String separator);
|
||||
|
||||
/**
|
||||
* 获取字典下所有的字典值与标签
|
||||
*
|
||||
* @param dictType 字典类型
|
||||
* @return dictValue为key,dictLabel为值组成的Map
|
||||
*/
|
||||
Map<String, String> getAllDictByDictType(String dictType);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package org.dromara.common.core.service;
|
||||
|
||||
import org.dromara.common.core.domain.dto.OssDTO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 通用 OSS服务
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
public interface OssService {
|
||||
|
||||
/**
|
||||
* 通过ossId查询对应的url
|
||||
*
|
||||
* @param ossIds ossId串逗号分隔
|
||||
* @return url串逗号分隔
|
||||
*/
|
||||
String selectUrlByIds(String ossIds);
|
||||
|
||||
/**
|
||||
* 通过ossId查询列表
|
||||
*
|
||||
* @param ossIds ossId串逗号分隔
|
||||
* @return 列表
|
||||
*/
|
||||
List<OssDTO> selectByIds(String ossIds);
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package org.dromara.common.core.service;
|
||||
|
||||
import org.dromara.common.core.domain.dto.UserDTO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 通用 用户服务
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
public interface UserService {
|
||||
|
||||
/**
|
||||
* 通过用户ID查询用户账户
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 用户账户
|
||||
*/
|
||||
String selectUserNameById(Long userId);
|
||||
|
||||
/**
|
||||
* 通过用户ID查询用户账户
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 用户名称
|
||||
*/
|
||||
String selectNicknameById(Long userId);
|
||||
|
||||
/**
|
||||
* 通过用户ID查询用户账户
|
||||
*
|
||||
* @param userIds 用户ID 多个用逗号隔开
|
||||
* @return 用户名称
|
||||
*/
|
||||
String selectNicknameByIds(String userIds);
|
||||
|
||||
/**
|
||||
* 通过用户ID查询用户手机号
|
||||
*
|
||||
* @param userId 用户id
|
||||
* @return 用户手机号
|
||||
*/
|
||||
String selectPhonenumberById(Long userId);
|
||||
|
||||
/**
|
||||
* 通过用户ID查询用户邮箱
|
||||
*
|
||||
* @param userId 用户id
|
||||
* @return 用户邮箱
|
||||
*/
|
||||
String selectEmailById(Long userId);
|
||||
|
||||
/**
|
||||
* 通过用户ID查询用户列表
|
||||
*
|
||||
* @param userIds 用户ids
|
||||
* @return 用户列表
|
||||
*/
|
||||
List<UserDTO> selectListByIds(List<Long> userIds);
|
||||
|
||||
/**
|
||||
* 通过角色ID查询用户ID
|
||||
*
|
||||
* @param roleIds 角色ids
|
||||
* @return 用户ids
|
||||
*/
|
||||
List<Long> selectUserIdsByRoleIds(List<Long> roleIds);
|
||||
|
||||
/**
|
||||
* 通过角色ID查询用户
|
||||
*
|
||||
* @param roleIds 角色ids
|
||||
* @return 用户
|
||||
*/
|
||||
List<UserDTO> selectUsersByRoleIds(List<Long> roleIds);
|
||||
|
||||
/**
|
||||
* 通过部门ID查询用户
|
||||
*
|
||||
* @param deptIds 部门ids
|
||||
* @return 用户
|
||||
*/
|
||||
List<UserDTO> selectUsersByDeptIds(List<Long> deptIds);
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package org.dromara.common.core.service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 通用 工作流服务
|
||||
*
|
||||
* @author may
|
||||
*/
|
||||
public interface WorkflowService {
|
||||
|
||||
/**
|
||||
* 运行中的实例 删除程实例,删除历史记录,删除业务与流程关联信息
|
||||
*
|
||||
* @param businessKeys 业务id
|
||||
* @return 结果
|
||||
*/
|
||||
boolean deleteRunAndHisInstance(List<String> businessKeys);
|
||||
|
||||
/**
|
||||
* 获取当前流程状态
|
||||
*
|
||||
* @param taskId 任务id
|
||||
*/
|
||||
String getBusinessStatusByTaskId(String taskId);
|
||||
|
||||
/**
|
||||
* 获取当前流程状态
|
||||
*
|
||||
* @param businessKey 业务id
|
||||
*/
|
||||
String getBusinessStatus(String businessKey);
|
||||
|
||||
/**
|
||||
* 设置流程变量(全局变量)
|
||||
*
|
||||
* @param taskId 任务id
|
||||
* @param variableName 变量名称
|
||||
* @param value 变量值
|
||||
*/
|
||||
void setVariable(String taskId, String variableName, Object value);
|
||||
|
||||
/**
|
||||
* 设置流程变量(全局变量)
|
||||
*
|
||||
* @param taskId 任务id
|
||||
* @param variables 流程变量
|
||||
*/
|
||||
void setVariables(String taskId, Map<String, Object> variables);
|
||||
|
||||
/**
|
||||
* 设置流程变量(本地变量,非全局变量)
|
||||
*
|
||||
* @param taskId 任务id
|
||||
* @param variableName 变量名称
|
||||
* @param value 变量值
|
||||
*/
|
||||
void setVariableLocal(String taskId, String variableName, Object value);
|
||||
|
||||
/**
|
||||
* 设置流程变量(本地变量,非全局变量)
|
||||
*
|
||||
* @param taskId 任务id
|
||||
* @param variables 流程变量
|
||||
*/
|
||||
void setVariablesLocal(String taskId, Map<String, Object> variables);
|
||||
|
||||
/**
|
||||
* 按照业务id查询流程实例id
|
||||
*
|
||||
* @param businessKey 业务id
|
||||
* @return 结果
|
||||
*/
|
||||
String getInstanceIdByBusinessKey(String businessKey);
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
package org.dromara.common.core.utils;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.apache.commons.lang3.time.DateFormatUtils;
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.*;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 时间工具类
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class DateUtils extends org.apache.commons.lang3.time.DateUtils {
|
||||
|
||||
public static final String YYYY = "yyyy";
|
||||
|
||||
public static final String YYYY_MM = "yyyy-MM";
|
||||
|
||||
public static final String YYYY_MM_DD = "yyyy-MM-dd";
|
||||
|
||||
public static final String YYYYMMDDHHMMSS = "yyyyMMddHHmmss";
|
||||
|
||||
public static final String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";
|
||||
|
||||
private static final String[] PARSE_PATTERNS = {
|
||||
"yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM",
|
||||
"yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM",
|
||||
"yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"};
|
||||
|
||||
/**
|
||||
* 获取当前Date型日期
|
||||
*
|
||||
* @return Date() 当前日期
|
||||
*/
|
||||
public static Date getNowDate() {
|
||||
return new Date();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前日期, 默认格式为yyyy-MM-dd
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
public static String getDate() {
|
||||
return dateTimeNow(YYYY_MM_DD);
|
||||
}
|
||||
|
||||
public static String getTime() {
|
||||
return dateTimeNow(YYYY_MM_DD_HH_MM_SS);
|
||||
}
|
||||
|
||||
public static String dateTimeNow() {
|
||||
return dateTimeNow(YYYYMMDDHHMMSS);
|
||||
}
|
||||
|
||||
public static String dateTimeNow(final String format) {
|
||||
return parseDateToStr(format, new Date());
|
||||
}
|
||||
|
||||
public static String dateTime(final Date date) {
|
||||
return parseDateToStr(YYYY_MM_DD, date);
|
||||
}
|
||||
|
||||
public static String parseDateToStr(final String format, final Date date) {
|
||||
return new SimpleDateFormat(format).format(date);
|
||||
}
|
||||
|
||||
public static Date dateTime(final String format, final String ts) {
|
||||
try {
|
||||
return new SimpleDateFormat(format).parse(ts);
|
||||
} catch (ParseException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 日期路径 即年/月/日 如2018/08/08
|
||||
*/
|
||||
public static String datePath() {
|
||||
Date now = new Date();
|
||||
return DateFormatUtils.format(now, "yyyy/MM/dd");
|
||||
}
|
||||
|
||||
/**
|
||||
* 日期路径 即年/月/日 如20180808
|
||||
*/
|
||||
public static String dateTime() {
|
||||
Date now = new Date();
|
||||
return DateFormatUtils.format(now, "yyyyMMdd");
|
||||
}
|
||||
|
||||
/**
|
||||
* 日期型字符串转化为日期 格式
|
||||
*/
|
||||
public static Date parseDate(Object str) {
|
||||
if (str == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return parseDate(str.toString(), PARSE_PATTERNS);
|
||||
} 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)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算两个时间差
|
||||
*/
|
||||
public static String getDatePoor(Date endDate, Date nowDate) {
|
||||
long nd = 1000 * 24 * 60 * 60;
|
||||
long nh = 1000 * 60 * 60;
|
||||
long nm = 1000 * 60;
|
||||
// long ns = 1000;
|
||||
// 获得两个时间的毫秒时间差异
|
||||
long diff = endDate.getTime() - nowDate.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,93 @@
|
||||
package org.dromara.common.core.utils;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import io.github.linpeilie.Converter;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Mapstruct 工具类
|
||||
* <p>参考文档:<a href="https://mapstruct.plus/introduction/quick-start.html">mapstruct-plus</a></p>
|
||||
*
|
||||
*
|
||||
* @author Michelle.Chung
|
||||
*/
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class MapstructUtils {
|
||||
|
||||
private final static Converter CONVERTER = SpringUtils.getBean(Converter.class);
|
||||
|
||||
/**
|
||||
* 将 T 类型对象,转换为 desc 类型的对象并返回
|
||||
*
|
||||
* @param source 数据来源实体
|
||||
* @param desc 描述对象 转换后的对象
|
||||
* @return desc
|
||||
*/
|
||||
public static <T, V> V convert(T source, Class<V> desc) {
|
||||
if (ObjectUtil.isNull(source)) {
|
||||
return null;
|
||||
}
|
||||
if (ObjectUtil.isNull(desc)) {
|
||||
return null;
|
||||
}
|
||||
return CONVERTER.convert(source, desc);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 T 类型对象,按照配置的映射字段规则,给 desc 类型的对象赋值并返回 desc 对象
|
||||
*
|
||||
* @param source 数据来源实体
|
||||
* @param desc 转换后的对象
|
||||
* @return desc
|
||||
*/
|
||||
public static <T, V> V convert(T source, V desc) {
|
||||
if (ObjectUtil.isNull(source)) {
|
||||
return null;
|
||||
}
|
||||
if (ObjectUtil.isNull(desc)) {
|
||||
return null;
|
||||
}
|
||||
return CONVERTER.convert(source, desc);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 T 类型的集合,转换为 desc 类型的集合并返回
|
||||
*
|
||||
* @param sourceList 数据来源实体列表
|
||||
* @param desc 描述对象 转换后的对象
|
||||
* @return desc
|
||||
*/
|
||||
public static <T, V> List<V> convert(List<T> sourceList, Class<V> desc) {
|
||||
if (ObjectUtil.isNull(sourceList)) {
|
||||
return null;
|
||||
}
|
||||
if (CollUtil.isEmpty(sourceList)) {
|
||||
return CollUtil.newArrayList();
|
||||
}
|
||||
return CONVERTER.convert(sourceList, desc);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 Map 转换为 beanClass 类型的集合并返回
|
||||
*
|
||||
* @param map 数据来源
|
||||
* @param beanClass bean类
|
||||
* @return bean对象
|
||||
*/
|
||||
public static <T> T convert(Map<String, Object> map, Class<T> beanClass) {
|
||||
if (MapUtil.isEmpty(map)) {
|
||||
return null;
|
||||
}
|
||||
if (ObjectUtil.isNull(beanClass)) {
|
||||
return null;
|
||||
}
|
||||
return CONVERTER.convert(map, beanClass);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package org.dromara.common.core.utils;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.context.NoSuchMessageException;
|
||||
import org.springframework.context.i18n.LocaleContextHolder;
|
||||
|
||||
/**
|
||||
* 获取i18n资源文件
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class MessageUtils {
|
||||
|
||||
private static final MessageSource MESSAGE_SOURCE = SpringUtils.getBean(MessageSource.class);
|
||||
|
||||
/**
|
||||
* 根据消息键和参数 获取消息 委托给spring messageSource
|
||||
*
|
||||
* @param code 消息键
|
||||
* @param args 参数
|
||||
* @return 获取国际化翻译值
|
||||
*/
|
||||
public static String message(String code, Object... args) {
|
||||
try {
|
||||
return MESSAGE_SOURCE.getMessage(code, args, LocaleContextHolder.getLocale());
|
||||
} catch (NoSuchMessageException e) {
|
||||
return code;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package org.dromara.common.core.utils;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* 对象工具类
|
||||
*
|
||||
* @author 秋辞未寒
|
||||
*/
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class ObjectUtils extends ObjectUtil {
|
||||
|
||||
/**
|
||||
* 如果对象不为空,则获取对象中的某个字段 ObjectUtils.notNullGetter(user, User::getName);
|
||||
*
|
||||
* @param obj 对象
|
||||
* @param func 获取方法
|
||||
* @return 对象字段
|
||||
*/
|
||||
public static <T, E> E notNullGetter(T obj, Function<T, E> func) {
|
||||
if (isNotNull(obj) && isNotNull(func)) {
|
||||
return func.apply(obj);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果对象不为空,则获取对象中的某个字段,否则返回默认值
|
||||
*
|
||||
* @param obj 对象
|
||||
* @param func 获取方法
|
||||
* @param defaultValue 默认值
|
||||
* @return 对象字段
|
||||
*/
|
||||
public static <T, E> E notNullGetter(T obj, Function<T, E> func, E defaultValue) {
|
||||
if (isNotNull(obj) && isNotNull(func)) {
|
||||
return func.apply(obj);
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果值不为空,则返回值,否则返回默认值
|
||||
*
|
||||
* @param obj 对象
|
||||
* @param defaultValue 默认值
|
||||
* @return 对象字段
|
||||
*/
|
||||
public static <T> T notNull(T obj, T defaultValue) {
|
||||
if (isNotNull(obj)) {
|
||||
return obj;
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,289 @@
|
||||
package org.dromara.common.core.utils;
|
||||
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.extra.servlet.JakartaServletUtil;
|
||||
import cn.hutool.http.HttpStatus;
|
||||
import jakarta.servlet.ServletRequest;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.util.LinkedCaseInsensitiveMap;
|
||||
import org.springframework.web.context.request.RequestAttributes;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URLDecoder;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 客户端工具类,提供获取请求参数、响应处理、头部信息等常用操作
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class ServletUtils extends JakartaServletUtil {
|
||||
|
||||
/**
|
||||
* 获取指定名称的 String 类型的请求参数
|
||||
*
|
||||
* @param name 参数名
|
||||
* @return 参数值
|
||||
*/
|
||||
public static String getParameter(String name) {
|
||||
return getRequest().getParameter(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定名称的 String 类型的请求参数,若参数不存在,则返回默认值
|
||||
*
|
||||
* @param name 参数名
|
||||
* @param defaultValue 默认值
|
||||
* @return 参数值或默认值
|
||||
*/
|
||||
public static String getParameter(String name, String defaultValue) {
|
||||
return Convert.toStr(getRequest().getParameter(name), defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定名称的 Integer 类型的请求参数
|
||||
*
|
||||
* @param name 参数名
|
||||
* @return 参数值
|
||||
*/
|
||||
public static Integer getParameterToInt(String name) {
|
||||
return Convert.toInt(getRequest().getParameter(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定名称的 Integer 类型的请求参数,若参数不存在,则返回默认值
|
||||
*
|
||||
* @param name 参数名
|
||||
* @param defaultValue 默认值
|
||||
* @return 参数值或默认值
|
||||
*/
|
||||
public static Integer getParameterToInt(String name, Integer defaultValue) {
|
||||
return Convert.toInt(getRequest().getParameter(name), defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定名称的 Boolean 类型的请求参数
|
||||
*
|
||||
* @param name 参数名
|
||||
* @return 参数值
|
||||
*/
|
||||
public static Boolean getParameterToBool(String name) {
|
||||
return Convert.toBool(getRequest().getParameter(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定名称的 Boolean 类型的请求参数,若参数不存在,则返回默认值
|
||||
*
|
||||
* @param name 参数名
|
||||
* @param defaultValue 默认值
|
||||
* @return 参数值或默认值
|
||||
*/
|
||||
public static Boolean getParameterToBool(String name, Boolean defaultValue) {
|
||||
return Convert.toBool(getRequest().getParameter(name), defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有请求参数(以 Map 的形式返回)
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有请求参数(以 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(), StringUtils.SEPARATOR));
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前 HTTP 请求对象
|
||||
*
|
||||
* @return 当前 HTTP 请求对象
|
||||
*/
|
||||
public static HttpServletRequest getRequest() {
|
||||
try {
|
||||
return getRequestAttributes().getRequest();
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前 HTTP 响应对象
|
||||
*
|
||||
* @return 当前 HTTP 响应对象
|
||||
*/
|
||||
public static HttpServletResponse getResponse() {
|
||||
try {
|
||||
return getRequestAttributes().getResponse();
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前请求的 HttpSession 对象
|
||||
* <p>
|
||||
* 如果当前请求已经关联了一个会话(即已经存在有效的 session ID),
|
||||
* 则返回该会话对象;如果没有关联会话,则会创建一个新的会话对象并返回。
|
||||
* <p>
|
||||
* HttpSession 用于存储会话级别的数据,如用户登录信息、购物车内容等,
|
||||
* 可以在多个请求之间共享会话数据
|
||||
*
|
||||
* @return 当前请求的 HttpSession 对象
|
||||
*/
|
||||
public static HttpSession getSession() {
|
||||
return getRequest().getSession();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前请求的请求属性
|
||||
*
|
||||
* @return {@link ServletRequestAttributes} 请求属性对象
|
||||
*/
|
||||
public static ServletRequestAttributes getRequestAttributes() {
|
||||
try {
|
||||
RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
|
||||
return (ServletRequestAttributes) attributes;
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定请求头的值,如果头部为空则返回空字符串
|
||||
*
|
||||
* @param request 请求对象
|
||||
* @param name 头部名称
|
||||
* @return 头部值
|
||||
*/
|
||||
public static String getHeader(HttpServletRequest request, String name) {
|
||||
String value = request.getHeader(name);
|
||||
if (StringUtils.isEmpty(value)) {
|
||||
return StringUtils.EMPTY;
|
||||
}
|
||||
return urlDecode(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有请求头的 Map,键为头部名称,值为头部值
|
||||
*
|
||||
* @param request 请求对象
|
||||
* @return 请求头的 Map
|
||||
*/
|
||||
public static Map<String, String> getHeaders(HttpServletRequest request) {
|
||||
Map<String, String> map = new LinkedCaseInsensitiveMap<>();
|
||||
Enumeration<String> enumeration = request.getHeaderNames();
|
||||
if (enumeration != null) {
|
||||
while (enumeration.hasMoreElements()) {
|
||||
String key = enumeration.nextElement();
|
||||
String value = request.getHeader(key);
|
||||
map.put(key, value);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字符串渲染到客户端(以 JSON 格式返回)
|
||||
*
|
||||
* @param response 渲染对象
|
||||
* @param string 待渲染的字符串
|
||||
*/
|
||||
public static void renderString(HttpServletResponse response, String string) {
|
||||
try {
|
||||
response.setStatus(HttpStatus.HTTP_OK);
|
||||
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
|
||||
response.setCharacterEncoding(StandardCharsets.UTF_8.toString());
|
||||
response.getWriter().print(string);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断当前请求是否为 Ajax 异步请求
|
||||
*
|
||||
* @param request 请求对象
|
||||
* @return 是否为 Ajax 请求
|
||||
*/
|
||||
public static boolean isAjaxRequest(HttpServletRequest request) {
|
||||
|
||||
// 判断 Accept 头部是否包含 application/json
|
||||
String accept = request.getHeader("accept");
|
||||
if (accept != null && accept.contains(MediaType.APPLICATION_JSON_VALUE)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 判断 X-Requested-With 头部是否包含 XMLHttpRequest
|
||||
String xRequestedWith = request.getHeader("X-Requested-With");
|
||||
if (xRequestedWith != null && xRequestedWith.contains("XMLHttpRequest")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 判断 URI 后缀是否为 .json 或 .xml
|
||||
String uri = request.getRequestURI();
|
||||
if (StringUtils.equalsAnyIgnoreCase(uri, ".json", ".xml")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 判断请求参数 __ajax 是否为 json 或 xml
|
||||
String ajax = request.getParameter("__ajax");
|
||||
return StringUtils.equalsAnyIgnoreCase(ajax, "json", "xml");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取客户端 IP 地址
|
||||
*
|
||||
* @return 客户端 IP 地址
|
||||
*/
|
||||
public static String getClientIP() {
|
||||
return getClientIP(getRequest());
|
||||
}
|
||||
|
||||
/**
|
||||
* 对内容进行 URL 编码
|
||||
*
|
||||
* @param str 内容
|
||||
* @return 编码后的内容
|
||||
*/
|
||||
public static String urlEncode(String str) {
|
||||
return URLEncoder.encode(str, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* 对内容进行 URL 解码
|
||||
*
|
||||
* @param str 内容
|
||||
* @return 解码后的内容
|
||||
*/
|
||||
public static String urlDecode(String str) {
|
||||
return URLDecoder.decode(str, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package org.dromara.common.core.utils;
|
||||
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.boot.autoconfigure.thread.Threading;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* spring工具类
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@Component
|
||||
public final class SpringUtils extends SpringUtil {
|
||||
|
||||
/**
|
||||
* 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true
|
||||
*/
|
||||
public static boolean containsBean(String name) {
|
||||
return getBeanFactory().containsBean(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断以给定名字注册的bean定义是一个singleton还是一个prototype。
|
||||
* 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException)
|
||||
*/
|
||||
public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException {
|
||||
return getBeanFactory().isSingleton(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Class 注册对象的类型
|
||||
*/
|
||||
public static Class<?> getType(String name) throws NoSuchBeanDefinitionException {
|
||||
return getBeanFactory().getType(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果给定的bean名字在bean定义中有别名,则返回这些别名
|
||||
*/
|
||||
public static String[] getAliases(String name) throws NoSuchBeanDefinitionException {
|
||||
return getBeanFactory().getAliases(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取aop代理对象
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T getAopProxy(T invoker) {
|
||||
return (T) getBean(invoker.getClass());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取spring上下文
|
||||
*/
|
||||
public static ApplicationContext context() {
|
||||
return getApplicationContext();
|
||||
}
|
||||
|
||||
public static boolean isVirtual() {
|
||||
return Threading.VIRTUAL.isActive(getBean(Environment.class));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,282 @@
|
||||
package org.dromara.common.core.utils;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* stream 流工具类
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class StreamUtils {
|
||||
|
||||
/**
|
||||
* 将collection过滤
|
||||
*
|
||||
* @param collection 需要转化的集合
|
||||
* @param function 过滤方法
|
||||
* @return 过滤后的list
|
||||
*/
|
||||
public static <E> List<E> filter(Collection<E> collection, Predicate<E> function) {
|
||||
if (CollUtil.isEmpty(collection)) {
|
||||
return CollUtil.newArrayList();
|
||||
}
|
||||
// 注意此处不要使用 .toList() 新语法 因为返回的是不可变List 会导致序列化问题
|
||||
return collection.stream().filter(function).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 找到流中满足条件的第一个元素
|
||||
*
|
||||
* @param collection 需要查询的集合
|
||||
* @param function 过滤方法
|
||||
* @return 找到符合条件的第一个元素,没有则返回null
|
||||
*/
|
||||
public static <E> E findFirst(Collection<E> collection, Predicate<E> function) {
|
||||
if (CollUtil.isEmpty(collection)) {
|
||||
return null;
|
||||
}
|
||||
return collection.stream().filter(function).findFirst().orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 找到流中任意一个满足条件的元素
|
||||
*
|
||||
* @param collection 需要查询的集合
|
||||
* @param function 过滤方法
|
||||
* @return 找到符合条件的任意一个元素,没有则返回null
|
||||
*/
|
||||
public static <E> Optional<E> findAny(Collection<E> collection, Predicate<E> function) {
|
||||
if (CollUtil.isEmpty(collection)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
return collection.stream().filter(function).findAny();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将collection拼接
|
||||
*
|
||||
* @param collection 需要转化的集合
|
||||
* @param function 拼接方法
|
||||
* @return 拼接后的list
|
||||
*/
|
||||
public static <E> String join(Collection<E> collection, Function<E, String> function) {
|
||||
return join(collection, function, StringUtils.SEPARATOR);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将collection拼接
|
||||
*
|
||||
* @param collection 需要转化的集合
|
||||
* @param function 拼接方法
|
||||
* @param delimiter 拼接符
|
||||
* @return 拼接后的list
|
||||
*/
|
||||
public static <E> String join(Collection<E> collection, Function<E, String> function, CharSequence delimiter) {
|
||||
if (CollUtil.isEmpty(collection)) {
|
||||
return StringUtils.EMPTY;
|
||||
}
|
||||
return collection.stream().map(function).filter(Objects::nonNull).collect(Collectors.joining(delimiter));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将collection排序
|
||||
*
|
||||
* @param collection 需要转化的集合
|
||||
* @param comparing 排序方法
|
||||
* @return 排序后的list
|
||||
*/
|
||||
public static <E> List<E> sorted(Collection<E> collection, Comparator<E> comparing) {
|
||||
if (CollUtil.isEmpty(collection)) {
|
||||
return CollUtil.newArrayList();
|
||||
}
|
||||
// 注意此处不要使用 .toList() 新语法 因为返回的是不可变List 会导致序列化问题
|
||||
return collection.stream().filter(Objects::nonNull).sorted(comparing).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 将collection转化为类型不变的map<br>
|
||||
* <B>{@code Collection<V> ----> Map<K,V>}</B>
|
||||
*
|
||||
* @param collection 需要转化的集合
|
||||
* @param key V类型转化为K类型的lambda方法
|
||||
* @param <V> collection中的泛型
|
||||
* @param <K> map中的key类型
|
||||
* @return 转化后的map
|
||||
*/
|
||||
public static <V, K> Map<K, V> toIdentityMap(Collection<V> collection, Function<V, K> key) {
|
||||
if (CollUtil.isEmpty(collection)) {
|
||||
return MapUtil.newHashMap();
|
||||
}
|
||||
return collection.stream().filter(Objects::nonNull).collect(Collectors.toMap(key, Function.identity(), (l, r) -> l));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将Collection转化为map(value类型与collection的泛型不同)<br>
|
||||
* <B>{@code Collection<E> -----> Map<K,V> }</B>
|
||||
*
|
||||
* @param collection 需要转化的集合
|
||||
* @param key E类型转化为K类型的lambda方法
|
||||
* @param value E类型转化为V类型的lambda方法
|
||||
* @param <E> collection中的泛型
|
||||
* @param <K> map中的key类型
|
||||
* @param <V> map中的value类型
|
||||
* @return 转化后的map
|
||||
*/
|
||||
public static <E, K, V> Map<K, V> toMap(Collection<E> collection, Function<E, K> key, Function<E, V> value) {
|
||||
if (CollUtil.isEmpty(collection)) {
|
||||
return MapUtil.newHashMap();
|
||||
}
|
||||
return collection.stream().filter(Objects::nonNull).collect(Collectors.toMap(key, value, (l, r) -> l));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将collection按照规则(比如有相同的班级id)分类成map<br>
|
||||
* <B>{@code Collection<E> -------> Map<K,List<E>> } </B>
|
||||
*
|
||||
* @param collection 需要分类的集合
|
||||
* @param key 分类的规则
|
||||
* @param <E> collection中的泛型
|
||||
* @param <K> map中的key类型
|
||||
* @return 分类后的map
|
||||
*/
|
||||
public static <E, K> Map<K, List<E>> groupByKey(Collection<E> collection, Function<E, K> key) {
|
||||
if (CollUtil.isEmpty(collection)) {
|
||||
return MapUtil.newHashMap();
|
||||
}
|
||||
return collection
|
||||
.stream().filter(Objects::nonNull)
|
||||
.collect(Collectors.groupingBy(key, LinkedHashMap::new, Collectors.toList()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将collection按照两个规则(比如有相同的年级id,班级id)分类成双层map<br>
|
||||
* <B>{@code Collection<E> ---> Map<T,Map<U,List<E>>> } </B>
|
||||
*
|
||||
* @param collection 需要分类的集合
|
||||
* @param key1 第一个分类的规则
|
||||
* @param key2 第二个分类的规则
|
||||
* @param <E> 集合元素类型
|
||||
* @param <K> 第一个map中的key类型
|
||||
* @param <U> 第二个map中的key类型
|
||||
* @return 分类后的map
|
||||
*/
|
||||
public static <E, K, U> Map<K, Map<U, List<E>>> groupBy2Key(Collection<E> collection, Function<E, K> key1, Function<E, U> key2) {
|
||||
if (CollUtil.isEmpty(collection)) {
|
||||
return MapUtil.newHashMap();
|
||||
}
|
||||
return collection
|
||||
.stream().filter(Objects::nonNull)
|
||||
.collect(Collectors.groupingBy(key1, LinkedHashMap::new, Collectors.groupingBy(key2, LinkedHashMap::new, Collectors.toList())));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将collection按照两个规则(比如有相同的年级id,班级id)分类成双层map<br>
|
||||
* <B>{@code Collection<E> ---> Map<T,Map<U,E>> } </B>
|
||||
*
|
||||
* @param collection 需要分类的集合
|
||||
* @param key1 第一个分类的规则
|
||||
* @param key2 第二个分类的规则
|
||||
* @param <T> 第一个map中的key类型
|
||||
* @param <U> 第二个map中的key类型
|
||||
* @param <E> collection中的泛型
|
||||
* @return 分类后的map
|
||||
*/
|
||||
public static <E, T, U> Map<T, Map<U, E>> group2Map(Collection<E> collection, Function<E, T> key1, Function<E, U> key2) {
|
||||
if (CollUtil.isEmpty(collection) || key1 == null || key2 == null) {
|
||||
return MapUtil.newHashMap();
|
||||
}
|
||||
return collection
|
||||
.stream().filter(Objects::nonNull)
|
||||
.collect(Collectors.groupingBy(key1, LinkedHashMap::new, Collectors.toMap(key2, Function.identity(), (l, r) -> l)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将collection转化为List集合,但是两者的泛型不同<br>
|
||||
* <B>{@code Collection<E> ------> List<T> } </B>
|
||||
*
|
||||
* @param collection 需要转化的集合
|
||||
* @param function collection中的泛型转化为list泛型的lambda表达式
|
||||
* @param <E> collection中的泛型
|
||||
* @param <T> List中的泛型
|
||||
* @return 转化后的list
|
||||
*/
|
||||
public static <E, T> List<T> toList(Collection<E> collection, Function<E, T> function) {
|
||||
if (CollUtil.isEmpty(collection)) {
|
||||
return CollUtil.newArrayList();
|
||||
}
|
||||
return collection
|
||||
.stream()
|
||||
.map(function)
|
||||
.filter(Objects::nonNull)
|
||||
// 注意此处不要使用 .toList() 新语法 因为返回的是不可变List 会导致序列化问题
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 将collection转化为Set集合,但是两者的泛型不同<br>
|
||||
* <B>{@code Collection<E> ------> Set<T> } </B>
|
||||
*
|
||||
* @param collection 需要转化的集合
|
||||
* @param function collection中的泛型转化为set泛型的lambda表达式
|
||||
* @param <E> collection中的泛型
|
||||
* @param <T> Set中的泛型
|
||||
* @return 转化后的Set
|
||||
*/
|
||||
public static <E, T> Set<T> toSet(Collection<E> collection, Function<E, T> function) {
|
||||
if (CollUtil.isEmpty(collection) || function == null) {
|
||||
return CollUtil.newHashSet();
|
||||
}
|
||||
return collection
|
||||
.stream()
|
||||
.map(function)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 合并两个相同key类型的map
|
||||
*
|
||||
* @param map1 第一个需要合并的 map
|
||||
* @param map2 第二个需要合并的 map
|
||||
* @param merge 合并的lambda,将key value1 value2合并成最终的类型,注意value可能为空的情况
|
||||
* @param <K> map中的key类型
|
||||
* @param <X> 第一个 map的value类型
|
||||
* @param <Y> 第二个 map的value类型
|
||||
* @param <V> 最终map的value类型
|
||||
* @return 合并后的map
|
||||
*/
|
||||
public static <K, X, Y, V> Map<K, V> merge(Map<K, X> map1, Map<K, Y> map2, BiFunction<X, Y, V> merge) {
|
||||
if (MapUtil.isEmpty(map1) && MapUtil.isEmpty(map2)) {
|
||||
return MapUtil.newHashMap();
|
||||
} else if (MapUtil.isEmpty(map1)) {
|
||||
map1 = MapUtil.newHashMap();
|
||||
} else if (MapUtil.isEmpty(map2)) {
|
||||
map2 = MapUtil.newHashMap();
|
||||
}
|
||||
Set<K> key = new HashSet<>();
|
||||
key.addAll(map1.keySet());
|
||||
key.addAll(map2.keySet());
|
||||
Map<K, V> map = new HashMap<>();
|
||||
for (K t : key) {
|
||||
X x = map1.get(t);
|
||||
Y y = map2.get(t);
|
||||
V z = merge.apply(x, y);
|
||||
if (z != null) {
|
||||
map.put(t, z);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,323 @@
|
||||
package org.dromara.common.core.utils;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.lang.Validator;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 字符串工具类
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class StringUtils extends org.apache.commons.lang3.StringUtils {
|
||||
|
||||
public static final String SEPARATOR = ",";
|
||||
|
||||
public static final String SLASH = "/";
|
||||
|
||||
/**
|
||||
* 获取参数不为空值
|
||||
*
|
||||
* @param str defaultValue 要判断的value
|
||||
* @return value 返回值
|
||||
*/
|
||||
public static String blankToDefault(String str, String defaultValue) {
|
||||
return StrUtil.blankToDefault(str, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* * 判断一个字符串是否为空串
|
||||
*
|
||||
* @param str String
|
||||
* @return true:为空 false:非空
|
||||
*/
|
||||
public static boolean isEmpty(String str) {
|
||||
return StrUtil.isEmpty(str);
|
||||
}
|
||||
|
||||
/**
|
||||
* * 判断一个字符串是否为非空串
|
||||
*
|
||||
* @param str String
|
||||
* @return true:非空串 false:空串
|
||||
*/
|
||||
public static boolean isNotEmpty(String str) {
|
||||
return !isEmpty(str);
|
||||
}
|
||||
|
||||
/**
|
||||
* 去空格
|
||||
*/
|
||||
public static String trim(String str) {
|
||||
return StrUtil.trim(str);
|
||||
}
|
||||
|
||||
/**
|
||||
* 截取字符串
|
||||
*
|
||||
* @param str 字符串
|
||||
* @param start 开始
|
||||
* @return 结果
|
||||
*/
|
||||
public static String substring(final String str, int start) {
|
||||
return substring(str, start, str.length());
|
||||
}
|
||||
|
||||
/**
|
||||
* 截取字符串
|
||||
*
|
||||
* @param str 字符串
|
||||
* @param start 开始
|
||||
* @param end 结束
|
||||
* @return 结果
|
||||
*/
|
||||
public static String substring(final String str, int start, int end) {
|
||||
return StrUtil.sub(str, start, end);
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化文本, {} 表示占位符<br>
|
||||
* 此方法只是简单将占位符 {} 按照顺序替换为参数<br>
|
||||
* 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可<br>
|
||||
* 例:<br>
|
||||
* 通常使用:format("this is {} for {}", "a", "b") -> this is a for b<br>
|
||||
* 转义{}: format("this is \\{} for {}", "a", "b") -> this is {} for a<br>
|
||||
* 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b<br>
|
||||
*
|
||||
* @param template 文本模板,被替换的部分用 {} 表示
|
||||
* @param params 参数值
|
||||
* @return 格式化后的文本
|
||||
*/
|
||||
public static String format(String template, Object... params) {
|
||||
return StrUtil.format(template, params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为http(s)://开头
|
||||
*
|
||||
* @param link 链接
|
||||
* @return 结果
|
||||
*/
|
||||
public static boolean ishttp(String link) {
|
||||
return Validator.isUrl(link);
|
||||
}
|
||||
|
||||
/**
|
||||
* 字符串转set
|
||||
*
|
||||
* @param str 字符串
|
||||
* @param sep 分隔符
|
||||
* @return set集合
|
||||
*/
|
||||
public static Set<String> str2Set(String str, String sep) {
|
||||
return new HashSet<>(str2List(str, sep, true, false));
|
||||
}
|
||||
|
||||
/**
|
||||
* 字符串转list
|
||||
*
|
||||
* @param str 字符串
|
||||
* @param sep 分隔符
|
||||
* @param filterBlank 过滤纯空白
|
||||
* @param trim 去掉首尾空白
|
||||
* @return list集合
|
||||
*/
|
||||
public static List<String> str2List(String str, String sep, boolean filterBlank, boolean trim) {
|
||||
List<String> list = new ArrayList<>();
|
||||
if (isEmpty(str)) {
|
||||
return list;
|
||||
}
|
||||
|
||||
// 过滤空白字符串
|
||||
if (filterBlank && isBlank(str)) {
|
||||
return list;
|
||||
}
|
||||
String[] split = str.split(sep);
|
||||
for (String string : split) {
|
||||
if (filterBlank && isBlank(string)) {
|
||||
continue;
|
||||
}
|
||||
if (trim) {
|
||||
string = trim(string);
|
||||
}
|
||||
list.add(string);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找指定字符串是否包含指定字符串列表中的任意一个字符串同时串忽略大小写
|
||||
*
|
||||
* @param cs 指定字符串
|
||||
* @param searchCharSequences 需要检查的字符串数组
|
||||
* @return 是否包含任意一个字符串
|
||||
*/
|
||||
public static boolean containsAnyIgnoreCase(CharSequence cs, CharSequence... searchCharSequences) {
|
||||
return StrUtil.containsAnyIgnoreCase(cs, searchCharSequences);
|
||||
}
|
||||
|
||||
/**
|
||||
* 驼峰转下划线命名
|
||||
*/
|
||||
public static String toUnderScoreCase(String str) {
|
||||
return StrUtil.toUnderlineCase(str);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否包含字符串
|
||||
*
|
||||
* @param str 验证字符串
|
||||
* @param strs 字符串组
|
||||
* @return 包含返回true
|
||||
*/
|
||||
public static boolean inStringIgnoreCase(String str, String... strs) {
|
||||
return StrUtil.equalsAnyIgnoreCase(str, strs);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。 例如:HELLO_WORLD->HelloWorld
|
||||
*
|
||||
* @param name 转换前的下划线大写方式命名的字符串
|
||||
* @return 转换后的驼峰式命名的字符串
|
||||
*/
|
||||
public static String convertToCamelCase(String name) {
|
||||
return StrUtil.upperFirst(StrUtil.toCamelCase(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* 驼峰式命名法 例如:user_name->userName
|
||||
*/
|
||||
public static String toCamelCase(String s) {
|
||||
return StrUtil.toCamelCase(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找指定字符串是否匹配指定字符串列表中的任意一个字符串
|
||||
*
|
||||
* @param str 指定字符串
|
||||
* @param strs 需要检查的字符串数组
|
||||
* @return 是否匹配
|
||||
*/
|
||||
public static boolean matches(String str, List<String> strs) {
|
||||
if (isEmpty(str) || CollUtil.isEmpty(strs)) {
|
||||
return false;
|
||||
}
|
||||
for (String pattern : strs) {
|
||||
if (isMatch(pattern, str)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断url是否与规则配置:
|
||||
* ? 表示单个字符;
|
||||
* * 表示一层路径内的任意字符串,不可跨层级;
|
||||
* ** 表示任意层路径;
|
||||
*
|
||||
* @param pattern 匹配规则
|
||||
* @param url 需要匹配的url
|
||||
*/
|
||||
public static boolean isMatch(String pattern, String url) {
|
||||
AntPathMatcher matcher = new AntPathMatcher();
|
||||
return matcher.match(pattern, url);
|
||||
}
|
||||
|
||||
/**
|
||||
* 数字左边补齐0,使之达到指定长度。注意,如果数字转换为字符串后,长度大于size,则只保留 最后size个字符。
|
||||
*
|
||||
* @param num 数字对象
|
||||
* @param size 字符串指定长度
|
||||
* @return 返回数字的字符串格式,该字符串为指定长度。
|
||||
*/
|
||||
public static 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 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) {
|
||||
sb.append(String.valueOf(c).repeat(size - len));
|
||||
sb.append(s);
|
||||
} else {
|
||||
return s.substring(len - size, len);
|
||||
}
|
||||
} else {
|
||||
sb.append(String.valueOf(c).repeat(Math.max(0, size)));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 切分字符串(分隔符默认逗号)
|
||||
*
|
||||
* @param str 被切分的字符串
|
||||
* @return 分割后的数据列表
|
||||
*/
|
||||
public static List<String> splitList(String str) {
|
||||
return splitTo(str, Convert::toStr);
|
||||
}
|
||||
|
||||
/**
|
||||
* 切分字符串
|
||||
*
|
||||
* @param str 被切分的字符串
|
||||
* @param separator 分隔符
|
||||
* @return 分割后的数据列表
|
||||
*/
|
||||
public static List<String> splitList(String str, String separator) {
|
||||
return splitTo(str, separator, Convert::toStr);
|
||||
}
|
||||
|
||||
/**
|
||||
* 切分字符串自定义转换(分隔符默认逗号)
|
||||
*
|
||||
* @param str 被切分的字符串
|
||||
* @param mapper 自定义转换
|
||||
* @return 分割后的数据列表
|
||||
*/
|
||||
public static <T> List<T> splitTo(String str, Function<? super Object, T> mapper) {
|
||||
return splitTo(str, SEPARATOR, mapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 切分字符串自定义转换
|
||||
*
|
||||
* @param str 被切分的字符串
|
||||
* @param separator 分隔符
|
||||
* @param mapper 自定义转换
|
||||
* @return 分割后的数据列表
|
||||
*/
|
||||
public static <T> List<T> splitTo(String str, String separator, Function<? super Object, T> mapper) {
|
||||
if (isBlank(str)) {
|
||||
return new ArrayList<>(0);
|
||||
}
|
||||
return StrUtil.split(str, separator)
|
||||
.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.map(mapper)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package org.dromara.common.core.utils;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.concurrent.*;
|
||||
|
||||
/**
|
||||
* 线程相关工具类.
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Slf4j
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class Threads {
|
||||
|
||||
/**
|
||||
* 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)) {
|
||||
log.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) {
|
||||
log.error(t.getMessage(), t);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package org.dromara.common.core.utils;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.lang.tree.Tree;
|
||||
import cn.hutool.core.lang.tree.TreeNodeConfig;
|
||||
import cn.hutool.core.lang.tree.TreeUtil;
|
||||
import cn.hutool.core.lang.tree.parser.NodeParser;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.dromara.common.core.utils.reflect.ReflectUtils;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* 扩展 hutool TreeUtil 封装系统树构建
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class TreeBuildUtils extends TreeUtil {
|
||||
|
||||
/**
|
||||
* 根据前端定制差异化字段
|
||||
*/
|
||||
public static final TreeNodeConfig DEFAULT_CONFIG = TreeNodeConfig.DEFAULT_CONFIG.setNameKey("label");
|
||||
|
||||
/**
|
||||
* 构建树形结构
|
||||
*
|
||||
* @param <T> 输入节点的类型
|
||||
* @param <K> 节点ID的类型
|
||||
* @param list 节点列表,其中包含了要构建树形结构的所有节点
|
||||
* @param nodeParser 解析器,用于将输入节点转换为树节点
|
||||
* @return 构建好的树形结构列表
|
||||
*/
|
||||
public static <T, K> List<Tree<K>> build(List<T> list, NodeParser<T, K> nodeParser) {
|
||||
if (CollUtil.isEmpty(list)) {
|
||||
return CollUtil.newArrayList();
|
||||
}
|
||||
K k = ReflectUtils.invokeGetter(list.get(0), "parentId");
|
||||
return TreeUtil.build(list, k, DEFAULT_CONFIG, nodeParser);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建树形结构
|
||||
*
|
||||
* @param <T> 输入节点的类型
|
||||
* @param <K> 节点ID的类型
|
||||
* @param parentId 顶级节点
|
||||
* @param list 节点列表,其中包含了要构建树形结构的所有节点
|
||||
* @param nodeParser 解析器,用于将输入节点转换为树节点
|
||||
* @return 构建好的树形结构列表
|
||||
*/
|
||||
public static <T, K> List<Tree<K>> build(List<T> list, K parentId, NodeParser<T, K> nodeParser) {
|
||||
if (CollUtil.isEmpty(list)) {
|
||||
return CollUtil.newArrayList();
|
||||
}
|
||||
return TreeUtil.build(list, parentId, DEFAULT_CONFIG, nodeParser);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取节点列表中所有节点的叶子节点
|
||||
*
|
||||
* @param <K> 节点ID的类型
|
||||
* @param nodes 节点列表
|
||||
* @return 包含所有叶子节点的列表
|
||||
*/
|
||||
public static <K> List<Tree<K>> getLeafNodes(List<Tree<K>> nodes) {
|
||||
if (CollUtil.isEmpty(nodes)) {
|
||||
return CollUtil.newArrayList();
|
||||
}
|
||||
return nodes.stream()
|
||||
.flatMap(TreeBuildUtils::extractLeafNodes)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定节点下的所有叶子节点
|
||||
*
|
||||
* @param <K> 节点ID的类型
|
||||
* @param node 要查找叶子节点的根节点
|
||||
* @return 包含所有叶子节点的列表
|
||||
*/
|
||||
private static <K> Stream<Tree<K>> extractLeafNodes(Tree<K> node) {
|
||||
if (!node.hasChild()) {
|
||||
return Stream.of(node);
|
||||
} else {
|
||||
// 递归调用,获取所有子节点的叶子节点
|
||||
return node.getChildren().stream()
|
||||
.flatMap(TreeBuildUtils::extractLeafNodes);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package org.dromara.common.core.utils;
|
||||
|
||||
public class UserLoginUtil {
|
||||
public static boolean validate(String userName){
|
||||
if("cjname".equals(userName)){
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean validateId(String userName) {
|
||||
if("cjid".equals(userName)){
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package org.dromara.common.core.utils;
|
||||
|
||||
import jakarta.validation.ConstraintViolation;
|
||||
import jakarta.validation.ConstraintViolationException;
|
||||
import jakarta.validation.Validator;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Validator 校验框架工具
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class ValidatorUtils {
|
||||
|
||||
private static final Validator VALID = SpringUtils.getBean(Validator.class);
|
||||
|
||||
/**
|
||||
* 对给定对象进行参数校验,并根据指定的校验组进行校验
|
||||
*
|
||||
* @param object 要进行校验的对象
|
||||
* @param groups 校验组
|
||||
* @throws ConstraintViolationException 如果校验不通过,则抛出参数校验异常
|
||||
*/
|
||||
public static <T> void validate(T object, Class<?>... groups) {
|
||||
Set<ConstraintViolation<T>> validate = VALID.validate(object, groups);
|
||||
if (!validate.isEmpty()) {
|
||||
throw new ConstraintViolationException("参数校验异常", validate);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package org.dromara.common.core.utils.file;
|
||||
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* 文件处理工具类
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class FileUtils extends FileUtil {
|
||||
|
||||
/**
|
||||
* 下载文件名重新编码
|
||||
*
|
||||
* @param response 响应对象
|
||||
* @param realFileName 真实文件名
|
||||
*/
|
||||
public static void setAttachmentResponseHeader(HttpServletResponse response, String realFileName) {
|
||||
String percentEncodedFileName = percentEncode(realFileName);
|
||||
String contentDispositionValue = "attachment; filename=%s;filename*=utf-8''%s".formatted(percentEncodedFileName, percentEncodedFileName);
|
||||
response.addHeader("Access-Control-Expose-Headers", "Content-Disposition,download-filename");
|
||||
response.setHeader("Content-disposition", contentDispositionValue);
|
||||
response.setHeader("download-filename", percentEncodedFileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 百分号编码工具方法
|
||||
*
|
||||
* @param s 需要百分号编码的字符串
|
||||
* @return 百分号编码后的字符串
|
||||
*/
|
||||
public static String percentEncode(String s) {
|
||||
String encode = URLEncoder.encode(s, StandardCharsets.UTF_8);
|
||||
return encode.replaceAll("\\+", "%20");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package org.dromara.common.core.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"};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package org.dromara.common.core.utils.ip;
|
||||
|
||||
import cn.hutool.core.net.NetUtil;
|
||||
import cn.hutool.http.HtmlUtil;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
|
||||
/**
|
||||
* 获取地址类
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@Slf4j
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class AddressUtils {
|
||||
|
||||
// 未知地址
|
||||
public static final String UNKNOWN = "XX XX";
|
||||
|
||||
public static String getRealAddressByIP(String ip) {
|
||||
if (StringUtils.isBlank(ip)) {
|
||||
return UNKNOWN;
|
||||
}
|
||||
// 内网不查询
|
||||
ip = StringUtils.contains(ip, "0:0:0:0:0:0:0:1") ? "127.0.0.1" : HtmlUtil.cleanHtmlTag(ip);
|
||||
if (NetUtil.isInnerIP(ip)) {
|
||||
return "内网IP";
|
||||
}
|
||||
return RegionUtils.getCityInfo(ip);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package org.dromara.common.core.utils.ip;
|
||||
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.io.resource.ClassPathResource;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import org.dromara.common.core.utils.file.FileUtils;
|
||||
import org.lionsoul.ip2region.xdb.Searcher;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* 根据ip地址定位工具类,离线方式
|
||||
* 参考地址:<a href="https://gitee.com/lionsoul/ip2region/tree/master/binding/java">集成 ip2region 实现离线IP地址定位库</a>
|
||||
*
|
||||
* @author lishuyan
|
||||
*/
|
||||
@Slf4j
|
||||
public class RegionUtils {
|
||||
|
||||
private static final Searcher SEARCHER;
|
||||
|
||||
static {
|
||||
String fileName = "/ip2region.xdb";
|
||||
File existFile = FileUtils.file(FileUtil.getTmpDir() + FileUtil.FILE_SEPARATOR + fileName);
|
||||
if (!FileUtils.exist(existFile)) {
|
||||
ClassPathResource fileStream = new ClassPathResource(fileName);
|
||||
if (ObjectUtil.isEmpty(fileStream.getStream())) {
|
||||
throw new ServiceException("RegionUtils初始化失败,原因:IP地址库数据不存在!");
|
||||
}
|
||||
FileUtils.writeFromStream(fileStream.getStream(), existFile);
|
||||
}
|
||||
|
||||
String dbPath = existFile.getPath();
|
||||
|
||||
// 1、从 dbPath 加载整个 xdb 到内存。
|
||||
byte[] cBuff;
|
||||
try {
|
||||
cBuff = Searcher.loadContentFromFile(dbPath);
|
||||
} catch (Exception e) {
|
||||
throw new ServiceException("RegionUtils初始化失败,原因:从ip2region.xdb文件加载内容失败!" + e.getMessage());
|
||||
}
|
||||
// 2、使用上述的 cBuff 创建一个完全基于内存的查询对象。
|
||||
try {
|
||||
SEARCHER = Searcher.newWithBuffer(cBuff);
|
||||
} catch (Exception e) {
|
||||
throw new ServiceException("RegionUtils初始化失败,原因:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据IP地址离线获取城市
|
||||
*/
|
||||
public static String getCityInfo(String ip) {
|
||||
try {
|
||||
ip = ip.trim();
|
||||
// 3、执行查询
|
||||
String region = SEARCHER.search(ip);
|
||||
return region.replace("0|", "").replace("|0", "");
|
||||
} catch (Exception e) {
|
||||
log.error("IP地址离线获取城市异常 {}", ip);
|
||||
return "未知";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package org.dromara.common.core.utils.reflect;
|
||||
|
||||
import cn.hutool.core.util.ReflectUtil;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* 反射工具类. 提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class, 被AOP过的真实类等工具函数.
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class ReflectUtils extends ReflectUtil {
|
||||
|
||||
private static final String SETTER_PREFIX = "set";
|
||||
|
||||
private static final String GETTER_PREFIX = "get";
|
||||
|
||||
/**
|
||||
* 调用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 = invoke(object, getterMethodName);
|
||||
}
|
||||
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 = invoke(object, getterMethodName);
|
||||
} else {
|
||||
String setterMethodName = SETTER_PREFIX + StringUtils.capitalize(names[i]);
|
||||
Method method = getMethodByName(object.getClass(), setterMethodName);
|
||||
invoke(object, method, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package org.dromara.common.core.utils.regex;
|
||||
|
||||
|
||||
import cn.hutool.core.util.ReUtil;
|
||||
import org.dromara.common.core.constant.RegexConstants;
|
||||
|
||||
/**
|
||||
* 正则相关工具类
|
||||
*
|
||||
* @author Feng
|
||||
*/
|
||||
public final class RegexUtils extends ReUtil {
|
||||
|
||||
/**
|
||||
* 从输入字符串中提取匹配的部分,如果没有匹配则返回默认值
|
||||
*
|
||||
* @param input 要提取的输入字符串
|
||||
* @param regex 用于匹配的正则表达式,可以使用 {@link RegexConstants} 中定义的常量
|
||||
* @param defaultInput 如果没有匹配时返回的默认值
|
||||
* @return 如果找到匹配的部分,则返回匹配的部分,否则返回默认值
|
||||
*/
|
||||
public static String extractFromString(String input, String regex, String defaultInput) {
|
||||
try {
|
||||
String str = ReUtil.get(regex, input, 1);
|
||||
return str == null ? defaultInput : str;
|
||||
} catch (Exception e) {
|
||||
return defaultInput;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package org.dromara.common.core.utils.regex;
|
||||
|
||||
import cn.hutool.core.exceptions.ValidateException;
|
||||
import cn.hutool.core.lang.Validator;
|
||||
import org.dromara.common.core.factory.RegexPatternPoolFactory;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* 正则字段校验器
|
||||
* 主要验证字段非空、是否为满足指定格式等
|
||||
*
|
||||
* @author Feng
|
||||
*/
|
||||
public class RegexValidator extends Validator {
|
||||
|
||||
/**
|
||||
* 字典类型必须以字母开头,且只能为(小写字母,数字,下滑线)
|
||||
*/
|
||||
public static final Pattern DICTIONARY_TYPE = RegexPatternPoolFactory.DICTIONARY_TYPE;
|
||||
|
||||
/**
|
||||
* 身份证号码(后6位)
|
||||
*/
|
||||
public static final Pattern ID_CARD_LAST_6 = RegexPatternPoolFactory.ID_CARD_LAST_6;
|
||||
|
||||
/**
|
||||
* QQ号码
|
||||
*/
|
||||
public static final Pattern QQ_NUMBER = RegexPatternPoolFactory.QQ_NUMBER;
|
||||
|
||||
/**
|
||||
* 邮政编码
|
||||
*/
|
||||
public static final Pattern POSTAL_CODE = RegexPatternPoolFactory.POSTAL_CODE;
|
||||
|
||||
/**
|
||||
* 注册账号
|
||||
*/
|
||||
public static final Pattern ACCOUNT = RegexPatternPoolFactory.ACCOUNT;
|
||||
|
||||
/**
|
||||
* 密码:包含至少8个字符,包括大写字母、小写字母、数字和特殊字符
|
||||
*/
|
||||
public static final Pattern PASSWORD = RegexPatternPoolFactory.PASSWORD;
|
||||
|
||||
/**
|
||||
* 通用状态(0表示正常,1表示停用)
|
||||
*/
|
||||
public static final Pattern STATUS = RegexPatternPoolFactory.STATUS;
|
||||
|
||||
|
||||
/**
|
||||
* 检查输入的账号是否匹配预定义的规则
|
||||
*
|
||||
* @param value 要验证的账号
|
||||
* @return 如果账号符合规则,返回 true;否则,返回 false。
|
||||
*/
|
||||
public static boolean isAccount(CharSequence value) {
|
||||
return isMatchRegex(ACCOUNT, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证输入的账号是否符合规则,如果不符合,则抛出 ValidateException 异常
|
||||
*
|
||||
* @param value 要验证的账号
|
||||
* @param errorMsg 验证失败时抛出的异常消息
|
||||
* @param <T> CharSequence 的子类型
|
||||
* @return 如果验证通过,返回输入的账号
|
||||
* @throws ValidateException 如果验证失败
|
||||
*/
|
||||
public static <T extends CharSequence> T validateAccount(T value, String errorMsg) throws ValidateException {
|
||||
if (!isAccount(value)) {
|
||||
throw new ValidateException(errorMsg);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查输入的状态是否匹配预定义的规则
|
||||
*
|
||||
* @param value 要验证的状态
|
||||
* @return 如果状态符合规则,返回 true;否则,返回 false。
|
||||
*/
|
||||
public static boolean isStatus(CharSequence value) {
|
||||
return isMatchRegex(STATUS, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证输入的状态是否符合规则,如果不符合,则抛出 ValidateException 异常
|
||||
*
|
||||
* @param value 要验证的状态
|
||||
* @param errorMsg 验证失败时抛出的异常消息
|
||||
* @param <T> CharSequence 的子类型
|
||||
* @return 如果验证通过,返回输入的状态
|
||||
* @throws ValidateException 如果验证失败
|
||||
*/
|
||||
public static <T extends CharSequence> T validateStatus(T value, String errorMsg) throws ValidateException {
|
||||
if (!isStatus(value)) {
|
||||
throw new ValidateException(errorMsg);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package org.dromara.common.core.utils.sql;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
|
||||
/**
|
||||
* sql操作工具类
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class SqlUtil {
|
||||
|
||||
/**
|
||||
* 定义常用的 sql关键字
|
||||
*/
|
||||
public static String SQL_REGEX = "and |extractvalue|updatexml|sleep|exec |insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |or |union |like |+|/*|user()";
|
||||
|
||||
/**
|
||||
* 仅支持字母、数字、下划线、空格、逗号、小数点(支持多个字段排序)
|
||||
*/
|
||||
public static final String SQL_PATTERN = "[a-zA-Z0-9_\\ \\,\\.]+";
|
||||
|
||||
/**
|
||||
* 检查字符,防止注入绕过
|
||||
*/
|
||||
public static String escapeOrderBySql(String value) {
|
||||
if (StringUtils.isNotEmpty(value) && !isValidOrderBySql(value)) {
|
||||
throw new IllegalArgumentException("参数不符合规范,不能进行查询");
|
||||
}
|
||||
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 IllegalArgumentException("参数存在SQL注入风险");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package org.dromara.common.core.validate;
|
||||
|
||||
/**
|
||||
* 校验分组 add
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
public interface AddGroup {
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package org.dromara.common.core.validate;
|
||||
|
||||
/**
|
||||
* 校验分组 edit
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
public interface EditGroup {
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package org.dromara.common.core.validate;
|
||||
|
||||
/**
|
||||
* 校验分组 query
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
public interface QueryGroup {
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package org.dromara.common.core.xss;
|
||||
|
||||
import jakarta.validation.Constraint;
|
||||
import jakarta.validation.Payload;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 自定义xss校验注解
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@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,20 @@
|
||||
package org.dromara.common.core.xss;
|
||||
|
||||
import cn.hutool.core.util.ReUtil;
|
||||
import cn.hutool.http.HtmlUtil;
|
||||
import jakarta.validation.ConstraintValidator;
|
||||
import jakarta.validation.ConstraintValidatorContext;
|
||||
|
||||
/**
|
||||
* 自定义xss校验注解实现
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
public class XssValidator implements ConstraintValidator<Xss, String> {
|
||||
|
||||
@Override
|
||||
public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) {
|
||||
return !ReUtil.contains(HtmlUtil.RE_HTML_MARK, value);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
org.dromara.common.core.config.ApplicationConfig
|
||||
org.dromara.common.core.config.AsyncConfig
|
||||
org.dromara.common.core.config.RuoYiConfig
|
||||
org.dromara.common.core.config.ThreadPoolConfig
|
||||
org.dromara.common.core.config.ValidatorConfig
|
||||
org.dromara.common.core.utils.SpringUtils
|
||||
41
ruoyi-common/ruoyi-common-doc/pom.xml
Normal file
41
ruoyi-common/ruoyi-common-doc/pom.xml
Normal file
@@ -0,0 +1,41 @@
|
||||
<?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>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>ruoyi-common-doc</artifactId>
|
||||
|
||||
<description>
|
||||
ruoyi-common-doc 系统接口
|
||||
</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-starter-webmvc-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.therapi</groupId>
|
||||
<artifactId>therapi-runtime-javadoc</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.module</groupId>
|
||||
<artifactId>jackson-module-kotlin</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,126 @@
|
||||
package org.dromara.common.doc.config;
|
||||
|
||||
import io.swagger.v3.oas.models.OpenAPI;
|
||||
import io.swagger.v3.oas.models.Paths;
|
||||
import io.swagger.v3.oas.models.info.Info;
|
||||
import io.swagger.v3.oas.models.security.SecurityRequirement;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.dromara.common.doc.config.properties.SpringDocProperties;
|
||||
import org.dromara.common.doc.handler.OpenApiHandler;
|
||||
import org.springdoc.core.configuration.SpringDocConfiguration;
|
||||
import org.springdoc.core.customizers.OpenApiBuilderCustomizer;
|
||||
import org.springdoc.core.customizers.OpenApiCustomizer;
|
||||
import org.springdoc.core.customizers.ServerBaseUrlCustomizer;
|
||||
import org.springdoc.core.properties.SpringDocConfigProperties;
|
||||
import org.springdoc.core.providers.JavadocProvider;
|
||||
import org.springdoc.core.service.OpenAPIService;
|
||||
import org.springdoc.core.service.SecurityService;
|
||||
import org.springdoc.core.utils.PropertyResolverUtils;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.web.ServerProperties;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Swagger 文档配置
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@AutoConfiguration(before = SpringDocConfiguration.class)
|
||||
@EnableConfigurationProperties(SpringDocProperties.class)
|
||||
@ConditionalOnProperty(name = "springdoc.api-docs.enabled", havingValue = "true", matchIfMissing = true)
|
||||
public class SpringDocConfig {
|
||||
|
||||
private final ServerProperties serverProperties;
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(OpenAPI.class)
|
||||
public OpenAPI openApi(SpringDocProperties properties) {
|
||||
OpenAPI openApi = new OpenAPI();
|
||||
// 文档基本信息
|
||||
SpringDocProperties.InfoProperties infoProperties = properties.getInfo();
|
||||
Info info = convertInfo(infoProperties);
|
||||
openApi.info(info);
|
||||
// 扩展文档信息
|
||||
openApi.externalDocs(properties.getExternalDocs());
|
||||
openApi.tags(properties.getTags());
|
||||
openApi.paths(properties.getPaths());
|
||||
openApi.components(properties.getComponents());
|
||||
Set<String> keySet = properties.getComponents().getSecuritySchemes().keySet();
|
||||
List<SecurityRequirement> list = new ArrayList<>();
|
||||
SecurityRequirement securityRequirement = new SecurityRequirement();
|
||||
keySet.forEach(securityRequirement::addList);
|
||||
list.add(securityRequirement);
|
||||
openApi.security(list);
|
||||
|
||||
return openApi;
|
||||
}
|
||||
|
||||
private Info convertInfo(SpringDocProperties.InfoProperties infoProperties) {
|
||||
Info info = new Info();
|
||||
info.setTitle(infoProperties.getTitle());
|
||||
info.setDescription(infoProperties.getDescription());
|
||||
info.setContact(infoProperties.getContact());
|
||||
info.setLicense(infoProperties.getLicense());
|
||||
info.setVersion(infoProperties.getVersion());
|
||||
return info;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义 openapi 处理器
|
||||
*/
|
||||
@Bean
|
||||
public OpenAPIService openApiBuilder(Optional<OpenAPI> openAPI,
|
||||
SecurityService securityParser,
|
||||
SpringDocConfigProperties springDocConfigProperties, PropertyResolverUtils propertyResolverUtils,
|
||||
Optional<List<OpenApiBuilderCustomizer>> openApiBuilderCustomisers,
|
||||
Optional<List<ServerBaseUrlCustomizer>> serverBaseUrlCustomisers, Optional<JavadocProvider> javadocProvider) {
|
||||
return new OpenApiHandler(openAPI, securityParser, springDocConfigProperties, propertyResolverUtils, openApiBuilderCustomisers, serverBaseUrlCustomisers, javadocProvider);
|
||||
}
|
||||
|
||||
/**
|
||||
* 对已经生成好的 OpenApi 进行自定义操作
|
||||
*/
|
||||
@Bean
|
||||
public OpenApiCustomizer openApiCustomizer() {
|
||||
String contextPath = serverProperties.getServlet().getContextPath();
|
||||
String finalContextPath;
|
||||
if (StringUtils.isBlank(contextPath) || "/".equals(contextPath)) {
|
||||
finalContextPath = "";
|
||||
} else {
|
||||
finalContextPath = contextPath;
|
||||
}
|
||||
// 对所有路径增加前置上下文路径
|
||||
return openApi -> {
|
||||
Paths oldPaths = openApi.getPaths();
|
||||
if (oldPaths instanceof PlusPaths) {
|
||||
return;
|
||||
}
|
||||
PlusPaths newPaths = new PlusPaths();
|
||||
oldPaths.forEach((k, v) -> newPaths.addPathItem(finalContextPath + k, v));
|
||||
openApi.setPaths(newPaths);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 单独使用一个类便于判断 解决springdoc路径拼接重复问题
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
static class PlusPaths extends Paths {
|
||||
|
||||
public PlusPaths() {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package org.dromara.common.doc.config.properties;
|
||||
|
||||
import io.swagger.v3.oas.models.Components;
|
||||
import io.swagger.v3.oas.models.ExternalDocumentation;
|
||||
import io.swagger.v3.oas.models.Paths;
|
||||
import io.swagger.v3.oas.models.info.Contact;
|
||||
import io.swagger.v3.oas.models.info.License;
|
||||
import io.swagger.v3.oas.models.tags.Tag;
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.NestedConfigurationProperty;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* swagger 配置属性
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@Data
|
||||
@ConfigurationProperties(prefix = "springdoc")
|
||||
public class SpringDocProperties {
|
||||
|
||||
/**
|
||||
* 文档基本信息
|
||||
*/
|
||||
@NestedConfigurationProperty
|
||||
private InfoProperties info = new InfoProperties();
|
||||
|
||||
/**
|
||||
* 扩展文档地址
|
||||
*/
|
||||
@NestedConfigurationProperty
|
||||
private ExternalDocumentation externalDocs;
|
||||
|
||||
/**
|
||||
* 标签
|
||||
*/
|
||||
private List<Tag> tags = null;
|
||||
|
||||
/**
|
||||
* 路径
|
||||
*/
|
||||
@NestedConfigurationProperty
|
||||
private Paths paths = null;
|
||||
|
||||
/**
|
||||
* 组件
|
||||
*/
|
||||
@NestedConfigurationProperty
|
||||
private Components components = null;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 文档的基础属性信息
|
||||
* </p>
|
||||
*
|
||||
* @see io.swagger.v3.oas.models.info.Info
|
||||
*
|
||||
* 为了 springboot 自动生产配置提示信息,所以这里复制一个类出来
|
||||
*/
|
||||
@Data
|
||||
public static class InfoProperties {
|
||||
|
||||
/**
|
||||
* 标题
|
||||
*/
|
||||
private String title = null;
|
||||
|
||||
/**
|
||||
* 描述
|
||||
*/
|
||||
private String description = null;
|
||||
|
||||
/**
|
||||
* 联系人信息
|
||||
*/
|
||||
@NestedConfigurationProperty
|
||||
private Contact contact = null;
|
||||
|
||||
/**
|
||||
* 许可证
|
||||
*/
|
||||
@NestedConfigurationProperty
|
||||
private License license = null;
|
||||
|
||||
/**
|
||||
* 版本
|
||||
*/
|
||||
private String version = null;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,253 @@
|
||||
package org.dromara.common.doc.handler;
|
||||
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import io.swagger.v3.core.jackson.TypeNameResolver;
|
||||
import io.swagger.v3.core.util.AnnotationsUtils;
|
||||
import io.swagger.v3.oas.annotations.tags.Tags;
|
||||
import io.swagger.v3.oas.models.Components;
|
||||
import io.swagger.v3.oas.models.OpenAPI;
|
||||
import io.swagger.v3.oas.models.Operation;
|
||||
import io.swagger.v3.oas.models.Paths;
|
||||
import io.swagger.v3.oas.models.tags.Tag;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.dromara.common.core.utils.StreamUtils;
|
||||
import org.springdoc.core.customizers.OpenApiBuilderCustomizer;
|
||||
import org.springdoc.core.customizers.ServerBaseUrlCustomizer;
|
||||
import org.springdoc.core.properties.SpringDocConfigProperties;
|
||||
import org.springdoc.core.providers.JavadocProvider;
|
||||
import org.springdoc.core.service.OpenAPIService;
|
||||
import org.springdoc.core.service.SecurityService;
|
||||
import org.springdoc.core.utils.PropertyResolverUtils;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
|
||||
import java.io.StringReader;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* 自定义 openapi 处理器
|
||||
* 对源码功能进行修改 增强使用
|
||||
*/
|
||||
@Slf4j
|
||||
@SuppressWarnings("all")
|
||||
public class OpenApiHandler extends OpenAPIService {
|
||||
|
||||
/**
|
||||
* The Basic error controller.
|
||||
*/
|
||||
private static Class<?> basicErrorController;
|
||||
|
||||
/**
|
||||
* The Security parser.
|
||||
*/
|
||||
private final SecurityService securityParser;
|
||||
|
||||
/**
|
||||
* The Mappings map.
|
||||
*/
|
||||
private final Map<String, Object> mappingsMap = new HashMap<>();
|
||||
|
||||
/**
|
||||
* The Springdoc tags.
|
||||
*/
|
||||
private final Map<HandlerMethod, Tag> springdocTags = new HashMap<>();
|
||||
|
||||
/**
|
||||
* The Open api builder customisers.
|
||||
*/
|
||||
private final Optional<List<OpenApiBuilderCustomizer>> openApiBuilderCustomisers;
|
||||
|
||||
/**
|
||||
* The server base URL customisers.
|
||||
*/
|
||||
private final Optional<List<ServerBaseUrlCustomizer>> serverBaseUrlCustomizers;
|
||||
|
||||
/**
|
||||
* The Spring doc config properties.
|
||||
*/
|
||||
private final SpringDocConfigProperties springDocConfigProperties;
|
||||
|
||||
/**
|
||||
* The Cached open api map.
|
||||
*/
|
||||
private final Map<String, OpenAPI> cachedOpenAPI = new HashMap<>();
|
||||
|
||||
/**
|
||||
* The Property resolver utils.
|
||||
*/
|
||||
private final PropertyResolverUtils propertyResolverUtils;
|
||||
|
||||
/**
|
||||
* The javadoc provider.
|
||||
*/
|
||||
private final Optional<JavadocProvider> javadocProvider;
|
||||
|
||||
/**
|
||||
* The Context.
|
||||
*/
|
||||
private ApplicationContext context;
|
||||
|
||||
/**
|
||||
* The Open api.
|
||||
*/
|
||||
private OpenAPI openAPI;
|
||||
|
||||
/**
|
||||
* The Is servers present.
|
||||
*/
|
||||
private boolean isServersPresent;
|
||||
|
||||
/**
|
||||
* The Server base url.
|
||||
*/
|
||||
private String serverBaseUrl;
|
||||
|
||||
/**
|
||||
* Instantiates a new Open api builder.
|
||||
*
|
||||
* @param openAPI the open api
|
||||
* @param securityParser the security parser
|
||||
* @param springDocConfigProperties the spring doc config properties
|
||||
* @param propertyResolverUtils the property resolver utils
|
||||
* @param openApiBuilderCustomizers the open api builder customisers
|
||||
* @param serverBaseUrlCustomizers the server base url customizers
|
||||
* @param javadocProvider the javadoc provider
|
||||
*/
|
||||
public OpenApiHandler(Optional<OpenAPI> openAPI, SecurityService securityParser,
|
||||
SpringDocConfigProperties springDocConfigProperties, PropertyResolverUtils propertyResolverUtils,
|
||||
Optional<List<OpenApiBuilderCustomizer>> openApiBuilderCustomizers,
|
||||
Optional<List<ServerBaseUrlCustomizer>> serverBaseUrlCustomizers,
|
||||
Optional<JavadocProvider> javadocProvider) {
|
||||
super(openAPI, securityParser, springDocConfigProperties, propertyResolverUtils, openApiBuilderCustomizers, serverBaseUrlCustomizers, javadocProvider);
|
||||
if (openAPI.isPresent()) {
|
||||
this.openAPI = openAPI.get();
|
||||
if (this.openAPI.getComponents() == null)
|
||||
this.openAPI.setComponents(new Components());
|
||||
if (this.openAPI.getPaths() == null)
|
||||
this.openAPI.setPaths(new Paths());
|
||||
if (!CollectionUtils.isEmpty(this.openAPI.getServers()))
|
||||
this.isServersPresent = true;
|
||||
}
|
||||
this.propertyResolverUtils = propertyResolverUtils;
|
||||
this.securityParser = securityParser;
|
||||
this.springDocConfigProperties = springDocConfigProperties;
|
||||
this.openApiBuilderCustomisers = openApiBuilderCustomizers;
|
||||
this.serverBaseUrlCustomizers = serverBaseUrlCustomizers;
|
||||
this.javadocProvider = javadocProvider;
|
||||
if (springDocConfigProperties.isUseFqn())
|
||||
TypeNameResolver.std.setUseFqn(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Operation buildTags(HandlerMethod handlerMethod, Operation operation, OpenAPI openAPI, Locale locale) {
|
||||
|
||||
Set<Tag> tags = new HashSet<>();
|
||||
Set<String> tagsStr = new HashSet<>();
|
||||
|
||||
buildTagsFromMethod(handlerMethod.getMethod(), tags, tagsStr, locale);
|
||||
buildTagsFromClass(handlerMethod.getBeanType(), tags, tagsStr, locale);
|
||||
|
||||
if (!CollectionUtils.isEmpty(tagsStr))
|
||||
tagsStr = tagsStr.stream()
|
||||
.map(str -> propertyResolverUtils.resolve(str, locale))
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
if (springdocTags.containsKey(handlerMethod)) {
|
||||
io.swagger.v3.oas.models.tags.Tag tag = springdocTags.get(handlerMethod);
|
||||
tagsStr.add(tag.getName());
|
||||
if (openAPI.getTags() == null || !openAPI.getTags().contains(tag)) {
|
||||
openAPI.addTagsItem(tag);
|
||||
}
|
||||
}
|
||||
|
||||
if (!CollectionUtils.isEmpty(tagsStr)) {
|
||||
if (CollectionUtils.isEmpty(operation.getTags()))
|
||||
operation.setTags(new ArrayList<>(tagsStr));
|
||||
else {
|
||||
Set<String> operationTagsSet = new HashSet<>(operation.getTags());
|
||||
operationTagsSet.addAll(tagsStr);
|
||||
operation.getTags().clear();
|
||||
operation.getTags().addAll(operationTagsSet);
|
||||
}
|
||||
}
|
||||
|
||||
if (isAutoTagClasses(operation)) {
|
||||
|
||||
|
||||
if (javadocProvider.isPresent()) {
|
||||
String description = javadocProvider.get().getClassJavadoc(handlerMethod.getBeanType());
|
||||
if (StringUtils.isNotBlank(description)) {
|
||||
io.swagger.v3.oas.models.tags.Tag tag = new io.swagger.v3.oas.models.tags.Tag();
|
||||
|
||||
// 自定义部分 修改使用java注释当tag名
|
||||
List<String> list = IoUtil.readLines(new StringReader(description), new ArrayList<>());
|
||||
// tag.setName(tagAutoName);
|
||||
tag.setName(list.get(0));
|
||||
operation.addTagsItem(list.get(0));
|
||||
|
||||
tag.setDescription(description);
|
||||
if (openAPI.getTags() == null || !openAPI.getTags().contains(tag)) {
|
||||
openAPI.addTagsItem(tag);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
String tagAutoName = splitCamelCase(handlerMethod.getBeanType().getSimpleName());
|
||||
operation.addTagsItem(tagAutoName);
|
||||
}
|
||||
}
|
||||
|
||||
if (!CollectionUtils.isEmpty(tags)) {
|
||||
// Existing tags
|
||||
List<io.swagger.v3.oas.models.tags.Tag> openApiTags = openAPI.getTags();
|
||||
if (!CollectionUtils.isEmpty(openApiTags))
|
||||
tags.addAll(openApiTags);
|
||||
openAPI.setTags(new ArrayList<>(tags));
|
||||
}
|
||||
|
||||
// Handle SecurityRequirement at operation level
|
||||
io.swagger.v3.oas.annotations.security.SecurityRequirement[] securityRequirements = securityParser
|
||||
.getSecurityRequirements(handlerMethod);
|
||||
if (securityRequirements != null) {
|
||||
if (securityRequirements.length == 0)
|
||||
operation.setSecurity(Collections.emptyList());
|
||||
else
|
||||
securityParser.buildSecurityRequirement(securityRequirements, operation);
|
||||
}
|
||||
|
||||
return operation;
|
||||
}
|
||||
|
||||
private void buildTagsFromMethod(Method method, Set<io.swagger.v3.oas.models.tags.Tag> tags, Set<String> tagsStr, Locale locale) {
|
||||
// method tags
|
||||
Set<Tags> tagsSet = AnnotatedElementUtils
|
||||
.findAllMergedAnnotations(method, Tags.class);
|
||||
Set<io.swagger.v3.oas.annotations.tags.Tag> methodTags = tagsSet.stream()
|
||||
.flatMap(x -> Stream.of(x.value())).collect(Collectors.toSet());
|
||||
methodTags.addAll(AnnotatedElementUtils.findAllMergedAnnotations(method, io.swagger.v3.oas.annotations.tags.Tag.class));
|
||||
if (!CollectionUtils.isEmpty(methodTags)) {
|
||||
tagsStr.addAll(StreamUtils.toSet(methodTags, tag -> propertyResolverUtils.resolve(tag.name(), locale)));
|
||||
List<io.swagger.v3.oas.annotations.tags.Tag> allTags = new ArrayList<>(methodTags);
|
||||
addTags(allTags, tags, locale);
|
||||
}
|
||||
}
|
||||
|
||||
private void addTags(List<io.swagger.v3.oas.annotations.tags.Tag> sourceTags, Set<io.swagger.v3.oas.models.tags.Tag> tags, Locale locale) {
|
||||
Optional<Set<io.swagger.v3.oas.models.tags.Tag>> optionalTagSet = AnnotationsUtils
|
||||
.getTags(sourceTags.toArray(new io.swagger.v3.oas.annotations.tags.Tag[0]), true);
|
||||
optionalTagSet.ifPresent(tagsSet -> {
|
||||
tagsSet.forEach(tag -> {
|
||||
tag.name(propertyResolverUtils.resolve(tag.getName(), locale));
|
||||
tag.description(propertyResolverUtils.resolve(tag.getDescription(), locale));
|
||||
if (tags.stream().noneMatch(t -> t.getName().equals(tag.getName())))
|
||||
tags.add(tag);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
org.dromara.common.doc.config.SpringDocConfig
|
||||
54
ruoyi-common/ruoyi-common-encrypt/pom.xml
Normal file
54
ruoyi-common/ruoyi-common-encrypt/pom.xml
Normal file
@@ -0,0 +1,54 @@
|
||||
<?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>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>ruoyi-common-encrypt</artifactId>
|
||||
|
||||
<description>
|
||||
ruoyi-common-encrypt 数据加解密模块
|
||||
</description>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk15to18</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-crypto</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-webmvc</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
|
||||
<optional>true</optional>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.mybatis</groupId>
|
||||
<artifactId>mybatis-spring</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,20 @@
|
||||
package org.dromara.common.encrypt.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 强制加密注解
|
||||
*
|
||||
* @author Michelle.Chung
|
||||
*/
|
||||
@Documented
|
||||
@Target({ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface ApiEncrypt {
|
||||
|
||||
/**
|
||||
* 响应加密忽略,默认不加密,为 true 时加密
|
||||
*/
|
||||
boolean response() default false;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package org.dromara.common.encrypt.annotation;
|
||||
|
||||
import org.dromara.common.encrypt.enumd.AlgorithmType;
|
||||
import org.dromara.common.encrypt.enumd.EncodeType;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 字段加密注解
|
||||
*
|
||||
* @author 老马
|
||||
*/
|
||||
@Documented
|
||||
@Inherited
|
||||
@Target({ElementType.FIELD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface EncryptField {
|
||||
|
||||
/**
|
||||
* 加密算法
|
||||
*/
|
||||
AlgorithmType algorithm() default AlgorithmType.DEFAULT;
|
||||
|
||||
/**
|
||||
* 秘钥。AES、SM4需要
|
||||
*/
|
||||
String password() default "";
|
||||
|
||||
/**
|
||||
* 公钥。RSA、SM2需要
|
||||
*/
|
||||
String publicKey() default "";
|
||||
|
||||
/**
|
||||
* 私钥。RSA、SM2需要
|
||||
*/
|
||||
String privateKey() default "";
|
||||
|
||||
/**
|
||||
* 编码方式。对加密算法为BASE64的不起作用
|
||||
*/
|
||||
EncodeType encode() default EncodeType.DEFAULT;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package org.dromara.common.encrypt.config;
|
||||
|
||||
import jakarta.servlet.DispatcherType;
|
||||
import org.dromara.common.encrypt.filter.CryptoFilter;
|
||||
import org.dromara.common.encrypt.properties.ApiDecryptProperties;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
/**
|
||||
* api 解密自动配置
|
||||
*
|
||||
* @author wdhcr
|
||||
*/
|
||||
@AutoConfiguration
|
||||
@EnableConfigurationProperties(ApiDecryptProperties.class)
|
||||
@ConditionalOnProperty(value = "api-decrypt.enabled", havingValue = "true")
|
||||
public class ApiDecryptAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
public FilterRegistrationBean<CryptoFilter> cryptoFilterRegistration(ApiDecryptProperties properties) {
|
||||
FilterRegistrationBean<CryptoFilter> registration = new FilterRegistrationBean<>();
|
||||
registration.setDispatcherTypes(DispatcherType.REQUEST);
|
||||
registration.setFilter(new CryptoFilter(properties));
|
||||
registration.addUrlPatterns("/*");
|
||||
registration.setName("cryptoFilter");
|
||||
registration.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE);
|
||||
return registration;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package org.dromara.common.encrypt.config;
|
||||
|
||||
import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration;
|
||||
import com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.common.encrypt.core.EncryptorManager;
|
||||
import org.dromara.common.encrypt.interceptor.MybatisDecryptInterceptor;
|
||||
import org.dromara.common.encrypt.interceptor.MybatisEncryptInterceptor;
|
||||
import org.dromara.common.encrypt.properties.EncryptorProperties;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
/**
|
||||
* 加解密配置
|
||||
*
|
||||
* @author 老马
|
||||
* @version 4.6.0
|
||||
*/
|
||||
@AutoConfiguration(after = MybatisPlusAutoConfiguration.class)
|
||||
@EnableConfigurationProperties(EncryptorProperties.class)
|
||||
@ConditionalOnProperty(value = "mybatis-encryptor.enable", havingValue = "true")
|
||||
@Slf4j
|
||||
public class EncryptorAutoConfiguration {
|
||||
|
||||
@Autowired
|
||||
private EncryptorProperties properties;
|
||||
|
||||
@Bean
|
||||
public EncryptorManager encryptorManager(MybatisPlusProperties mybatisPlusProperties) {
|
||||
return new EncryptorManager(mybatisPlusProperties.getTypeAliasesPackage());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public MybatisEncryptInterceptor mybatisEncryptInterceptor(EncryptorManager encryptorManager) {
|
||||
return new MybatisEncryptInterceptor(encryptorManager, properties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public MybatisDecryptInterceptor mybatisDecryptInterceptor(EncryptorManager encryptorManager) {
|
||||
return new MybatisDecryptInterceptor(encryptorManager, properties);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
package org.dromara.common.encrypt.core;
|
||||
|
||||
import lombok.Data;
|
||||
import org.dromara.common.encrypt.enumd.AlgorithmType;
|
||||
import org.dromara.common.encrypt.enumd.EncodeType;
|
||||
|
||||
/**
|
||||
* 加密上下文 用于encryptor传递必要的参数。
|
||||
*
|
||||
* @author 老马
|
||||
* @version 4.6.0
|
||||
*/
|
||||
@Data
|
||||
public class EncryptContext {
|
||||
|
||||
/**
|
||||
* 默认算法
|
||||
*/
|
||||
private AlgorithmType algorithm;
|
||||
|
||||
/**
|
||||
* 安全秘钥
|
||||
*/
|
||||
private String password;
|
||||
|
||||
/**
|
||||
* 公钥
|
||||
*/
|
||||
private String publicKey;
|
||||
|
||||
/**
|
||||
* 私钥
|
||||
*/
|
||||
private String privateKey;
|
||||
|
||||
/**
|
||||
* 编码方式,base64/hex
|
||||
*/
|
||||
private EncodeType encode;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
package org.dromara.common.encrypt.core;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ReflectUtil;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.ibatis.io.Resources;
|
||||
import org.dromara.common.core.utils.ObjectUtils;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.dromara.common.encrypt.annotation.EncryptField;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
||||
import org.springframework.core.io.support.ResourcePatternResolver;
|
||||
import org.springframework.core.type.ClassMetadata;
|
||||
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 加密管理类
|
||||
*
|
||||
* @author 老马
|
||||
* @version 4.6.0
|
||||
*/
|
||||
@Slf4j
|
||||
@NoArgsConstructor
|
||||
public class EncryptorManager {
|
||||
|
||||
/**
|
||||
* 缓存加密器
|
||||
*/
|
||||
Map<Integer, IEncryptor> encryptorMap = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 类加密字段缓存
|
||||
*/
|
||||
Map<Class<?>, Set<Field>> fieldCache = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 构造方法传入类加密字段缓存
|
||||
*
|
||||
* @param typeAliasesPackage 实体类包
|
||||
*/
|
||||
public EncryptorManager(String typeAliasesPackage) {
|
||||
scanEncryptClasses(typeAliasesPackage);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取类加密字段缓存
|
||||
*/
|
||||
public Set<Field> getFieldCache(Class<?> sourceClazz) {
|
||||
return ObjectUtils.notNullGetter(fieldCache, f -> f.get(sourceClazz));
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册加密执行者到缓存
|
||||
*
|
||||
* @param encryptContext 加密执行者需要的相关配置参数
|
||||
*/
|
||||
public IEncryptor registAndGetEncryptor(EncryptContext encryptContext) {
|
||||
int key = encryptContext.hashCode();
|
||||
if (encryptorMap.containsKey(key)) {
|
||||
return encryptorMap.get(key);
|
||||
}
|
||||
IEncryptor encryptor = ReflectUtil.newInstance(encryptContext.getAlgorithm().getClazz(), encryptContext);
|
||||
encryptorMap.put(key, encryptor);
|
||||
return encryptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除缓存中的加密执行者
|
||||
*
|
||||
* @param encryptContext 加密执行者需要的相关配置参数
|
||||
*/
|
||||
public void removeEncryptor(EncryptContext encryptContext) {
|
||||
this.encryptorMap.remove(encryptContext.hashCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据配置进行加密。会进行本地缓存对应的算法和对应的秘钥信息。
|
||||
*
|
||||
* @param value 待加密的值
|
||||
* @param encryptContext 加密相关的配置信息
|
||||
*/
|
||||
public String encrypt(String value, EncryptContext encryptContext) {
|
||||
IEncryptor encryptor = this.registAndGetEncryptor(encryptContext);
|
||||
return encryptor.encrypt(value, encryptContext.getEncode());
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据配置进行解密
|
||||
*
|
||||
* @param value 待解密的值
|
||||
* @param encryptContext 加密相关的配置信息
|
||||
*/
|
||||
public String decrypt(String value, EncryptContext encryptContext) {
|
||||
IEncryptor encryptor = this.registAndGetEncryptor(encryptContext);
|
||||
return encryptor.decrypt(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过 typeAliasesPackage 设置的扫描包 扫描缓存实体
|
||||
*/
|
||||
private void scanEncryptClasses(String typeAliasesPackage) {
|
||||
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
|
||||
CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
|
||||
String[] packagePatternArray = StringUtils.splitPreserveAllTokens(typeAliasesPackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
|
||||
String classpath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX;
|
||||
try {
|
||||
for (String packagePattern : packagePatternArray) {
|
||||
String path = ClassUtils.convertClassNameToResourcePath(packagePattern);
|
||||
Resource[] resources = resolver.getResources(classpath + path + "/*.class");
|
||||
for (Resource resource : resources) {
|
||||
ClassMetadata classMetadata = factory.getMetadataReader(resource).getClassMetadata();
|
||||
Class<?> clazz = Resources.classForName(classMetadata.getClassName());
|
||||
Set<Field> encryptFieldSet = getEncryptFieldSetFromClazz(clazz);
|
||||
if (CollUtil.isNotEmpty(encryptFieldSet)) {
|
||||
fieldCache.put(clazz, encryptFieldSet);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("初始化数据安全缓存时出错:{}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得一个类的加密字段集合
|
||||
*/
|
||||
private Set<Field> getEncryptFieldSetFromClazz(Class<?> clazz) {
|
||||
Set<Field> fieldSet = new HashSet<>();
|
||||
// 判断clazz如果是接口,内部类,匿名类就直接返回
|
||||
if (clazz.isInterface() || clazz.isMemberClass() || clazz.isAnonymousClass()) {
|
||||
return fieldSet;
|
||||
}
|
||||
while (clazz != null) {
|
||||
Field[] fields = clazz.getDeclaredFields();
|
||||
fieldSet.addAll(Arrays.asList(fields));
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
fieldSet = fieldSet.stream().filter(field ->
|
||||
field.isAnnotationPresent(EncryptField.class) && field.getType() == String.class)
|
||||
.collect(Collectors.toSet());
|
||||
for (Field field : fieldSet) {
|
||||
field.setAccessible(true);
|
||||
}
|
||||
return fieldSet;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package org.dromara.common.encrypt.core;
|
||||
|
||||
import org.dromara.common.encrypt.enumd.AlgorithmType;
|
||||
import org.dromara.common.encrypt.enumd.EncodeType;
|
||||
|
||||
/**
|
||||
* 加解者
|
||||
*
|
||||
* @author 老马
|
||||
* @version 4.6.0
|
||||
*/
|
||||
public interface IEncryptor {
|
||||
|
||||
/**
|
||||
* 获得当前算法
|
||||
*/
|
||||
AlgorithmType algorithm();
|
||||
|
||||
/**
|
||||
* 加密
|
||||
*
|
||||
* @param value 待加密字符串
|
||||
* @param encodeType 加密后的编码格式
|
||||
* @return 加密后的字符串
|
||||
*/
|
||||
String encrypt(String value, EncodeType encodeType);
|
||||
|
||||
/**
|
||||
* 解密
|
||||
*
|
||||
* @param value 待加密字符串
|
||||
* @return 解密后的字符串
|
||||
*/
|
||||
String decrypt(String value);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package org.dromara.common.encrypt.core.encryptor;
|
||||
|
||||
import org.dromara.common.encrypt.core.EncryptContext;
|
||||
import org.dromara.common.encrypt.core.IEncryptor;
|
||||
|
||||
/**
|
||||
* 所有加密执行者的基类
|
||||
*
|
||||
* @author 老马
|
||||
* @version 4.6.0
|
||||
*/
|
||||
public abstract class AbstractEncryptor implements IEncryptor {
|
||||
|
||||
public AbstractEncryptor(EncryptContext context) {
|
||||
// 用户配置校验与配置注入
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package org.dromara.common.encrypt.core.encryptor;
|
||||
|
||||
import org.dromara.common.encrypt.core.EncryptContext;
|
||||
import org.dromara.common.encrypt.enumd.AlgorithmType;
|
||||
import org.dromara.common.encrypt.enumd.EncodeType;
|
||||
import org.dromara.common.encrypt.utils.EncryptUtils;
|
||||
|
||||
/**
|
||||
* AES算法实现
|
||||
*
|
||||
* @author 老马
|
||||
* @version 4.6.0
|
||||
*/
|
||||
public class AesEncryptor extends AbstractEncryptor {
|
||||
|
||||
private final EncryptContext context;
|
||||
|
||||
public AesEncryptor(EncryptContext context) {
|
||||
super(context);
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得当前算法
|
||||
*/
|
||||
@Override
|
||||
public AlgorithmType algorithm() {
|
||||
return AlgorithmType.AES;
|
||||
}
|
||||
|
||||
/**
|
||||
* 加密
|
||||
*
|
||||
* @param value 待加密字符串
|
||||
* @param encodeType 加密后的编码格式
|
||||
*/
|
||||
@Override
|
||||
public String encrypt(String value, EncodeType encodeType) {
|
||||
if (encodeType == EncodeType.HEX) {
|
||||
return EncryptUtils.encryptByAesHex(value, context.getPassword());
|
||||
} else {
|
||||
return EncryptUtils.encryptByAes(value, context.getPassword());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密
|
||||
*
|
||||
* @param value 待加密字符串
|
||||
*/
|
||||
@Override
|
||||
public String decrypt(String value) {
|
||||
return EncryptUtils.decryptByAes(value, context.getPassword());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package org.dromara.common.encrypt.core.encryptor;
|
||||
|
||||
import org.dromara.common.encrypt.core.EncryptContext;
|
||||
import org.dromara.common.encrypt.enumd.AlgorithmType;
|
||||
import org.dromara.common.encrypt.enumd.EncodeType;
|
||||
import org.dromara.common.encrypt.utils.EncryptUtils;
|
||||
|
||||
/**
|
||||
* Base64算法实现
|
||||
*
|
||||
* @author 老马
|
||||
* @version 4.6.0
|
||||
*/
|
||||
public class Base64Encryptor extends AbstractEncryptor {
|
||||
|
||||
public Base64Encryptor(EncryptContext context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得当前算法
|
||||
*/
|
||||
@Override
|
||||
public AlgorithmType algorithm() {
|
||||
return AlgorithmType.BASE64;
|
||||
}
|
||||
|
||||
/**
|
||||
* 加密
|
||||
*
|
||||
* @param value 待加密字符串
|
||||
* @param encodeType 加密后的编码格式
|
||||
*/
|
||||
@Override
|
||||
public String encrypt(String value, EncodeType encodeType) {
|
||||
return EncryptUtils.encryptByBase64(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密
|
||||
*
|
||||
* @param value 待加密字符串
|
||||
*/
|
||||
@Override
|
||||
public String decrypt(String value) {
|
||||
return EncryptUtils.decryptByBase64(value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package org.dromara.common.encrypt.core.encryptor;
|
||||
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.dromara.common.encrypt.core.EncryptContext;
|
||||
import org.dromara.common.encrypt.enumd.AlgorithmType;
|
||||
import org.dromara.common.encrypt.enumd.EncodeType;
|
||||
import org.dromara.common.encrypt.utils.EncryptUtils;
|
||||
|
||||
|
||||
/**
|
||||
* RSA算法实现
|
||||
*
|
||||
* @author 老马
|
||||
* @version 4.6.0
|
||||
*/
|
||||
public class RsaEncryptor extends AbstractEncryptor {
|
||||
|
||||
private final EncryptContext context;
|
||||
|
||||
public RsaEncryptor(EncryptContext context) {
|
||||
super(context);
|
||||
String privateKey = context.getPrivateKey();
|
||||
String publicKey = context.getPublicKey();
|
||||
if (StringUtils.isAnyEmpty(privateKey, publicKey)) {
|
||||
throw new IllegalArgumentException("RSA公私钥均需要提供,公钥加密,私钥解密。");
|
||||
}
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得当前算法
|
||||
*/
|
||||
@Override
|
||||
public AlgorithmType algorithm() {
|
||||
return AlgorithmType.RSA;
|
||||
}
|
||||
|
||||
/**
|
||||
* 加密
|
||||
*
|
||||
* @param value 待加密字符串
|
||||
* @param encodeType 加密后的编码格式
|
||||
*/
|
||||
@Override
|
||||
public String encrypt(String value, EncodeType encodeType) {
|
||||
if (encodeType == EncodeType.HEX) {
|
||||
return EncryptUtils.encryptByRsaHex(value, context.getPublicKey());
|
||||
} else {
|
||||
return EncryptUtils.encryptByRsa(value, context.getPublicKey());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密
|
||||
*
|
||||
* @param value 待加密字符串
|
||||
*/
|
||||
@Override
|
||||
public String decrypt(String value) {
|
||||
return EncryptUtils.decryptByRsa(value, context.getPrivateKey());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package org.dromara.common.encrypt.core.encryptor;
|
||||
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.dromara.common.encrypt.core.EncryptContext;
|
||||
import org.dromara.common.encrypt.enumd.AlgorithmType;
|
||||
import org.dromara.common.encrypt.enumd.EncodeType;
|
||||
import org.dromara.common.encrypt.utils.EncryptUtils;
|
||||
|
||||
/**
|
||||
* sm2算法实现
|
||||
*
|
||||
* @author 老马
|
||||
* @version 4.6.0
|
||||
*/
|
||||
public class Sm2Encryptor extends AbstractEncryptor {
|
||||
|
||||
private final EncryptContext context;
|
||||
|
||||
public Sm2Encryptor(EncryptContext context) {
|
||||
super(context);
|
||||
String privateKey = context.getPrivateKey();
|
||||
String publicKey = context.getPublicKey();
|
||||
if (StringUtils.isAnyEmpty(privateKey, publicKey)) {
|
||||
throw new IllegalArgumentException("SM2公私钥均需要提供,公钥加密,私钥解密。");
|
||||
}
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得当前算法
|
||||
*/
|
||||
@Override
|
||||
public AlgorithmType algorithm() {
|
||||
return AlgorithmType.SM2;
|
||||
}
|
||||
|
||||
/**
|
||||
* 加密
|
||||
*
|
||||
* @param value 待加密字符串
|
||||
* @param encodeType 加密后的编码格式
|
||||
*/
|
||||
@Override
|
||||
public String encrypt(String value, EncodeType encodeType) {
|
||||
if (encodeType == EncodeType.HEX) {
|
||||
return EncryptUtils.encryptBySm2Hex(value, context.getPublicKey());
|
||||
} else {
|
||||
return EncryptUtils.encryptBySm2(value, context.getPublicKey());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密
|
||||
*
|
||||
* @param value 待加密字符串
|
||||
*/
|
||||
@Override
|
||||
public String decrypt(String value) {
|
||||
return EncryptUtils.decryptBySm2(value, context.getPrivateKey());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package org.dromara.common.encrypt.core.encryptor;
|
||||
|
||||
import org.dromara.common.encrypt.core.EncryptContext;
|
||||
import org.dromara.common.encrypt.enumd.AlgorithmType;
|
||||
import org.dromara.common.encrypt.enumd.EncodeType;
|
||||
import org.dromara.common.encrypt.utils.EncryptUtils;
|
||||
|
||||
/**
|
||||
* sm4算法实现
|
||||
*
|
||||
* @author 老马
|
||||
* @version 4.6.0
|
||||
*/
|
||||
public class Sm4Encryptor extends AbstractEncryptor {
|
||||
|
||||
private final EncryptContext context;
|
||||
|
||||
public Sm4Encryptor(EncryptContext context) {
|
||||
super(context);
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得当前算法
|
||||
*/
|
||||
@Override
|
||||
public AlgorithmType algorithm() {
|
||||
return AlgorithmType.SM4;
|
||||
}
|
||||
|
||||
/**
|
||||
* 加密
|
||||
*
|
||||
* @param value 待加密字符串
|
||||
* @param encodeType 加密后的编码格式
|
||||
*/
|
||||
@Override
|
||||
public String encrypt(String value, EncodeType encodeType) {
|
||||
if (encodeType == EncodeType.HEX) {
|
||||
return EncryptUtils.encryptBySm4Hex(value, context.getPassword());
|
||||
} else {
|
||||
return EncryptUtils.encryptBySm4(value, context.getPassword());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密
|
||||
*
|
||||
* @param value 待加密字符串
|
||||
*/
|
||||
@Override
|
||||
public String decrypt(String value) {
|
||||
return EncryptUtils.decryptBySm4(value, context.getPassword());
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user