Merge remote-tracking branch 'origin/dev'

# Conflicts:
#	ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RedisLockAspect.java
This commit is contained in:
疯狂的狮子li 2021-06-23 09:44:04 +08:00
commit c12dc71ece
165 changed files with 3118 additions and 4159 deletions

View File

@ -2,6 +2,8 @@
[![码云Gitee](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/badge/star.svg?theme=blue)](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus)
[![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/blob/master/LICENSE)
[![使用IntelliJ IDEA开发维护](https://img.shields.io/badge/IntelliJ%20IDEA-提供支持-blue.svg)](https://www.jetbrains.com/?from=RuoYi-Vue-Plus)
[![JDK-8+](https://img.shields.io/badge/JDK-8-green.svg)]()
[![JDK-11](https://img.shields.io/badge/JDK-11-green.svg)]()
基于 RuoYi-Vue 集成 Mybatis-Plus Lombok Hutool 等便捷开发工具 适配重写相关业务 便于开发 定期与 RuoYi-Vue 同步
* 前端开发框架 Vue、Element UI
@ -16,9 +18,15 @@
* 监控框架 spring-boot-admin 全方位服务监控
* 校验框架 validation 增强接口安全性 严谨性
* 文档框架 knife4j 美化接口文档
* 序列化框架 统一使用 jackson 高效可靠
* 代码生成器 一键生成前后端代码
* 多数据源框架 dynamic-datasource 支持主从与多种类数据库异构
* Redis客户端 采用 Redisson 性能更强
* 分布式锁 Lock4j 注解锁、工具锁 多种多样
## 参考文档
使用框架前请仔细阅读文档重点注意事项
[参考文档 Wiki](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/pages)
## 修改RuoYi功能
@ -34,7 +42,10 @@
* 集成 Feign 接口化管理 Http 请求(如三方请求 支付,短信,推送等)
* 移除 自带服务监控 改为 spring-boot-admin 全方位监控
* 增加 demo 模块示例(给不会增加模块的小伙伴做参考)
* 增加 redisson 支持分布式锁 功能更强大
* 增加 redisson 高性能 Redis 客户端
* 移除 fastjson 统一使用 jackson 序列化
* 集成 dynamic-datasource 多数据源(默认支持MySQL,其他种类需自行适配)
* 集成 Lock4j 实现分布式 注解锁、工具锁 多种多样
### 代码改动
@ -46,6 +57,7 @@
* 项目修改为 maven多环境配置
* 项目配置修改为 application.yml 统一管理
* 数据权限修改为 适配支持单表、多表
* 使用 redisson 实现分布式锁注解与工具类
* 使用 redisson 实现 spring-cache 整合
* 增加 mybatis-plus 二级缓存 redis 存储

71
pom.xml
View File

@ -14,23 +14,24 @@
<properties>
<ruoyi-vue-plus.version>2.3.2</ruoyi-vue-plus.version>
<spring-boot.version>2.3.11.RELEASE</spring-boot.version>
<spring-boot.version>2.4.7</spring-boot.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
<druid.version>1.2.6</druid.version>
<knife4j.version>3.0.2</knife4j.version>
<fastjson.version>1.2.76</fastjson.version>
<poi.version>4.1.2</poi.version>
<velocity.version>1.7</velocity.version>
<jwt.version>0.9.1</jwt.version>
<mybatis-plus.version>3.4.3</mybatis-plus.version>
<hutool.version>5.6.5</hutool.version>
<feign.version>2.2.6.RELEASE</feign.version>
<hutool.version>5.7.2</hutool.version>
<feign.version>3.0.3</feign.version>
<feign-okhttp.version>11.0</feign-okhttp.version>
<spring-boot-admin.version>2.3.1</spring-boot-admin.version>
<spring-boot-admin.version>2.4.1</spring-boot-admin.version>
<redisson.version>3.15.2</redisson.version>
<lock4j.version>2.2.1</lock4j.version>
<datasource.version>3.4.0</datasource.version>
</properties>
<!-- 依赖声明 -->
@ -73,13 +74,6 @@
<version>${velocity.version}</version>
</dependency>
<!-- 阿里JSON解析器 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<!-- Token生成与解析-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
@ -87,6 +81,13 @@
<version>${jwt.version}</version>
</dependency>
<!-- dynamic-datasource 多数据源-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>${datasource.version}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
@ -97,6 +98,7 @@
<artifactId>mybatis-plus-extension</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
@ -132,7 +134,11 @@
<artifactId>redisson-spring-boot-starter</artifactId>
<version>${redisson.version}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>lock4j-redisson-spring-boot-starter</artifactId>
<version>${lock4j.version}</version>
</dependency>
<!-- 定时任务-->
<dependency>
<groupId>com.ruoyi</groupId>
@ -270,6 +276,45 @@
<logging.level>warn</logging.level>
</properties>
</profile>
<!-- jdk多版本配置 -->
<profile>
<id>jdk8</id>
<activation>
<activeByDefault>true</activeByDefault>
<jdk>1.8</jdk>
</activation>
<properties>
<java.version>1.8</java.version>
</properties>
</profile>
<profile>
<id>jdk11</id>
<activation>
<jdk>11</jdk>
</activation>
<properties>
<java.version>11</java.version>
<jaxb.version>3.0.1</jaxb.version>
</properties>
<dependencyManagement>
<dependencies>
<!-- jdk11 缺失依赖 jaxb-->
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>${jaxb.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!--jaxb-->
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
</dependency>
</dependencies>
</profile>
</profiles>
</project>

View File

@ -2,7 +2,6 @@ package com.ruoyi;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
/**
* 启动程序
@ -10,7 +9,7 @@ import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
* @author ruoyi
*/
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
@SpringBootApplication
public class RuoYiApplication
{
public static void main(String[] args)

View File

@ -54,10 +54,10 @@ public class CommonController
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
FileUtils.setAttachmentResponseHeader(response, realFileName);
FileUtils.writeBytes(filePath, response.getOutputStream());
FileUtils.writeToStream(filePath, response.getOutputStream());
if (delete)
{
FileUtils.deleteFile(filePath);
FileUtils.del(filePath);
}
}
catch (Exception e)
@ -111,7 +111,7 @@ public class CommonController
String downloadName = StrUtil.subAfter(downloadPath, "/",true);
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
FileUtils.setAttachmentResponseHeader(response, downloadName);
FileUtils.writeBytes(downloadPath, response.getOutputStream());
FileUtils.writeToStream(downloadPath, response.getOutputStream());
}
catch (Exception e)
{

View File

@ -56,7 +56,7 @@ public class SysDeptController extends BaseController
{
SysDept d = (SysDept) it.next();
if (d.getDeptId().intValue() == deptId
|| ArrayUtils.contains(StrUtil.split(d.getAncestors(), ","), deptId + ""))
|| ArrayUtils.contains(StrUtil.splitToArray(d.getAncestors(), ","), deptId + ""))
{
it.remove();
}

View File

@ -1,21 +1,27 @@
# 数据源配置
spring:
autoconfigure:
exclude: com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: com.mysql.cj.jdbc.Driver
# 动态数据源文档 https://www.kancloud.cn/tracy5546/dynamic-datasource/content
dynamic:
#设置默认的数据源或者数据源组,默认值即为 master
primary: master
datasource:
# 主库数据源
master:
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true
username: root
password: root
# 从库数据源
slave:
driverClassName: com.mysql.cj.jdbc.Driver
url:
username:
password:
druid:
# 主库数据源
master:
url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true
username: root
password: root
# 从库数据源
slave:
# 从数据源开关/默认关闭
enabled: false
url:
username:
password:
# 初始连接数
initialSize: 5
# 最小连接池数量
@ -35,6 +41,8 @@ spring:
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
# 注意这个值和druid原生不一致默认启动了stat
filters: stat
webStatFilter:
enabled: true
statViewServlet:

View File

@ -1,21 +1,27 @@
# 数据源配置
spring:
autoconfigure:
exclude: com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: com.mysql.cj.jdbc.Driver
# 动态数据源文档 https://www.kancloud.cn/tracy5546/dynamic-datasource/content
dynamic:
#设置默认的数据源或者数据源组,默认值即为 master
primary: master
datasource:
# 主库数据源
master:
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true
username: root
password: root
# 从库数据源
slave:
driverClassName: com.mysql.cj.jdbc.Driver
url:
username:
password:
druid:
# 主库数据源
master:
url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true
username: root
password: root
# 从库数据源
slave:
# 从数据源开关/默认关闭
enabled: false
url:
username:
password:
# 初始连接数
initialSize: 5
# 最小连接池数量
@ -35,6 +41,8 @@ spring:
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
# 注意这个值和druid原生不一致默认启动了stat
filters: stat
webStatFilter:
enabled: true
statViewServlet:
@ -58,7 +66,7 @@ spring:
# redis 配置
redis:
# 地址
host: 192.168.0.222
host: localhost
# 端口默认为6379
port: 6379
# 数据库索引

View File

@ -11,7 +11,7 @@ ruoyi:
# 文件路径
profile: ./ruoyi/uploadPath
# 获取ip地址开关
addressEnabled: false
addressEnabled: true
captcha:
# 验证码开关
@ -86,6 +86,17 @@ spring:
thymeleaf:
# 将系统模板放置到最前面 否则会与 springboot-admin 页面冲突
template-resolver-order: 1
jackson:
# 日期格式化
date-format: yyyy-MM-dd HH:mm:ss
serialization:
# 格式化输出
indent_output: false
# 忽略无法转换的对象
fail_on_empty_beans: false
deserialization:
# 允许对象忽略json中不存在的属性
fail_on_unknown_properties: false
# token配置
token:
@ -242,9 +253,16 @@ feign:
enabled: true
okhttp:
enabled: true
hystrix:
circuitbreaker:
enabled: true
--- # 分布式锁 lock4j 全局配置
lock4j:
# 获取分布式锁超时时间,默认为 3000 毫秒
acquire-timeout: 3000
# 分布式锁的超时时间,默认为 30 毫秒
expire: 30000
--- # 定时任务配置
spring:
quartz:

View File

@ -53,12 +53,6 @@
<artifactId>jackson-databind</artifactId>
</dependency>
<!-- 阿里JSON解析器 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
<!-- excel工具 -->
<dependency>
<groupId>org.apache.poi</groupId>
@ -152,6 +146,17 @@
<artifactId>redisson-spring-boot-starter</artifactId>
</dependency>
<!-- dynamic-datasource 多数据源-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>lock4j-redisson-spring-boot-starter</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -1,27 +0,0 @@
package com.ruoyi.common.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 分布式锁注解模式不推荐使用最好用锁的工具类
*
* @author shenxinquan
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface RedisLock {
/**
* 锁过期时间 默认30秒
*/
int expireTime() default 30;
/**
* 锁key值
*/
String key() default "redisLockKey";
}

View File

@ -127,6 +127,11 @@ public class Constants
*/
public static final String RESOURCE_PREFIX = "/profile";
/**
* RMI 远程方法调用
*/
public static final String LOOKUP_RMI = "rmi://";
/**
* 资源映射路径 前缀
*/

View File

@ -2,14 +2,8 @@ package com.ruoyi.common.core.controller;
import cn.hutool.core.util.StrUtil;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import java.beans.PropertyEditorSupport;
import java.util.Date;
/**
* web层通用数据处理
@ -20,23 +14,6 @@ public class BaseController
{
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
/**
* 将前台传递过来的日期格式的字符串自动转化为Date类型
*/
@InitBinder
public void initBinder(WebDataBinder binder)
{
// Date 类型转换
binder.registerCustomEditor(Date.class, new PropertyEditorSupport()
{
@Override
public void setAsText(String text)
{
setValue(DateUtils.parseDate(text));
}
});
}
/**
* 响应返回结果
*

View File

@ -1,6 +1,5 @@
package com.ruoyi.common.core.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
@ -30,14 +29,12 @@ public class BaseEntity implements Serializable
private String createBy;
/** 创建时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
/** 更新者 */
private String updateBy;
/** 更新时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date updateTime;
/** 备注 */

View File

@ -1,4 +1,4 @@
package com.ruoyi.common.core.mybatisplus;
package com.ruoyi.common.core.mybatisplus.cache;
import cn.hutool.extra.spring.SpringUtil;
import com.ruoyi.common.core.redis.RedisCache;

View File

@ -1,6 +1,9 @@
package com.ruoyi.common.core.page;
package com.ruoyi.common.core.mybatisplus.core;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;
import java.util.Collection;
/**
* 自定义 Mapper 接口, 实现 自定义扩展
@ -9,4 +12,7 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
* @since 2021-05-13
*/
public interface BaseMapperPlus<T> extends BaseMapper<T> {
int insertAll(@Param("list") Collection<T> batchList);
}

View File

@ -1,9 +1,10 @@
package com.ruoyi.common.core.page;
package com.ruoyi.common.core.mybatisplus.core;
import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.common.core.page.PagePlus;
import java.io.Serializable;
import java.util.Collection;
@ -182,7 +183,6 @@ public interface IServicePlus<T> extends IService<T> {
*
* @param page 翻页对象
* @param queryWrapper 实体对象封装操作类
* @param kClass vo类型
*/
default <K> PagePlus<T, K> pageVo(PagePlus<T, K> page, Wrapper<T> queryWrapper, Class<K> kClass) {
PagePlus<T, K> result = getBaseMapper().selectPage(page, queryWrapper);
@ -210,7 +210,6 @@ public interface IServicePlus<T> extends IService<T> {
* 无条件翻页查询
*
* @param page 翻页对象
* @param kClass vo类型
*/
default <K> PagePlus<T, K> pageVo(PagePlus<T, K> page, Class<K> kClass) {
return pageVo(page, Wrappers.emptyWrapper(), kClass);
@ -226,5 +225,21 @@ public interface IServicePlus<T> extends IService<T> {
return pageVo(page, Wrappers.emptyWrapper(), convertor);
}
@Override
default boolean saveBatch(Collection<T> entityList) {
return saveBatch(entityList, DEFAULT_BATCH_SIZE);
}
@Override
default boolean saveOrUpdateBatch(Collection<T> entityList) {
return saveOrUpdateBatch(entityList, DEFAULT_BATCH_SIZE);
}
@Override
default boolean updateBatchById(Collection<T> entityList) {
return updateBatchById(entityList, DEFAULT_BATCH_SIZE);
}
boolean saveAll(Collection<T> entityList);
}

View File

@ -0,0 +1,90 @@
package com.ruoyi.common.core.mybatisplus.core;
import com.baomidou.mybatisplus.core.toolkit.ClassUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.ResolvableType;
import java.util.Collection;
/**
* IServicePlus 实现类
*
* @author Lion Li
*/
@Slf4j
@SuppressWarnings("unchecked")
public class ServicePlusImpl<M extends BaseMapperPlus<T>, T> extends ServiceImpl<M, T> implements IServicePlus<T> {
@Autowired
protected M baseMapper;
@Override
public M getBaseMapper() {
return baseMapper;
}
protected Class<T> entityClass = currentModelClass();
@Override
public Class<T> getEntityClass() {
return entityClass;
}
protected Class<T> mapperClass = currentMapperClass();
@Override
protected Class<T> currentMapperClass() {
return (Class<T>) this.getResolvableType().as(ServicePlusImpl.class).getGeneric(0).getType();
}
@Override
protected Class<T> currentModelClass() {
return (Class<T>) this.getResolvableType().as(ServicePlusImpl.class).getGeneric(1).getType();
}
@Override
protected ResolvableType getResolvableType() {
return ResolvableType.forClass(ClassUtils.getUserClass(getClass()));
}
/**
* 单条执行性能差
*
* {@link #saveAll(Collection)}
*/
@Deprecated
@Override
public boolean saveBatch(Collection<T> entityList, int batchSize) {
return super.saveBatch(entityList, batchSize);
}
@Override
public boolean saveOrUpdate(T entity) {
return super.saveOrUpdate(entity);
}
/**
* 单条执行性能差
*
* {@link #saveAll(Collection)}
*/
@Deprecated
@Override
public boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize) {
return super.saveOrUpdateBatch(entityList, batchSize);
}
@Override
public boolean updateBatchById(Collection<T> entityList, int batchSize) {
return super.updateBatchById(entityList, batchSize);
}
@Override
public boolean saveAll(Collection<T> entityList) {
return baseMapper.insertAll(entityList) == entityList.size();
}
}

View File

@ -0,0 +1,52 @@
package com.ruoyi.common.core.mybatisplus.methods;
import cn.hutool.core.util.StrUtil;
import org.apache.ibatis.executor.keygen.NoKeyGenerator;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
/**
* 单sql批量插入
*
* @author Lion Li
*/
public class InsertAll extends AbstractMethod {
@Override
public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
final String sql = "<script>insert into %s %s values %s</script>";
final String fieldSql = prepareFieldSql(tableInfo);
final String valueSql = prepareValuesSqlForMysqlBatch(tableInfo);
final String sqlResult = String.format(sql, tableInfo.getTableName(), fieldSql, valueSql);
SqlSource sqlSource = languageDriver.createSqlSource(configuration, sqlResult, modelClass);
return this.addInsertMappedStatement(mapperClass, modelClass, "insertAll", sqlSource, new NoKeyGenerator(), null, null);
}
private String prepareFieldSql(TableInfo tableInfo) {
StringBuilder fieldSql = new StringBuilder();
if (StrUtil.isNotBlank(tableInfo.getKeyColumn())) {
fieldSql.append(tableInfo.getKeyColumn()).append(",");
}
tableInfo.getFieldList().forEach(x -> fieldSql.append(x.getColumn()).append(","));
fieldSql.delete(fieldSql.length() - 1, fieldSql.length());
fieldSql.insert(0, "(");
fieldSql.append(")");
return fieldSql.toString();
}
private String prepareValuesSqlForMysqlBatch(TableInfo tableInfo) {
final StringBuilder valueSql = new StringBuilder();
valueSql.append("<foreach collection=\"list\" item=\"item\" index=\"index\" open=\"(\" separator=\"),(\" close=\")\">");
if (StrUtil.isNotBlank(tableInfo.getKeyColumn())) {
valueSql.append("#{item.").append(tableInfo.getKeyProperty()).append("},");
}
tableInfo.getFieldList().forEach(x -> valueSql.append("#{item.").append(x.getProperty()).append("},"));
valueSql.delete(valueSql.length() - 1, valueSql.length());
valueSql.append("</foreach>");
return valueSql.toString();
}
}

View File

@ -1,170 +0,0 @@
package com.ruoyi.common.core.redis;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.StrUtil;
import org.redisson.api.RCountDownLatch;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
/**
* redis 锁管理类
*
* @author shenxinquan
*/
@Component
public class RedisLockManager {
@Autowired
private RedissonClient redissonClient;
/**
* 通用锁
*/
private final static Integer BASE_LOCK = 1;
/**
* 公平锁
*/
private final static Integer FAIR_LOCK = 2;
/**
* 存放当前线程获取锁的类型
*/
private final ThreadLocal<Integer> threadLocal = new ThreadLocal<>();
/**
* 获取锁
*/
private RLock getLock(String key, Integer lockType) {
Assert.isTrue(StrUtil.isNotBlank(key), "key不能为空");
threadLocal.set(lockType);
RLock lock;
if (BASE_LOCK.equals(lockType)) {
lock = redissonClient.getLock(key);
} else if (FAIR_LOCK.equals(lockType)) {
lock = redissonClient.getFairLock(key);
} else {
throw new RuntimeException("锁不存在!");
}
return lock;
}
/**
* 获取锁不用设置超时时间一直等待
*/
public boolean getLock(String key) {
RLock lock = getLock(key, BASE_LOCK);
return lock.tryLock();
}
/**
* 设置过期时间
*
* @param key
* @param time 过期时间
* @param expireUnit 时间单位
*/
public boolean getLock(String key, long time, TimeUnit expireUnit) {
Assert.isTrue(time > 0, "过期时间必须大于0");
Assert.isTrue(Validator.isNotEmpty(expireUnit), "时间单位不能为空");
RLock lock = getLock(key, BASE_LOCK);
try {
return lock.tryLock(time, expireUnit);
} catch (InterruptedException e) {
e.printStackTrace();
return false;
}
}
/**
* 设置过期时间
*
* @param key
* @param waitTime 获取锁等待时间
* @param leaseTime 保留锁的时间
* @param expireUnit 时间单位
*/
public boolean getLock(String key, long waitTime, long leaseTime, TimeUnit expireUnit) {
Assert.isTrue(waitTime > 0, "获取锁等待时间必须大于0");
Assert.isTrue(leaseTime > 0, "保留锁的时间必须大于0");
Assert.isTrue(Validator.isNotEmpty(expireUnit), "时间单位不能为空");
RLock lock = getLock(key, BASE_LOCK);
try {
return lock.tryLock(waitTime, leaseTime, expireUnit);
} catch (InterruptedException e) {
e.printStackTrace();
return false;
}
}
/**
* 获取计数器锁
*
* @param key
* @param count countDownLatch 的数量
*/
public RCountDownLatch getCountDownLatch(String key, long count) {
Assert.isTrue(count >= 0, "count数量必须大于等于0");
RCountDownLatch rCountDownLatch = redissonClient.getCountDownLatch(key);
rCountDownLatch.trySetCount(count);
return rCountDownLatch;
}
/**
* 获取公平锁
*
* @param key
* @param waitTime 获取锁等待时间
* @param leaseTime 持有锁的时间
* @param expireUnit 时间单位
* @return
* @throws InterruptedException
*/
public boolean getFairLock(String key, long waitTime, long leaseTime, TimeUnit expireUnit) {
Assert.isTrue(waitTime > 0, "获取锁等待时间必须大于0");
Assert.isTrue(leaseTime > 0, "保留锁的时间必须大于0");
Assert.isTrue(Validator.isNotEmpty(expireUnit), "时间单位不能为空");
RLock lock = getLock(key, FAIR_LOCK);
try {
return lock.tryLock(waitTime, leaseTime, expireUnit);
} catch (InterruptedException e) {
e.printStackTrace();
return false;
}
}
/**
* 获取公平锁
*
* @param key
* @param leaseTime 持有锁的时间
* @param expireUnit 时间单位
*/
public boolean getFairLock(String key, long leaseTime, TimeUnit expireUnit) {
Assert.isTrue(leaseTime > 0, "保留锁的时间必须大于0");
Assert.isTrue(Validator.isNotEmpty(expireUnit), "时间单位不能为空");
RLock lock = getLock(key, FAIR_LOCK);
try {
return lock.tryLock(leaseTime, expireUnit);
} catch (InterruptedException e) {
e.printStackTrace();
return false;
}
}
/**
* 释放锁(统一释放)
*/
public void unLock(String key) {
Integer lockType = threadLocal.get();
RLock lock = getLock(key, lockType);
lock.unlock();
threadLocal.remove();
}
}

View File

@ -1,19 +1,25 @@
package com.ruoyi.common.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 数据源
*
* @author ruoyi
* @author Lion Li
*/
public enum DataSourceType
{
/**
* 主库
*/
MASTER,
@AllArgsConstructor
public enum DataSourceType {
/**
* 主库
*/
MASTER("master"),
/**
* 从库
*/
SLAVE
/**
* 从库
*/
SLAVE("slave");
@Getter
private final String source;
}

View File

@ -1,15 +1,17 @@
package com.ruoyi.common.filter;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import cn.hutool.core.io.IoUtil;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import com.ruoyi.common.utils.http.HttpHelper;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
/**
* 构建可重复读取inputStream的request
@ -26,7 +28,7 @@ public class RepeatedlyRequestWrapper extends HttpServletRequestWrapper
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
body = HttpHelper.getBodyString(request).getBytes("UTF-8");
body = IoUtil.readUtf8(request.getInputStream()).getBytes(StandardCharsets.UTF_8);
}
@Override

View File

@ -1,9 +1,9 @@
package com.ruoyi.common.filter;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HtmlUtil;
import org.apache.commons.io.IOUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
@ -13,6 +13,7 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
/**
* XSS过滤处理
@ -57,7 +58,7 @@ public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper
}
// 为空,直接返回
String json = IOUtils.toString(super.getInputStream(), "utf-8");
String json = IoUtil.read(super.getInputStream(), StandardCharsets.UTF_8);
if (Validator.isEmpty(json))
{
return super.getInputStream();
@ -65,7 +66,8 @@ public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper
// xss过滤
json = HtmlUtil.cleanHtmlTag(json).trim();
final ByteArrayInputStream bis = new ByteArrayInputStream(json.getBytes("utf-8"));
final ByteArrayInputStream bis = IoUtil.toStream(json, StandardCharsets.UTF_8);
return new ServletInputStream()
{
@Override

View File

@ -0,0 +1,101 @@
package com.ruoyi.common.utils;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* JSON 工具类
*
* @author 芋道源码
*/
public class JsonUtils {
private static ObjectMapper objectMapper = new ObjectMapper();
/**
* 初始化 objectMapper 属性
* <p>
* 通过这样的方式使用 Spring 创建的 ObjectMapper Bean
*
* @param objectMapper ObjectMapper 对象
*/
public static void init(ObjectMapper objectMapper) {
JsonUtils.objectMapper = objectMapper;
}
public static String toJsonString(Object object) {
if (Validator.isEmpty(object)) {
return null;
}
try {
return objectMapper.writeValueAsString(object);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
public static <T> T parseObject(String text, Class<T> clazz) {
if (StrUtil.isEmpty(text)) {
return null;
}
try {
return objectMapper.readValue(text, clazz);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static <T> T parseObject(byte[] bytes, Class<T> clazz) {
if (ArrayUtil.isEmpty(bytes)) {
return null;
}
try {
return objectMapper.readValue(bytes, clazz);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static <T> T parseObject(String text, TypeReference<T> typeReference) {
if (StrUtil.isBlank(text)) {
return null;
}
try {
return objectMapper.readValue(text, typeReference);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static <T> Map<String, T> parseMap(String text) {
if (StrUtil.isBlank(text)) {
return null;
}
try {
return objectMapper.readValue(text, new TypeReference<Map<String, T>>() {});
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static <T> List<T> parseArray(String text, Class<T> clazz) {
if (StrUtil.isEmpty(text)) {
return new ArrayList<>();
}
try {
return objectMapper.readValue(text, objectMapper.getTypeFactory().constructCollectionType(List.class, clazz));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

View File

@ -70,20 +70,31 @@ public class PageUtils {
return page;
}
public static <T> Page<T> buildPage() {
return buildPage(null, null);
}
/**
* 构建 MP 普通分页对象
* @param <T> domain 实体
* @return 分页对象
*/
public static <T> Page<T> buildPage() {
public static <T> Page<T> buildPage(String defaultOrderByColumn, String defaultIsAsc) {
Integer pageNum = ServletUtils.getParameterToInt(PAGE_NUM, DEFAULT_PAGE_NUM);
Integer pageSize = ServletUtils.getParameterToInt(PAGE_SIZE, DEFAULT_PAGE_SIZE);
String orderByColumn = ServletUtils.getParameter(ORDER_BY_COLUMN);
String isAsc = ServletUtils.getParameter(IS_ASC);
String orderByColumn = ServletUtils.getParameter(ORDER_BY_COLUMN, defaultOrderByColumn);
String isAsc = ServletUtils.getParameter(IS_ASC, defaultIsAsc);
// 兼容前端排序类型
if ("ascending".equals(isAsc)) {
isAsc = "asc";
} else if ("descending".equals(isAsc)) {
isAsc = "desc";
}
Page<T> page = new Page<>(pageNum, pageSize);
if (StrUtil.isNotBlank(orderByColumn)) {
String orderBy = SqlUtil.escapeOrderBySql(orderByColumn);
if ("asc".equals(isAsc)) {
orderBy = StrUtil.toUnderlineCase(orderBy);
if ("asc".equals(isAsc)) {
page.addOrder(OrderItem.asc(orderBy));
} else if ("desc".equals(isAsc)) {
page.addOrder(OrderItem.desc(orderBy));

View File

@ -2,6 +2,9 @@ package com.ruoyi.common.utils;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.servlet.ServletUtil;
import cn.hutool.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
@ -10,129 +13,118 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
/**
* 客户端工具类
*
* @author ruoyi
*/
public class ServletUtils
{
/**
* 获取String参数
*/
public static String getParameter(String name)
{
return getRequest().getParameter(name);
}
public class ServletUtils extends ServletUtil {
/**
* 获取String参数
*/
public static String getParameter(String name) {
return getRequest().getParameter(name);
}
/**
* 获取String参数
*/
public static String getParameter(String name, String defaultValue)
{
return Convert.toStr(getRequest().getParameter(name), defaultValue);
}
/**
* 获取String参数
*/
public static String getParameter(String name, String defaultValue) {
return Convert.toStr(getRequest().getParameter(name), defaultValue);
}
/**
* 获取Integer参数
*/
public static Integer getParameterToInt(String name)
{
return Convert.toInt(getRequest().getParameter(name));
}
/**
* 获取Integer参数
*/
public static Integer getParameterToInt(String name) {
return Convert.toInt(getRequest().getParameter(name));
}
/**
* 获取Integer参数
*/
public static Integer getParameterToInt(String name, Integer defaultValue)
{
return Convert.toInt(getRequest().getParameter(name), defaultValue);
}
/**
* 获取Integer参数
*/
public static Integer getParameterToInt(String name, Integer defaultValue) {
return Convert.toInt(getRequest().getParameter(name), defaultValue);
}
/**
* 获取request
*/
public static HttpServletRequest getRequest()
{
return getRequestAttributes().getRequest();
}
/**
* 获取request
*/
public static HttpServletRequest getRequest() {
return getRequestAttributes().getRequest();
}
/**
* 获取response
*/
public static HttpServletResponse getResponse()
{
return getRequestAttributes().getResponse();
}
/**
* 获取response
*/
public static HttpServletResponse getResponse() {
return getRequestAttributes().getResponse();
}
/**
* 获取session
*/
public static HttpSession getSession()
{
return getRequest().getSession();
}
/**
* 获取session
*/
public static HttpSession getSession() {
return getRequest().getSession();
}
public static ServletRequestAttributes getRequestAttributes()
{
RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
return (ServletRequestAttributes) attributes;
}
public static ServletRequestAttributes getRequestAttributes() {
RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
return (ServletRequestAttributes) attributes;
}
/**
* 将字符串渲染到客户端
*
* @param response 渲染对象
* @param string 待渲染的字符串
* @return null
*/
public static String renderString(HttpServletResponse response, String string)
{
try
{
response.setStatus(200);
response.setContentType("application/json");
response.setCharacterEncoding("utf-8");
response.getWriter().print(string);
}
catch (IOException e)
{
e.printStackTrace();
}
return null;
}
/**
* 将字符串渲染到客户端
*
* @param response 渲染对象
* @param string 待渲染的字符串
* @return null
*/
public static String 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();
}
return null;
}
/**
* 是否是Ajax异步请求
*
* @param request
*/
public static boolean isAjaxRequest(HttpServletRequest request)
{
String accept = request.getHeader("accept");
if (accept != null && accept.indexOf("application/json") != -1)
{
return true;
}
/**
* 是否是Ajax异步请求
*
* @param request
*/
public static boolean isAjaxRequest(HttpServletRequest request) {
String xRequestedWith = request.getHeader("X-Requested-With");
if (xRequestedWith != null && xRequestedWith.indexOf("XMLHttpRequest") != -1)
{
return true;
}
String accept = request.getHeader("accept");
if (accept != null && accept.indexOf("application/json") != -1) {
return true;
}
String uri = request.getRequestURI();
if (StrUtil.equalsAnyIgnoreCase(uri, ".json", ".xml"))
{
return true;
}
String xRequestedWith = request.getHeader("X-Requested-With");
if (xRequestedWith != null && xRequestedWith.indexOf("XMLHttpRequest") != -1) {
return true;
}
String uri = request.getRequestURI();
if (StrUtil.equalsAnyIgnoreCase(uri, ".json", ".xml")) {
return true;
}
String ajax = request.getParameter("__ajax");
if (StrUtil.equalsAnyIgnoreCase(ajax, "json", "xml")) {
return true;
}
return false;
}
public static String getClientIP() {
return getClientIP(getRequest());
}
String ajax = request.getParameter("__ajax");
if (StrUtil.equalsAnyIgnoreCase(ajax, "json", "xml"))
{
return true;
}
return false;
}
}

View File

@ -1,5 +1,6 @@
package com.ruoyi.common.utils.file;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
@ -14,85 +15,10 @@ import java.nio.charset.StandardCharsets;
*
* @author ruoyi
*/
public class FileUtils extends org.apache.commons.io.FileUtils
public class FileUtils extends FileUtil
{
public static String FILENAME_PATTERN = "[a-zA-Z0-9_\\-\\|\\.\\u4e00-\\u9fa5]+";
/**
* 输出指定文件的byte数组
*
* @param filePath 文件路径
* @param os 输出流
* @return
*/
public static void writeBytes(String filePath, OutputStream os) throws IOException
{
FileInputStream fis = null;
try
{
File file = new File(filePath);
if (!file.exists())
{
throw new FileNotFoundException(filePath);
}
fis = new FileInputStream(file);
byte[] b = new byte[1024];
int length;
while ((length = fis.read(b)) > 0)
{
os.write(b, 0, length);
}
}
catch (IOException e)
{
throw e;
}
finally
{
if (os != null)
{
try
{
os.close();
}
catch (IOException e1)
{
e1.printStackTrace();
}
}
if (fis != null)
{
try
{
fis.close();
}
catch (IOException e1)
{
e1.printStackTrace();
}
}
}
}
/**
* 删除文件
*
* @param filePath 文件
* @return
*/
public static boolean deleteFile(String filePath)
{
boolean flag = false;
File file = new File(filePath);
// 路径为文件且不为空则进行删除
if (file.isFile() && file.exists())
{
file.delete();
flag = true;
}
return flag;
}
/**
* 文件名称验证
*
@ -142,7 +68,7 @@ public class FileUtils extends org.apache.commons.io.FileUtils
if (agent.contains("MSIE"))
{
// IE浏览器
filename = URLEncoder.encode(filename, "utf-8");
filename = URLEncoder.encode(filename, StandardCharsets.UTF_8.toString());
filename = filename.replace("+", " ");
}
else if (agent.contains("Firefox"))
@ -153,12 +79,12 @@ public class FileUtils extends org.apache.commons.io.FileUtils
else if (agent.contains("Chrome"))
{
// google浏览器
filename = URLEncoder.encode(filename, "utf-8");
filename = URLEncoder.encode(filename, StandardCharsets.UTF_8.toString());
}
else
{
// 其它浏览器
filename = URLEncoder.encode(filename, "utf-8");
filename = URLEncoder.encode(filename, StandardCharsets.UTF_8.toString());
}
return filename;
}

View File

@ -1,56 +0,0 @@
package com.ruoyi.common.utils.http;
import cn.hutool.core.exceptions.ExceptionUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.ServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
/**
* 通用http工具封装
*
* @author ruoyi
*/
public class HttpHelper
{
private static final Logger LOGGER = LoggerFactory.getLogger(HttpHelper.class);
public static String getBodyString(ServletRequest request)
{
StringBuilder sb = new StringBuilder();
BufferedReader reader = null;
try (InputStream inputStream = request.getInputStream())
{
reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")));
String line = "";
while ((line = reader.readLine()) != null)
{
sb.append(line);
}
}
catch (IOException e)
{
LOGGER.warn("getBodyString出现问题");
}
finally
{
if (reader != null)
{
try
{
reader.close();
}
catch (IOException e)
{
LOGGER.error(ExceptionUtil.getMessage(e));
}
}
}
return sb.toString();
}
}

View File

@ -1,262 +0,0 @@
package com.ruoyi.common.utils.http;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ConnectException;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.net.URLConnection;
import java.security.cert.X509Certificate;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.ruoyi.common.constant.Constants;
/**
* 通用http发送方法
*
* @author ruoyi
*/
public class HttpUtils
{
private static final Logger log = LoggerFactory.getLogger(HttpUtils.class);
/**
* 向指定 URL 发送GET方法的请求
*
* @param url 发送请求的 URL
* @param param 请求参数请求参数应该是 name1=value1&name2=value2 的形式
* @return 所代表远程资源的响应结果
*/
public static String sendGet(String url, String param)
{
return sendGet(url, param, Constants.UTF8);
}
/**
* 向指定 URL 发送GET方法的请求
*
* @param url 发送请求的 URL
* @param param 请求参数请求参数应该是 name1=value1&name2=value2 的形式
* @param contentType 编码类型
* @return 所代表远程资源的响应结果
*/
public static String sendGet(String url, String param, String contentType)
{
StringBuilder result = new StringBuilder();
BufferedReader in = null;
try
{
String urlNameString = url + "?" + param;
log.info("sendGet - {}", urlNameString);
URL realUrl = new URL(urlNameString);
URLConnection connection = realUrl.openConnection();
connection.setRequestProperty("accept", "*/*");
connection.setRequestProperty("connection", "Keep-Alive");
connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
connection.connect();
in = new BufferedReader(new InputStreamReader(connection.getInputStream(), contentType));
String line;
while ((line = in.readLine()) != null)
{
result.append(line);
}
log.info("recv - {}", result);
}
catch (ConnectException e)
{
log.error("调用HttpUtils.sendGet ConnectException, url=" + url + ",param=" + param, e);
}
catch (SocketTimeoutException e)
{
log.error("调用HttpUtils.sendGet SocketTimeoutException, url=" + url + ",param=" + param, e);
}
catch (IOException e)
{
log.error("调用HttpUtils.sendGet IOException, url=" + url + ",param=" + param, e);
}
catch (Exception e)
{
log.error("调用HttpsUtil.sendGet Exception, url=" + url + ",param=" + param, e);
}
finally
{
try
{
if (in != null)
{
in.close();
}
}
catch (Exception ex)
{
log.error("调用in.close Exception, url=" + url + ",param=" + param, ex);
}
}
return result.toString();
}
/**
* 向指定 URL 发送POST方法的请求
*
* @param url 发送请求的 URL
* @param param 请求参数请求参数应该是 name1=value1&name2=value2 的形式
* @return 所代表远程资源的响应结果
*/
public static String sendPost(String url, String param)
{
PrintWriter out = null;
BufferedReader in = null;
StringBuilder result = new StringBuilder();
try
{
String urlNameString = url;
log.info("sendPost - {}", urlNameString);
URL realUrl = new URL(urlNameString);
URLConnection conn = realUrl.openConnection();
conn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("connection", "Keep-Alive");
conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
conn.setRequestProperty("Accept-Charset", "utf-8");
conn.setRequestProperty("contentType", "utf-8");
conn.setDoOutput(true);
conn.setDoInput(true);
out = new PrintWriter(conn.getOutputStream());
out.print(param);
out.flush();
in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"));
String line;
while ((line = in.readLine()) != null)
{
result.append(line);
}
log.info("recv - {}", result);
}
catch (ConnectException e)
{
log.error("调用HttpUtils.sendPost ConnectException, url=" + url + ",param=" + param, e);
}
catch (SocketTimeoutException e)
{
log.error("调用HttpUtils.sendPost SocketTimeoutException, url=" + url + ",param=" + param, e);
}
catch (IOException e)
{
log.error("调用HttpUtils.sendPost IOException, url=" + url + ",param=" + param, e);
}
catch (Exception e)
{
log.error("调用HttpsUtil.sendPost Exception, url=" + url + ",param=" + param, e);
}
finally
{
try
{
if (out != null)
{
out.close();
}
if (in != null)
{
in.close();
}
}
catch (IOException ex)
{
log.error("调用in.close Exception, url=" + url + ",param=" + param, ex);
}
}
return result.toString();
}
public static String sendSSLPost(String url, String param)
{
StringBuilder result = new StringBuilder();
String urlNameString = url + "?" + param;
try
{
log.info("sendSSLPost - {}", urlNameString);
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, new TrustManager[] { new TrustAnyTrustManager() }, new java.security.SecureRandom());
URL console = new URL(urlNameString);
HttpsURLConnection conn = (HttpsURLConnection) console.openConnection();
conn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("connection", "Keep-Alive");
conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
conn.setRequestProperty("Accept-Charset", "utf-8");
conn.setRequestProperty("contentType", "utf-8");
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setSSLSocketFactory(sc.getSocketFactory());
conn.setHostnameVerifier(new TrustAnyHostnameVerifier());
conn.connect();
InputStream is = conn.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String ret = "";
while ((ret = br.readLine()) != null)
{
if (ret != null && !"".equals(ret.trim()))
{
result.append(new String(ret.getBytes("ISO-8859-1"), "utf-8"));
}
}
log.info("recv - {}", result);
conn.disconnect();
br.close();
}
catch (ConnectException e)
{
log.error("调用HttpUtils.sendSSLPost ConnectException, url=" + url + ",param=" + param, e);
}
catch (SocketTimeoutException e)
{
log.error("调用HttpUtils.sendSSLPost SocketTimeoutException, url=" + url + ",param=" + param, e);
}
catch (IOException e)
{
log.error("调用HttpUtils.sendSSLPost IOException, url=" + url + ",param=" + param, e);
}
catch (Exception e)
{
log.error("调用HttpsUtil.sendSSLPost Exception, url=" + url + ",param=" + param, e);
}
return result.toString();
}
private static class TrustAnyTrustManager implements X509TrustManager
{
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType)
{
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType)
{
}
@Override
public X509Certificate[] getAcceptedIssuers()
{
return new X509Certificate[] {};
}
}
private static class TrustAnyHostnameVerifier implements HostnameVerifier
{
@Override
public boolean verify(String hostname, SSLSession session)
{
return true;
}
}
}

View File

@ -1,56 +1,55 @@
package com.ruoyi.common.utils.ip;
import cn.hutool.core.net.NetUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import cn.hutool.http.HtmlUtil;
import cn.hutool.http.HttpUtil;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.utils.http.HttpUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.ruoyi.common.utils.JsonUtils;
import lombok.extern.slf4j.Slf4j;
import java.util.Map;
/**
* 获取地址类
*
* @author ruoyi
*/
public class AddressUtils
{
private static final Logger log = LoggerFactory.getLogger(AddressUtils.class);
@Slf4j
public class AddressUtils {
// IP地址查询
public static final String IP_URL = "http://whois.pconline.com.cn/ipJson.jsp";
// IP地址查询
public static final String IP_URL = "http://whois.pconline.com.cn/ipJson.jsp";
// 未知地址
public static final String UNKNOWN = "XX XX";
// 未知地址
public static final String UNKNOWN = "XX XX";
public static String getRealAddressByIP(String ip)
{
String address = UNKNOWN;
// 内网不查询
if (IpUtils.internalIp(ip))
{
return "内网IP";
}
if (RuoYiConfig.isAddressEnabled())
{
try
{
String rspStr = HttpUtils.sendGet(IP_URL, "ip=" + ip + "&json=true", Constants.GBK);
if (StrUtil.isEmpty(rspStr))
{
log.error("获取地理位置异常 {}", ip);
return UNKNOWN;
}
JSONObject obj = JSONObject.parseObject(rspStr);
String region = obj.getString("pro");
String city = obj.getString("city");
return String.format("%s %s", region, city);
}
catch (Exception e)
{
log.error("获取地理位置异常 {}", ip);
}
}
return address;
}
public static String getRealAddressByIP(String ip) {
String address = UNKNOWN;
// 内网不查询
ip = "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : HtmlUtil.cleanHtmlTag(ip);
if (NetUtil.isInnerIP(ip)) {
return "内网IP";
}
if (RuoYiConfig.isAddressEnabled()) {
try {
String rspStr = HttpUtil.createGet(IP_URL)
.body("ip=" + ip + "&json=true", Constants.GBK)
.execute()
.body();
if (StrUtil.isEmpty(rspStr)) {
log.error("获取地理位置异常 {}", ip);
return UNKNOWN;
}
Map<String, String> obj = JsonUtils.parseMap(rspStr);
String region = obj.get("pro");
String city = obj.get("city");
return String.format("%s %s", region, city);
} catch (Exception e) {
log.error("获取地理位置异常 {}", ip);
}
}
return address;
}
}

View File

@ -1,196 +0,0 @@
package com.ruoyi.common.utils.ip;
import cn.hutool.core.lang.Validator;
import cn.hutool.http.HtmlUtil;
import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.UnknownHostException;
/**
* 获取IP方法
*
* @author ruoyi
*/
public class IpUtils
{
public static String getIpAddr(HttpServletRequest request)
{
if (request == null)
{
return "unknown";
}
String ip = request.getHeader("x-forwarded-for");
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
{
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
{
ip = request.getHeader("X-Forwarded-For");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
{
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
{
ip = request.getHeader("X-Real-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
{
ip = request.getRemoteAddr();
}
return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : HtmlUtil.cleanHtmlTag(ip);
}
public static boolean internalIp(String ip)
{
byte[] addr = textToNumericFormatV4(ip);
return internalIp(addr) || "127.0.0.1".equals(ip);
}
private static boolean internalIp(byte[] addr)
{
if (Validator.isNull(addr) || addr.length < 2)
{
return true;
}
final byte b0 = addr[0];
final byte b1 = addr[1];
// 10.x.x.x/8
final byte SECTION_1 = 0x0A;
// 172.16.x.x/12
final byte SECTION_2 = (byte) 0xAC;
final byte SECTION_3 = (byte) 0x10;
final byte SECTION_4 = (byte) 0x1F;
// 192.168.x.x/16
final byte SECTION_5 = (byte) 0xC0;
final byte SECTION_6 = (byte) 0xA8;
switch (b0)
{
case SECTION_1:
return true;
case SECTION_2:
if (b1 >= SECTION_3 && b1 <= SECTION_4)
{
return true;
}
case SECTION_5:
switch (b1)
{
case SECTION_6:
return true;
}
default:
return false;
}
}
/**
* 将IPv4地址转换成字节
*
* @param text IPv4地址
* @return byte 字节
*/
public static byte[] textToNumericFormatV4(String text)
{
if (text.length() == 0)
{
return null;
}
byte[] bytes = new byte[4];
String[] elements = text.split("\\.", -1);
try
{
long l;
int i;
switch (elements.length)
{
case 1:
l = Long.parseLong(elements[0]);
if ((l < 0L) || (l > 4294967295L)) {
return null;
}
bytes[0] = (byte) (int) (l >> 24 & 0xFF);
bytes[1] = (byte) (int) ((l & 0xFFFFFF) >> 16 & 0xFF);
bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF);
bytes[3] = (byte) (int) (l & 0xFF);
break;
case 2:
l = Integer.parseInt(elements[0]);
if ((l < 0L) || (l > 255L)) {
return null;
}
bytes[0] = (byte) (int) (l & 0xFF);
l = Integer.parseInt(elements[1]);
if ((l < 0L) || (l > 16777215L)) {
return null;
}
bytes[1] = (byte) (int) (l >> 16 & 0xFF);
bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF);
bytes[3] = (byte) (int) (l & 0xFF);
break;
case 3:
for (i = 0; i < 2; ++i)
{
l = Integer.parseInt(elements[i]);
if ((l < 0L) || (l > 255L)) {
return null;
}
bytes[i] = (byte) (int) (l & 0xFF);
}
l = Integer.parseInt(elements[2]);
if ((l < 0L) || (l > 65535L)) {
return null;
}
bytes[2] = (byte) (int) (l >> 8 & 0xFF);
bytes[3] = (byte) (int) (l & 0xFF);
break;
case 4:
for (i = 0; i < 4; ++i)
{
l = Integer.parseInt(elements[i]);
if ((l < 0L) || (l > 255L)) {
return null;
}
bytes[i] = (byte) (int) (l & 0xFF);
}
break;
default:
return null;
}
}
catch (NumberFormatException e)
{
return null;
}
return bytes;
}
public static String getHostIp()
{
try
{
return InetAddress.getLocalHost().getHostAddress();
}
catch (UnknownHostException e)
{
}
return "127.0.0.1";
}
public static String getHostName()
{
try
{
return InetAddress.getLocalHost().getHostName();
}
catch (UnknownHostException e)
{
}
return "未知";
}
}

View File

@ -1,406 +1,54 @@
package com.ruoyi.common.utils.reflect;
import cn.hutool.core.convert.Convert;
import com.ruoyi.common.utils.DateUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.apache.poi.ss.usermodel.DateUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import java.lang.reflect.*;
import java.util.Date;
import java.lang.reflect.Method;
import java.util.List;
/**
* 反射工具类. 提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class, 被AOP过的真实类等工具函数.
*
* @author ruoyi
* @author Lion Li
*/
@SuppressWarnings("rawtypes")
public class ReflectUtils
{
private static final String SETTER_PREFIX = "set";
public class ReflectUtils extends ReflectUtil {
private static final String GETTER_PREFIX = "get";
private static final String SETTER_PREFIX = "set";
private static final String CGLIB_CLASS_SEPARATOR = "$$";
private static final String GETTER_PREFIX = "get";
private static Logger logger = LoggerFactory.getLogger(ReflectUtils.class);
/**
* 调用Getter方法.
* 支持多级对象名.对象名.方法
*/
@SuppressWarnings("unchecked")
public static <E> E invokeGetter(Object obj, String propertyName) {
Object object = obj;
for (String name : StrUtil.split(propertyName, ".")) {
String getterMethodName = GETTER_PREFIX + StrUtil.upperFirst(name);
object = invoke(object, getterMethodName);
}
return (E) object;
}
/**
* 调用Getter方法.
* 支持多级对象名.对象名.方法
*/
@SuppressWarnings("unchecked")
public static <E> E invokeGetter(Object obj, String propertyName)
{
Object object = obj;
for (String name : StringUtils.split(propertyName, "."))
{
String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(name);
object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {});
}
return (E) object;
}
/**
* 调用Setter方法, 仅匹配方法名
* 支持多级对象名.对象名.方法
*/
public static <E> void invokeSetter(Object obj, String propertyName, E value) {
Object object = obj;
List<String> names = StrUtil.split(propertyName, ".");
for (int i = 0; i < names.size(); i++) {
if (i < names.size() - 1) {
String getterMethodName = GETTER_PREFIX + StrUtil.upperFirst(names.get(i));
object = invoke(object, getterMethodName);
} else {
String setterMethodName = SETTER_PREFIX + StrUtil.upperFirst(names.get(i));
Method method = getMethodByName(object.getClass(), setterMethodName);
invoke(object, method, value);
}
}
}
/**
* 调用Setter方法, 仅匹配方法名
* 支持多级对象名.对象名.方法
*/
public static <E> void invokeSetter(Object obj, String propertyName, E value)
{
Object object = obj;
String[] names = StringUtils.split(propertyName, ".");
for (int i = 0; i < names.length; i++)
{
if (i < names.length - 1)
{
String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(names[i]);
object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {});
}
else
{
String setterMethodName = SETTER_PREFIX + StringUtils.capitalize(names[i]);
invokeMethodByName(object, setterMethodName, new Object[] { value });
}
}
}
/**
* 直接读取对象属性值, 无视private/protected修饰符, 不经过getter函数.
*/
@SuppressWarnings("unchecked")
public static <E> E getFieldValue(final Object obj, final String fieldName)
{
Field field = getAccessibleField(obj, fieldName);
if (field == null)
{
logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 ");
return null;
}
E result = null;
try
{
result = (E) field.get(obj);
}
catch (IllegalAccessException e)
{
logger.error("不可能抛出的异常{}", e.getMessage());
}
return result;
}
/**
* 直接设置对象属性值, 无视private/protected修饰符, 不经过setter函数.
*/
public static <E> void setFieldValue(final Object obj, final String fieldName, final E value)
{
Field field = getAccessibleField(obj, fieldName);
if (field == null)
{
// throw new IllegalArgumentException("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 ");
logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 ");
return;
}
try
{
field.set(obj, value);
}
catch (IllegalAccessException e)
{
logger.error("不可能抛出的异常: {}", e.getMessage());
}
}
/**
* 直接调用对象方法, 无视private/protected修饰符.
* 用于一次性调用的情况否则应使用getAccessibleMethod()函数获得Method后反复调用.
* 同时匹配方法名+参数类型
*/
@SuppressWarnings("unchecked")
public static <E> E invokeMethod(final Object obj, final String methodName, final Class<?>[] parameterTypes,
final Object[] args)
{
if (obj == null || methodName == null)
{
return null;
}
Method method = getAccessibleMethod(obj, methodName, parameterTypes);
if (method == null)
{
logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 ");
return null;
}
try
{
return (E) method.invoke(obj, args);
}
catch (Exception e)
{
String msg = "method: " + method + ", obj: " + obj + ", args: " + args + "";
throw convertReflectionExceptionToUnchecked(msg, e);
}
}
/**
* 直接调用对象方法, 无视private/protected修饰符
* 用于一次性调用的情况否则应使用getAccessibleMethodByName()函数获得Method后反复调用.
* 只匹配函数名如果有多个同名函数调用第一个
*/
@SuppressWarnings("unchecked")
public static <E> E invokeMethodByName(final Object obj, final String methodName, final Object[] args)
{
Method method = getAccessibleMethodByName(obj, methodName, args.length);
if (method == null)
{
// 如果为空不报错,直接返回空。
logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 ");
return null;
}
try
{
// 类型转换(将参数数据类型转换为目标方法参数类型)
Class<?>[] cs = method.getParameterTypes();
for (int i = 0; i < cs.length; i++)
{
if (args[i] != null && !args[i].getClass().equals(cs[i]))
{
if (cs[i] == String.class)
{
args[i] = Convert.toStr(args[i]);
if (StringUtils.endsWith((String) args[i], ".0"))
{
args[i] = StringUtils.substringBefore((String) args[i], ".0");
}
}
else if (cs[i] == Integer.class)
{
args[i] = Convert.toInt(args[i]);
}
else if (cs[i] == Long.class)
{
args[i] = Convert.toLong(args[i]);
}
else if (cs[i] == Double.class)
{
args[i] = Convert.toDouble(args[i]);
}
else if (cs[i] == Float.class)
{
args[i] = Convert.toFloat(args[i]);
}
else if (cs[i] == Date.class)
{
if (args[i] instanceof String)
{
args[i] = DateUtils.parseDate(args[i]);
}
else
{
args[i] = DateUtil.getJavaDate((Double) args[i]);
}
}
else if (cs[i] == boolean.class || cs[i] == Boolean.class)
{
args[i] = Convert.toBool(args[i]);
}
}
}
return (E) method.invoke(obj, args);
}
catch (Exception e)
{
String msg = "method: " + method + ", obj: " + obj + ", args: " + args + "";
throw convertReflectionExceptionToUnchecked(msg, e);
}
}
/**
* 循环向上转型, 获取对象的DeclaredField, 并强制设置为可访问.
* 如向上转型到Object仍无法找到, 返回null.
*/
public static Field getAccessibleField(final Object obj, final String fieldName)
{
// 为空不报错。直接返回 null
if (obj == null)
{
return null;
}
Validate.notBlank(fieldName, "fieldName can't be blank");
for (Class<?> superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass())
{
try
{
Field field = superClass.getDeclaredField(fieldName);
makeAccessible(field);
return field;
}
catch (NoSuchFieldException e)
{
continue;
}
}
return null;
}
/**
* 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问.
* 如向上转型到Object仍无法找到, 返回null.
* 匹配函数名+参数类型
* 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args)
*/
public static Method getAccessibleMethod(final Object obj, final String methodName,
final Class<?>... parameterTypes)
{
// 为空不报错。直接返回 null
if (obj == null)
{
return null;
}
Validate.notBlank(methodName, "methodName can't be blank");
for (Class<?> searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass())
{
try
{
Method method = searchType.getDeclaredMethod(methodName, parameterTypes);
makeAccessible(method);
return method;
}
catch (NoSuchMethodException e)
{
continue;
}
}
return null;
}
/**
* 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问.
* 如向上转型到Object仍无法找到, 返回null.
* 只匹配函数名
* 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args)
*/
public static Method getAccessibleMethodByName(final Object obj, final String methodName, int argsNum)
{
// 为空不报错。直接返回 null
if (obj == null)
{
return null;
}
Validate.notBlank(methodName, "methodName can't be blank");
for (Class<?> searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass())
{
Method[] methods = searchType.getDeclaredMethods();
for (Method method : methods)
{
if (method.getName().equals(methodName) && method.getParameterTypes().length == argsNum)
{
makeAccessible(method);
return method;
}
}
}
return null;
}
/**
* 改变private/protected的方法为public尽量不调用实际改动的语句避免JDK的SecurityManager抱怨
*/
public static void makeAccessible(Method method)
{
if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers()))
&& !method.isAccessible())
{
method.setAccessible(true);
}
}
/**
* 改变private/protected的成员变量为public尽量不调用实际改动的语句避免JDK的SecurityManager抱怨
*/
public static void makeAccessible(Field field)
{
if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers())
|| Modifier.isFinal(field.getModifiers())) && !field.isAccessible())
{
field.setAccessible(true);
}
}
/**
* 通过反射, 获得Class定义中声明的泛型参数的类型, 注意泛型必须定义在父类处
* 如无法找到, 返回Object.class.
*/
@SuppressWarnings("unchecked")
public static <T> Class<T> getClassGenricType(final Class clazz)
{
return getClassGenricType(clazz, 0);
}
/**
* 通过反射, 获得Class定义中声明的父类的泛型参数的类型.
* 如无法找到, 返回Object.class.
*/
public static Class getClassGenricType(final Class clazz, final int index)
{
Type genType = clazz.getGenericSuperclass();
if (!(genType instanceof ParameterizedType))
{
logger.debug(clazz.getSimpleName() + "'s superclass not ParameterizedType");
return Object.class;
}
Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
if (index >= params.length || index < 0)
{
logger.debug("Index: " + index + ", Size of " + clazz.getSimpleName() + "'s Parameterized Type: "
+ params.length);
return Object.class;
}
if (!(params[index] instanceof Class))
{
logger.debug(clazz.getSimpleName() + " not set the actual class on superclass generic parameter");
return Object.class;
}
return (Class) params[index];
}
public static Class<?> getUserClass(Object instance)
{
if (instance == null)
{
throw new RuntimeException("Instance must not be null");
}
Class clazz = instance.getClass();
if (clazz != null && clazz.getName().contains(CGLIB_CLASS_SEPARATOR))
{
Class<?> superClass = clazz.getSuperclass();
if (superClass != null && !Object.class.equals(superClass))
{
return superClass;
}
}
return clazz;
}
/**
* 将反射时的checked exception转换为unchecked exception.
*/
public static RuntimeException convertReflectionExceptionToUnchecked(String msg, Exception e)
{
if (e instanceof IllegalAccessException || e instanceof IllegalArgumentException
|| e instanceof NoSuchMethodException)
{
return new IllegalArgumentException(msg, e);
}
else if (e instanceof InvocationTargetException)
{
return new RuntimeException(msg, ((InvocationTargetException) e).getTargetException());
}
return new RuntimeException(msg, e);
}
}

View File

@ -1,146 +1,65 @@
package com.ruoyi.common.utils.spring;
import cn.hutool.core.lang.Validator;
import cn.hutool.extra.spring.SpringUtil;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
/**
* spring工具类 方便在非spring管理环境中获取bean
* spring工具类
*
* @author ruoyi
* @author Lion Li
*/
@Component
public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationContextAware
{
/** Spring应用上下文环境 */
private static ConfigurableListableBeanFactory beanFactory;
public final class SpringUtils extends SpringUtil {
private static ApplicationContext applicationContext;
/**
* 如果BeanFactory包含一个与所给名称匹配的bean定义则返回true
*
* @param name
* @return boolean
*/
public static boolean containsBean(String name) {
return getBeanFactory().containsBean(name);
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException
{
SpringUtils.beanFactory = beanFactory;
}
/**
* 判断以给定名字注册的bean定义是一个singleton还是一个prototype
* 如果与给定名字相应的bean定义没有被找到将会抛出一个异常NoSuchBeanDefinitionException
*
* @param name
* @return boolean
*/
public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException {
return getBeanFactory().isSingleton(name);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
{
SpringUtils.applicationContext = applicationContext;
}
/**
* @param name
* @return Class 注册对象的类型
*/
public static Class<?> getType(String name) throws NoSuchBeanDefinitionException {
return getBeanFactory().getType(name);
}
/**
* 获取对象
*
* @param name
* @return Object 一个以所给名字注册的bean的实例
* @throws org.springframework.beans.BeansException
*
*/
@SuppressWarnings("unchecked")
public static <T> T getBean(String name) throws BeansException
{
return (T) beanFactory.getBean(name);
}
/**
* 如果给定的bean名字在bean定义中有别名则返回这些别名
*
* @param name
*/
public static String[] getAliases(String name) throws NoSuchBeanDefinitionException {
return getBeanFactory().getAliases(name);
}
/**
* 获取类型为requiredType的对象
*
* @param clz
* @return
* @throws org.springframework.beans.BeansException
*
*/
public static <T> T getBean(Class<T> clz) throws BeansException
{
T result = (T) beanFactory.getBean(clz);
return result;
}
/**
* 获取aop代理对象
*
* @param invoker
* @return
*/
@SuppressWarnings("unchecked")
public static <T> T getAopProxy(T invoker) {
return (T) AopContext.currentProxy();
}
/**
* 如果BeanFactory包含一个与所给名称匹配的bean定义则返回true
*
* @param name
* @return boolean
*/
public static boolean containsBean(String name)
{
return beanFactory.containsBean(name);
}
/**
* 判断以给定名字注册的bean定义是一个singleton还是一个prototype 如果与给定名字相应的bean定义没有被找到将会抛出一个异常NoSuchBeanDefinitionException
*
* @param name
* @return boolean
* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
*
*/
public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException
{
return beanFactory.isSingleton(name);
}
/**
* @param name
* @return Class 注册对象的类型
* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
*
*/
public static Class<?> getType(String name) throws NoSuchBeanDefinitionException
{
return beanFactory.getType(name);
}
/**
* 如果给定的bean名字在bean定义中有别名则返回这些别名
*
* @param name
* @return
* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
*
*/
public static String[] getAliases(String name) throws NoSuchBeanDefinitionException
{
return beanFactory.getAliases(name);
}
/**
* 获取aop代理对象
*
* @param invoker
* @return
*/
@SuppressWarnings("unchecked")
public static <T> T getAopProxy(T invoker)
{
return (T) AopContext.currentProxy();
}
/**
* 获取当前的环境配置无配置返回null
*
* @return 当前的环境配置
*/
public static String[] getActiveProfiles()
{
return applicationContext.getEnvironment().getActiveProfiles();
}
/**
* 获取当前的环境配置当有多个环境配置时只获取第一个
*
* @return 当前的环境配置
*/
public static String getActiveProfile()
{
final String[] activeProfiles = getActiveProfiles();
return Validator.isNotEmpty(activeProfiles) ? activeProfiles[0] : null;
}
}

View File

@ -5,7 +5,7 @@ import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import java.util.Date;
/**
@ -40,4 +40,8 @@ public class TestDemoAddBo {
@NotBlank(message = "值不能为空")
private String value;
/** 创建时间 */
@ApiModelProperty("创建时间")
private Date createTime;
}

View File

@ -1,8 +1,10 @@
package com.ruoyi.demo.controller;
import com.ruoyi.common.annotation.RedisLock;
import com.baomidou.lock.LockInfo;
import com.baomidou.lock.LockTemplate;
import com.baomidou.lock.annotation.Lock4j;
import com.baomidou.lock.executor.RedissonLockExecutor;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.redis.RedisLockManager;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
@ -10,7 +12,7 @@ import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.TimeUnit;
import java.time.LocalTime;
/**
@ -24,44 +26,47 @@ import java.util.concurrent.TimeUnit;
public class RedisLockController {
@Autowired
private RedisLockManager redisLockManager;
private LockTemplate lockTemplate;
/**
* #p0 标识取第一个参数为redis锁的key
* 测试lock4j 注解
*/
@GetMapping("/testLock1")
@RedisLock(expireTime = 10, key = "#p0")
public AjaxResult<String> testLock1(String key, String value) {
@Lock4j(keys = {"#key"})
@GetMapping("/testLock4j")
public AjaxResult<String> testLock4j(String key,String value){
System.out.println("start:"+key+",time:"+ LocalTime.now().toString());
try {
// 同时请求排队
// Thread.sleep(5000);
// 锁超时测试
Thread.sleep(11000);
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("end :"+key+",time:"+LocalTime.now().toString());
return AjaxResult.success("操作成功",value);
}
/**
* 测试锁工具类
* 测试lock4j 工具
*/
@GetMapping("/testLock2")
public AjaxResult<Void> testLock(String key, Long time) {
try {
boolean flag = redisLockManager.getLock(key, time, TimeUnit.SECONDS);
if (flag) {
log.info("获取锁成功: " + key);
Thread.sleep(3000);
redisLockManager.unLock(key);
log.info("释放锁成功: " + key);
} else {
log.error("获取锁失败: " + key);
}
} catch (InterruptedException e) {
log.error(e.getMessage());
@GetMapping("/testLock4jLockTemaplate")
public AjaxResult<String> testLock4jLockTemaplate(String key,String value){
final LockInfo lockInfo = lockTemplate.lock(key, 30000L, 5000L, RedissonLockExecutor.class);
if (null == lockInfo) {
throw new RuntimeException("业务处理中,请稍后再试");
}
return AjaxResult.success();
// 获取锁成功,处理业务
try {
try {
Thread.sleep(8000);
} catch (InterruptedException e) {
//
}
System.out.println("执行简单方法1 , 当前线程:" + Thread.currentThread().getName());
} finally {
//释放锁
lockTemplate.releaseLock(lockInfo);
}
//结束
return AjaxResult.success("操作成功",value);
}
/**

View File

@ -0,0 +1,52 @@
package com.ruoyi.demo.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.demo.domain.TestDemo;
import com.ruoyi.demo.service.ITestDemoService;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
/**
* 测试批量方法
*
* @author Lion Li
* @date 2021-05-30
*/
@RequiredArgsConstructor(onConstructor_ = @Autowired)
@RestController
@RequestMapping("/demo/batch")
public class TestBatchController extends BaseController {
private final ITestDemoService iTestDemoService;
/**
* 新增批量方法
*/
@PostMapping()
public AjaxResult<Void> add() {
List<TestDemo> list = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
list.add(new TestDemo().setOrderNum(-1L).setTestKey("批量新增").setValue("测试新增"));
}
return toAjax(iTestDemoService.saveAll(list) ? 1 : 0);
}
/**
* 修改批量方法
*/
@DeleteMapping()
public AjaxResult<Void> edit() {
return toAjax(iTestDemoService.remove(new LambdaQueryWrapper<TestDemo>()
.eq(TestDemo::getOrderNum, -1L)) ? 1 : 0);
}
}

View File

@ -1,6 +1,8 @@
package com.ruoyi.demo.domain;
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
@ -23,8 +25,11 @@ public class TestDemo implements Serializable {
private static final long serialVersionUID=1L;
/** 主键 */
/**
* 主键
*/
@TableId(value = "id")
@JsonSerialize(using = ToStringSerializer.class)
private Long id;
/** 部门id */
@ -64,7 +69,6 @@ public class TestDemo implements Serializable {
private String updateBy;
/** 删除标志 */
@TableLogic
private Long delFlag;
}

View File

@ -1,13 +1,14 @@
package com.ruoyi.demo.domain;
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.Date;
import java.math.BigDecimal;
import com.ruoyi.common.annotation.Excel;
/**
* 测试树表对象 test_tree
@ -26,6 +27,7 @@ public class TestTree implements Serializable {
/** 主键 */
@TableId(value = "id")
@JsonSerialize(using = ToStringSerializer.class)
private Long id;
/** 父id */

View File

@ -1,5 +1,6 @@
package com.ruoyi.demo.feign;
import com.ruoyi.demo.feign.constant.FeignTestConstant;
import com.ruoyi.demo.feign.fallback.FeignTestFallback;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
@ -7,10 +8,15 @@ import org.springframework.web.bind.annotation.RequestParam;
/**
* feign测试service
*
* 规范接口 Service 无感调用
* 常量管理请求路径 更加规范
* 自定义容错处理 安全可靠
* @author Lion Li
*/
@FeignClient(name = "baidu",url = "http://www.baidu.com",fallback = FeignTestFallback.class)
@FeignClient(
name = FeignTestConstant.BAIDU_NAME,
url = FeignTestConstant.BAIDU_URL,
fallback = FeignTestFallback.class)
public interface FeignTestService {
@GetMapping("/s")

View File

@ -0,0 +1,9 @@
package com.ruoyi.demo.feign.constant;
public class FeignTestConstant {
public static final String BAIDU_NAME = "baidu";
public static final String BAIDU_URL = "http://www.baidu.com";
}

View File

@ -1,7 +1,7 @@
package com.ruoyi.demo.mapper;
import com.ruoyi.common.core.mybatisplus.MybatisPlusRedisCache;
import com.ruoyi.common.core.page.BaseMapperPlus;
import com.ruoyi.common.core.mybatisplus.cache.MybatisPlusRedisCache;
import com.ruoyi.common.core.mybatisplus.core.BaseMapperPlus;
import com.ruoyi.demo.domain.TestDemo;
import org.apache.ibatis.annotations.CacheNamespace;
@ -11,6 +11,7 @@ import org.apache.ibatis.annotations.CacheNamespace;
* @author Lion Li
* @date 2021-05-30
*/
// 如使需切换数据源 请勿使用缓存 会造成数据不一致现象
@CacheNamespace(implementation = MybatisPlusRedisCache.class, eviction = MybatisPlusRedisCache.class)
public interface TestDemoMapper extends BaseMapperPlus<TestDemo> {

View File

@ -1,9 +1,7 @@
package com.ruoyi.demo.mapper;
import com.ruoyi.common.core.mybatisplus.MybatisPlusRedisCache;
import com.ruoyi.common.core.page.BaseMapperPlus;
import com.ruoyi.common.core.mybatisplus.core.BaseMapperPlus;
import com.ruoyi.demo.domain.TestTree;
import org.apache.ibatis.annotations.CacheNamespace;
/**
* 测试树表Mapper接口
@ -11,7 +9,7 @@ import org.apache.ibatis.annotations.CacheNamespace;
* @author Lion Li
* @date 2021-05-30
*/
@CacheNamespace(implementation = MybatisPlusRedisCache.class, eviction = MybatisPlusRedisCache.class)
//@CacheNamespace(implementation = MybatisPlusRedisCache.class, eviction = MybatisPlusRedisCache.class)
public interface TestTreeMapper extends BaseMapperPlus<TestTree> {
}

View File

@ -5,7 +5,7 @@ import com.ruoyi.demo.vo.TestDemoVo;
import com.ruoyi.demo.bo.TestDemoQueryBo;
import com.ruoyi.demo.bo.TestDemoAddBo;
import com.ruoyi.demo.bo.TestDemoEditBo;
import com.ruoyi.common.core.page.IServicePlus;
import com.ruoyi.common.core.mybatisplus.core.IServicePlus;
import com.ruoyi.common.core.page.TableDataInfo;
import java.util.Collection;
@ -18,6 +18,7 @@ import java.util.List;
* @date 2021-05-30
*/
public interface ITestDemoService extends IServicePlus<TestDemo> {
/**
* 查询单个
* @return

View File

@ -1,6 +1,6 @@
package com.ruoyi.demo.service;
import com.ruoyi.common.core.page.IServicePlus;
import com.ruoyi.common.core.mybatisplus.core.IServicePlus;
import com.ruoyi.demo.bo.TestTreeAddBo;
import com.ruoyi.demo.bo.TestTreeEditBo;
import com.ruoyi.demo.bo.TestTreeQueryBo;

View File

@ -4,8 +4,8 @@ import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.annotation.DataScope;
import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl;
import com.ruoyi.common.core.page.PagePlus;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.utils.PageUtils;
@ -29,7 +29,7 @@ import java.util.Map;
* @date 2021-05-30
*/
@Service
public class TestDemoServiceImpl extends ServiceImpl<TestDemoMapper, TestDemo> implements ITestDemoService {
public class TestDemoServiceImpl extends ServicePlusImpl<TestDemoMapper, TestDemo> implements ITestDemoService {
@Override
public TestDemoVo queryById(Long id) {

View File

@ -4,8 +4,8 @@ import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.annotation.DataScope;
import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl;
import com.ruoyi.demo.bo.TestTreeAddBo;
import com.ruoyi.demo.bo.TestTreeEditBo;
import com.ruoyi.demo.bo.TestTreeQueryBo;
@ -26,13 +26,14 @@ import java.util.Map;
* @date 2021-05-30
*/
@Service
public class TestTreeServiceImpl extends ServiceImpl<TestTreeMapper, TestTree> implements ITestTreeService {
public class TestTreeServiceImpl extends ServicePlusImpl<TestTreeMapper, TestTree> implements ITestTreeService {
@Override
public TestTreeVo queryById(Long id) {
return getVoById(id, TestTreeVo.class);
}
// @DataSource(DataSourceType.SLAVE) // 切换从库查询
@DataScope(isUser = true)
@Override
public List<TestTreeVo> queryList(TestTreeQueryBo bo) {

View File

@ -1,10 +1,12 @@
package com.ruoyi.demo.vo;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.ruoyi.common.annotation.Excel;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
@ -21,8 +23,13 @@ public class TestDemoVo {
private static final long serialVersionUID = 1L;
/** 主键 */
/**
* 主键
* 如果是自定义id 或者 雪花id
* 需要增加序列化为字符串注解 因为Long到前端会失真
*/
@ApiModelProperty("主键")
@JsonSerialize(using = ToStringSerializer.class)
private Long id;
/** 部门id */
@ -52,7 +59,7 @@ public class TestDemoVo {
/** 创建时间 */
@Excel(name = "创建时间" , width = 30, dateFormat = "yyyy-MM-dd")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
// @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@ApiModelProperty("创建时间")
private Date createTime;
@ -63,7 +70,7 @@ public class TestDemoVo {
/** 更新时间 */
@Excel(name = "更新时间" , width = 30, dateFormat = "yyyy-MM-dd")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
// @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@ApiModelProperty("更新时间")
private Date updateTime;

View File

@ -1,10 +1,13 @@
package com.ruoyi.demo.vo;
import com.ruoyi.common.annotation.Excel;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.ruoyi.common.annotation.Excel;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
@ -23,6 +26,7 @@ public class TestTreeVo {
/** 主键 */
@ApiModelProperty("主键")
@JsonSerialize(using = ToStringSerializer.class)
private Long id;
/** 父id */

View File

@ -1,15 +1,13 @@
package com.ruoyi.framework.aspectj;
import cn.hutool.core.lang.Validator;
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
import com.ruoyi.common.annotation.DataSource;
import com.ruoyi.framework.datasource.DynamicDataSourceContextHolder;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@ -22,52 +20,43 @@ import java.util.Objects;
* @author ruoyi
*/
@Aspect
@Order(1)
@Order(-500)
@Component
public class DataSourceAspect
{
protected Logger logger = LoggerFactory.getLogger(getClass());
public class DataSourceAspect {
@Pointcut("@annotation(com.ruoyi.common.annotation.DataSource)"
+ "|| @within(com.ruoyi.common.annotation.DataSource)")
public void dsPointCut()
{
@Pointcut("@annotation(com.ruoyi.common.annotation.DataSource)"
+ "|| @within(com.ruoyi.common.annotation.DataSource)")
public void dsPointCut() {
}
}
@Around("dsPointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
DataSource dataSource = getDataSource(point);
@Around("dsPointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable
{
DataSource dataSource = getDataSource(point);
if (Validator.isNotNull(dataSource)) {
DynamicDataSourceContextHolder.poll();
String source = dataSource.value().getSource();
DynamicDataSourceContextHolder.push(source);
}
if (Validator.isNotNull(dataSource))
{
DynamicDataSourceContextHolder.setDataSourceType(dataSource.value().name());
}
try {
return point.proceed();
} finally {
// 销毁数据源 在执行方法之后
DynamicDataSourceContextHolder.clear();
}
}
try
{
return point.proceed();
}
finally
{
// 销毁数据源 在执行方法之后
DynamicDataSourceContextHolder.clearDataSourceType();
}
}
/**
* 获取需要切换的数据源
*/
public DataSource getDataSource(ProceedingJoinPoint point) {
MethodSignature signature = (MethodSignature) point.getSignature();
DataSource dataSource = AnnotationUtils.findAnnotation(signature.getMethod(), DataSource.class);
if (Objects.nonNull(dataSource)) {
return dataSource;
}
/**
* 获取需要切换的数据源
*/
public DataSource getDataSource(ProceedingJoinPoint point)
{
MethodSignature signature = (MethodSignature) point.getSignature();
DataSource dataSource = AnnotationUtils.findAnnotation(signature.getMethod(), DataSource.class);
if (Objects.nonNull(dataSource))
{
return dataSource;
}
return AnnotationUtils.findAnnotation(signature.getDeclaringType(), DataSource.class);
}
return AnnotationUtils.findAnnotation(signature.getDeclaringType(), DataSource.class);
}
}

View File

@ -2,16 +2,14 @@ package com.ruoyi.framework.aspectj;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.enums.BusinessStatus;
import com.ruoyi.common.enums.HttpMethod;
import com.ruoyi.common.utils.JsonUtils;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.ip.IpUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.framework.manager.AsyncManager;
import com.ruoyi.framework.manager.factory.AsyncFactory;
import com.ruoyi.framework.web.service.AsyncService;
import com.ruoyi.framework.web.service.TokenService;
import com.ruoyi.system.domain.SysOperLog;
import org.aspectj.lang.JoinPoint;
@ -32,7 +30,6 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
/**
@ -93,10 +90,10 @@ public class LogAspect
SysOperLog operLog = new SysOperLog();
operLog.setStatus(BusinessStatus.SUCCESS.ordinal());
// 请求的地址
String ip = IpUtils.getIpAddr(ServletUtils.getRequest());
String ip = ServletUtils.getClientIP();
operLog.setOperIp(ip);
// 返回参数
operLog.setJsonResult(JSON.toJSONString(jsonResult));
operLog.setJsonResult(JsonUtils.toJsonString(jsonResult));
operLog.setOperUrl(ServletUtils.getRequest().getRequestURI());
if (loginUser != null)
@ -118,7 +115,7 @@ public class LogAspect
// 处理设置注解上的参数
getControllerMethodDescription(joinPoint, controllerLog, operLog);
// 保存数据库
AsyncManager.me().execute(AsyncFactory.recordOper(operLog));
SpringUtils.getBean(AsyncService.class).recordOper(operLog);
}
catch (Exception exp)
{
@ -194,19 +191,16 @@ public class LogAspect
*/
private String argsArrayToString(Object[] paramsArray)
{
String params = "";
StringBuilder params = new StringBuilder();
if (paramsArray != null && paramsArray.length > 0)
{
for (int i = 0; i < paramsArray.length; i++)
{
if (Validator.isNotNull(paramsArray[i]) && !isFilterObject(paramsArray[i]))
{
Object jsonObj = JSON.toJSON(paramsArray[i]);
params += jsonObj.toString() + " ";
}
}
for (Object o : paramsArray) {
if (Validator.isNotNull(o) && !isFilterObject(o)) {
params.append(JsonUtils.toJsonString(o)).append(" ");
}
}
}
return params.trim();
return params.toString().trim();
}
/**
@ -226,19 +220,17 @@ public class LogAspect
else if (Collection.class.isAssignableFrom(clazz))
{
Collection collection = (Collection) o;
for (Iterator iter = collection.iterator(); iter.hasNext();)
{
return iter.next() instanceof MultipartFile;
}
for (Object value : collection) {
return value instanceof MultipartFile;
}
}
else if (Map.class.isAssignableFrom(clazz))
{
Map map = (Map) o;
for (Iterator iter = map.entrySet().iterator(); iter.hasNext();)
{
Map.Entry entry = (Map.Entry) iter.next();
return entry.getValue() instanceof MultipartFile;
}
for (Object value : map.entrySet()) {
Map.Entry entry = (Map.Entry) value;
return entry.getValue() instanceof MultipartFile;
}
}
return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse
|| o instanceof BindingResult;

View File

@ -1,139 +0,0 @@
package com.ruoyi.framework.aspectj;
import com.ruoyi.common.annotation.RedisLock;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.redis.RedisLockManager;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* 分布式锁注解实现版本
*
* @author shenxinquan
*/
@Slf4j
@Aspect
@Order(9)
@Component
public class RedisLockAspect {
@Autowired
private RedisLockManager redisLockManager;
@Pointcut("@annotation(com.ruoyi.common.annotation.RedisLock)")
public void annotationPointcut() {
}
@Around("annotationPointcut()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
// 获得当前访问的class
Class<?> className = joinPoint.getTarget().getClass();
// 获得访问的方法名
String methodName = joinPoint.getSignature().getName();
// 得到方法的参数的类型
Class<?>[] argClass = ((MethodSignature) joinPoint.getSignature()).getParameterTypes();
Object[] args = joinPoint.getArgs();
String key = "";
// 默认30秒过期时间
int expireTime = 30;
try {
// 得到访问的方法对象
Method method = className.getMethod(methodName, argClass);
method.setAccessible(true);
// 判断是否存在@RedisLock注解
if (method.isAnnotationPresent(RedisLock.class)) {
RedisLock annotation = method.getAnnotation(RedisLock.class);
key = getRedisKey(args, annotation.key());
expireTime = getExpireTime(annotation);
}
} catch (Exception e) {
throw new RuntimeException("redis分布式锁注解参数异常", e);
}
// 声明锁名称
key = Constants.REDIS_LOCK_KEY + key;
Object res;
try {
if (redisLockManager.getLock(key, expireTime, TimeUnit.SECONDS)) {
log.info("lock => key : " + key + " , ThreadName : " + Thread.currentThread().getName());
try {
res = joinPoint.proceed();
return res;
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
redisLockManager.unLock(key);
log.info("unlock => key : " + key + " , ThreadName : " + Thread.currentThread().getName());
}
} else {
throw new RuntimeException("redis分布式锁注解参数异常");
}
} catch (IllegalMonitorStateException e) {
log.error("lock timeout => key : " + key + " , ThreadName : " + Thread.currentThread().getName());
throw new RuntimeException("lock timeout => key : " + key);
} catch (Exception e) {
throw new Exception("redis分布式未知异常", e);
}
}
private int getExpireTime(RedisLock annotation) {
return annotation.expireTime();
}
private String getRedisKey(Object[] args, String primalKey) {
if (args.length == 0) {
return primalKey;
}
// 获取#p0...集合
List<String> keyList = getKeyParsList(primalKey);
for (String keyName : keyList) {
int keyIndex = Integer.parseInt(keyName.toLowerCase().replace("#p", ""));
Object parValue = args[keyIndex];
primalKey = primalKey.replace(keyName, String.valueOf(parValue));
}
return primalKey.replace("+", "").replace("'", "");
}
/**
* 获取key中#p0中的参数名称
*/
private static List<String> getKeyParsList(String key) {
List<String> listPar = new ArrayList<>();
if (key.contains("#")) {
int plusIndex = key.substring(key.indexOf("#")).indexOf("+");
int indexNext = 0;
String parName;
int indexPre = key.indexOf("#");
if (plusIndex > 0) {
indexNext = key.indexOf("#") + plusIndex;
parName = key.substring(indexPre, indexNext);
} else {
parName = key.substring(indexPre);
}
listPar.add(parName.trim());
key = key.substring(indexNext + 1);
if (key.contains("#")) {
listPar.addAll(getKeyParsList(key));
}
}
return listPar;
}
}

View File

@ -2,6 +2,8 @@ package com.ruoyi.framework.config;
import com.ruoyi.common.exception.CustomException;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurerSupport;
import org.springframework.scheduling.annotation.EnableAsync;
@ -9,7 +11,7 @@ import org.springframework.security.concurrent.DelegatingSecurityContextExecutor
import java.util.Arrays;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
/**
* 异步配置
@ -20,13 +22,16 @@ import java.util.concurrent.Executors;
@Configuration
public class AsyncConfig extends AsyncConfigurerSupport {
@Autowired
@Qualifier("scheduledExecutorService")
private ScheduledExecutorService scheduledExecutorService;
/**
* 异步执行需要使用权限框架自带的包装线程池 保证权限信息的传递
*/
@Override
public Executor getAsyncExecutor() {
return new DelegatingSecurityContextExecutorService(
Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()));
return new DelegatingSecurityContextExecutorService(scheduledExecutorService);
}
/**

View File

@ -1,28 +1,14 @@
package com.ruoyi.framework.config;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.sql.DataSource;
import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties;
import com.alibaba.druid.util.Utils;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties;
import com.alibaba.druid.util.Utils;
import com.ruoyi.common.enums.DataSourceType;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.framework.config.properties.DruidProperties;
import com.ruoyi.framework.datasource.DynamicDataSource;
import javax.servlet.*;
import java.io.IOException;
/**
* druid 配置多数据源
@ -30,53 +16,7 @@ import com.ruoyi.framework.datasource.DynamicDataSource;
* @author ruoyi
*/
@Configuration
public class DruidConfig
{
@Bean
@ConfigurationProperties("spring.datasource.druid.master")
public DataSource masterDataSource(DruidProperties druidProperties)
{
DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
return druidProperties.dataSource(dataSource);
}
@Bean
@ConfigurationProperties("spring.datasource.druid.slave")
@ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true")
public DataSource slaveDataSource(DruidProperties druidProperties)
{
DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
return druidProperties.dataSource(dataSource);
}
@Bean(name = "dynamicDataSource")
@Primary
public DynamicDataSource dataSource(DataSource masterDataSource)
{
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource);
setDataSource(targetDataSources, DataSourceType.SLAVE.name(), "slaveDataSource");
return new DynamicDataSource(masterDataSource, targetDataSources);
}
/**
* 设置数据源
*
* @param targetDataSources 备选数据源集合
* @param sourceName 数据源名称
* @param beanName bean名称
*/
public void setDataSource(Map<Object, Object> targetDataSources, String sourceName, String beanName)
{
try
{
DataSource dataSource = SpringUtils.getBean(beanName);
targetDataSources.put(sourceName, dataSource);
}
catch (Exception e)
{
}
}
public class DruidConfig {
/**
* 去除监控页面底部的广告

View File

@ -1,7 +1,6 @@
package com.ruoyi.framework.config;
import feign.*;
import feign.hystrix.HystrixFeign;
import okhttp3.ConnectionPool;
import okhttp3.OkHttpClient;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
@ -35,11 +34,6 @@ public class FeignConfig {
.build();
}
@Bean
public Feign.Builder feignBuilder() {
return HystrixFeign.builder();
}
@Bean
public Contract feignContract() {
return new SpringMvcContract();

View File

@ -30,7 +30,7 @@ public class FilterConfig {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setDispatcherTypes(DispatcherType.REQUEST);
registration.setFilter(new XssFilter());
registration.addUrlPatterns(StrUtil.split(xssProperties.getUrlPatterns(), ","));
registration.addUrlPatterns(StrUtil.splitToArray(xssProperties.getUrlPatterns(), ","));
registration.setName("xssFilter");
registration.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE);
Map<String, String> initParameters = new HashMap<String, String>();

View File

@ -2,35 +2,47 @@ package com.ruoyi.framework.config;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.ruoyi.common.utils.JsonUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import java.time.LocalDateTime;
import java.util.TimeZone;
/**
* 当Mybatis plus设置为雪花ID时
* 使用此类会把所有数字返回变为字符串返回适配前端Long型失真问题
* jackson 配置
*
* @author Ming LI
* @author Lion Li
*/
@Slf4j
@Configuration
public class JacksonConfig {
@Bean
@Primary
@ConditionalOnMissingBean(ObjectMapper.class)
@ConditionalOnProperty(value = "mybatis-plus.global-config.dbConfig.idType", havingValue = "ASSIGN_ID")
public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
ObjectMapper objectMapper = builder.createXmlMapper(false).build();
// 全局配置序列化返回 JSON 处理
SimpleModule simpleModule = new SimpleModule();
//JSON Long ==> String
simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
objectMapper.registerModule(simpleModule);
return objectMapper;
public BeanPostProcessor objectMapperBeanPostProcessor() {
return new BeanPostProcessor() {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (!(bean instanceof ObjectMapper)) {
return bean;
}
ObjectMapper objectMapper = (ObjectMapper) bean;
// 全局配置序列化返回 JSON 处理
SimpleModule simpleModule = new SimpleModule();
simpleModule.addSerializer(LocalDateTime.class, LocalDateTimeSerializer.INSTANCE);
simpleModule.addDeserializer(LocalDateTime.class, LocalDateTimeDeserializer.INSTANCE);
objectMapper.registerModule(simpleModule);
objectMapper.setTimeZone(TimeZone.getDefault());
JsonUtils.init(objectMapper);
log.info("初始化 jackson 配置");
return bean;
}
};
}
}

View File

@ -2,15 +2,21 @@ package com.ruoyi.framework.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector;
import com.baomidou.mybatisplus.core.injector.ISqlInjector;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.ruoyi.common.core.mybatisplus.methods.InsertAll;
import com.ruoyi.framework.mybatisplus.CreateAndUpdateMetaObjectHandler;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import java.util.List;
/**
* mybatis-plus配置类
*
@ -94,10 +100,17 @@ public class MybatisPlusConfig {
* sql注入器配置
* https://baomidou.com/guide/sql-injector.html
*/
// @Bean
// public ISqlInjector sqlInjector() {
// return new DefaultSqlInjector();
// }
@Bean
public ISqlInjector sqlInjector() {
return new DefaultSqlInjector() {
@Override
public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
List<AbstractMethod> methodList = super.getMethodList(mapperClass);
methodList.add(new InsertAll());
return methodList;
}
};
}
/**
* TenantLineInnerInterceptor 多租户插件

View File

@ -29,9 +29,6 @@ public class ResourcesConfig implements WebMvcConfigurer
{
/** 本地文件上传路径 */
registry.addResourceHandler(Constants.RESOURCE_PREFIX + "/**").addResourceLocations("file:" + RuoYiConfig.getProfile() + "/");
/** swagger配置 */
registry.addResourceHandler("/swagger-ui/**").addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/");
}
/**
@ -53,7 +50,7 @@ public class ResourcesConfig implements WebMvcConfigurer
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
// 设置访问源地址
config.addAllowedOrigin("*");
config.addAllowedOriginPattern("*");
// 设置访问源请求头
config.addAllowedHeader("*");
// 设置访问源请求方法

View File

@ -112,7 +112,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter
.antMatchers("/profile/**").anonymous()
.antMatchers("/common/download**").anonymous()
.antMatchers("/common/download/resource**").anonymous()
.antMatchers("/swagger-ui.html").anonymous()
.antMatchers("/doc.html").anonymous()
.antMatchers("/swagger-resources/**").anonymous()
.antMatchers("/webjars/**").anonymous()
.antMatchers("/*/api-docs").anonymous()

View File

@ -1,55 +0,0 @@
package com.ruoyi.framework.config.properties;
import com.alibaba.druid.pool.DruidDataSource;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
* druid 配置属性
*
* @author Lion Li
*/
@Data
@Configuration
@ConfigurationProperties(prefix = "spring.datasource.druid")
public class DruidProperties {
/** 初始连接数 */
private int initialSize;
/** 最小连接池数量 */
private int minIdle;
/** 最大连接池数量 */
private int maxActive;
/** 配置获取连接等待超时的时间 */
private int maxWait;
/** 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 */
private int timeBetweenEvictionRunsMillis;
/** 配置一个连接在池中最小生存的时间,单位是毫秒 */
private int minEvictableIdleTimeMillis;
/** 配置一个连接在池中最大生存的时间,单位是毫秒 */
private int maxEvictableIdleTimeMillis;
/** 配置检测连接是否有效 */
private String validationQuery;
/** 初始连接数 */
private boolean testWhileIdle;
/** 初始连接数 */
private boolean testOnBorrow;
/** 初始连接数 */
private boolean testOnReturn;
public DruidDataSource dataSource(DruidDataSource datasource) {
datasource.setInitialSize(initialSize);
datasource.setMaxActive(maxActive);
datasource.setMinIdle(minIdle);
datasource.setMaxWait(maxWait);
datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
datasource.setMaxEvictableIdleTimeMillis(maxEvictableIdleTimeMillis);
datasource.setValidationQuery(validationQuery);
datasource.setTestWhileIdle(testWhileIdle);
datasource.setTestOnBorrow(testOnBorrow);
datasource.setTestOnReturn(testOnReturn);
return datasource;
}
}

View File

@ -1,26 +0,0 @@
package com.ruoyi.framework.datasource;
import java.util.Map;
import javax.sql.DataSource;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
/**
* 动态数据源
*
* @author ruoyi
*/
public class DynamicDataSource extends AbstractRoutingDataSource
{
public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources)
{
super.setDefaultTargetDataSource(defaultTargetDataSource);
super.setTargetDataSources(targetDataSources);
super.afterPropertiesSet();
}
@Override
protected Object determineCurrentLookupKey()
{
return DynamicDataSourceContextHolder.getDataSourceType();
}
}

View File

@ -1,45 +0,0 @@
package com.ruoyi.framework.datasource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 数据源切换处理
*
* @author ruoyi
*/
public class DynamicDataSourceContextHolder
{
public static final Logger log = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class);
/**
* 使用ThreadLocal维护变量ThreadLocal为每个使用该变量的线程提供独立的变量副本
* 所以每一个线程都可以独立地改变自己的副本而不会影响其它线程所对应的副本
*/
private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();
/**
* 设置数据源的变量
*/
public static void setDataSourceType(String dsType)
{
log.info("切换到{}数据源", dsType);
CONTEXT_HOLDER.set(dsType);
}
/**
* 获得数据源的变量
*/
public static String getDataSourceType()
{
return CONTEXT_HOLDER.get();
}
/**
* 清空数据源变量
*/
public static void clearDataSourceType()
{
CONTEXT_HOLDER.remove();
}
}

View File

@ -1,15 +1,16 @@
package com.ruoyi.framework.interceptor;
import java.lang.reflect.Method;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.ruoyi.common.annotation.RepeatSubmit;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.JsonUtils;
import com.ruoyi.common.utils.ServletUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import com.alibaba.fastjson.JSONObject;
import com.ruoyi.common.annotation.RepeatSubmit;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.ServletUtils;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
/**
* 防止重复提交拦截器
@ -32,7 +33,7 @@ public abstract class RepeatSubmitInterceptor extends HandlerInterceptorAdapter
if (this.isRepeatSubmit(request))
{
AjaxResult ajaxResult = AjaxResult.error("不允许重复提交,请稍后再试");
ServletUtils.renderString(response, JSONObject.toJSONString(ajaxResult));
ServletUtils.renderString(response, JsonUtils.toJsonString(ajaxResult));
return false;
}
}

View File

@ -1,17 +1,19 @@
package com.ruoyi.framework.interceptor.impl;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.lang.Validator;
import com.alibaba.fastjson.JSONObject;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.filter.RepeatedlyRequestWrapper;
import com.ruoyi.common.utils.http.HttpHelper;
import com.ruoyi.common.utils.JsonUtils;
import com.ruoyi.framework.interceptor.RepeatSubmitInterceptor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@ -22,6 +24,7 @@ import java.util.concurrent.TimeUnit;
*
* @author ruoyi
*/
@Slf4j
@Component
public class SameUrlDataInterceptor extends RepeatSubmitInterceptor
{
@ -56,13 +59,17 @@ public class SameUrlDataInterceptor extends RepeatSubmitInterceptor
if (request instanceof RepeatedlyRequestWrapper)
{
RepeatedlyRequestWrapper repeatedlyRequest = (RepeatedlyRequestWrapper) request;
nowParams = HttpHelper.getBodyString(repeatedlyRequest);
}
try {
nowParams = IoUtil.readUtf8(repeatedlyRequest.getInputStream());
} catch (IOException e) {
log.warn("读取流出现问题!");
}
}
// body参数为空获取Parameter的数据
if (Validator.isEmpty(nowParams))
{
nowParams = JSONObject.toJSONString(request.getParameterMap());
nowParams = JsonUtils.toJsonString(request.getParameterMap());
}
Map<String, Object> nowDataMap = new HashMap<String, Object>();
nowDataMap.put(REPEAT_PARAMS, nowParams);

View File

@ -1,55 +0,0 @@
package com.ruoyi.framework.manager;
import java.util.TimerTask;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import com.ruoyi.common.utils.Threads;
import com.ruoyi.common.utils.spring.SpringUtils;
/**
* 异步任务管理器
*
* @author ruoyi
*/
public class AsyncManager
{
/**
* 操作延迟10毫秒
*/
private final int OPERATE_DELAY_TIME = 10;
/**
* 异步操作任务调度线程池
*/
private ScheduledExecutorService executor = SpringUtils.getBean("scheduledExecutorService");
/**
* 单例模式
*/
private AsyncManager(){}
private static AsyncManager me = new AsyncManager();
public static AsyncManager me()
{
return me;
}
/**
* 执行任务
*
* @param task 任务
*/
public void execute(TimerTask task)
{
executor.schedule(task, OPERATE_DELAY_TIME, TimeUnit.MILLISECONDS);
}
/**
* 停止任务线程池
*/
public void shutdown()
{
Threads.shutdownAndAwaitTermination(executor);
}
}

View File

@ -1,39 +1,41 @@
package com.ruoyi.framework.manager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.ruoyi.common.utils.Threads;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import javax.annotation.PreDestroy;
import java.util.concurrent.ScheduledExecutorService;
/**
* 确保应用退出时能关闭后台线程
*
* @author ruoyi
* @author Lion Li
*/
@Slf4j(topic = "sys-user")
@Component
public class ShutdownManager
{
private static final Logger logger = LoggerFactory.getLogger("sys-user");
public class ShutdownManager {
@PreDestroy
public void destroy()
{
shutdownAsyncManager();
}
@Autowired
@Qualifier("scheduledExecutorService")
private ScheduledExecutorService scheduledExecutorService;
/**
* 停止异步执行任务
*/
private void shutdownAsyncManager()
{
try
{
logger.info("====关闭后台任务任务线程池====");
AsyncManager.me().shutdown();
}
catch (Exception e)
{
logger.error(e.getMessage(), e);
}
}
@PreDestroy
public void destroy() {
shutdownAsyncManager();
}
/**
* 停止异步执行任务
*/
private void shutdownAsyncManager() {
try {
log.info("====关闭后台任务任务线程池====");
Threads.shutdownAndAwaitTermination(scheduledExecutorService);
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
}

View File

@ -1,111 +0,0 @@
package com.ruoyi.framework.manager.factory;
import cn.hutool.http.useragent.UserAgent;
import cn.hutool.http.useragent.UserAgentUtil;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.ip.AddressUtils;
import com.ruoyi.common.utils.ip.IpUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.system.domain.SysLogininfor;
import com.ruoyi.system.domain.SysOperLog;
import com.ruoyi.system.service.ISysLogininforService;
import com.ruoyi.system.service.ISysOperLogService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.TimerTask;
/**
* 异步工厂产生任务用
*
* @author ruoyi
*/
public class AsyncFactory
{
private static final Logger sys_user_logger = LoggerFactory.getLogger("sys-user");
/**
* 记录登录信息
*
* @param username 用户名
* @param status 状态
* @param message 消息
* @param args 列表
* @return 任务task
*/
public static TimerTask recordLogininfor(final String username, final String status, final String message,
final Object... args)
{
final UserAgent userAgent = UserAgentUtil.parse(ServletUtils.getRequest().getHeader("User-Agent"));
final String ip = IpUtils.getIpAddr(ServletUtils.getRequest());
return new TimerTask()
{
@Override
public void run()
{
String address = AddressUtils.getRealAddressByIP(ip);
StringBuilder s = new StringBuilder();
s.append(getBlock(ip));
s.append(address);
s.append(getBlock(username));
s.append(getBlock(status));
s.append(getBlock(message));
// 打印信息到日志
sys_user_logger.info(s.toString(), args);
// 获取客户端操作系统
String os = userAgent.getOs().getName();
// 获取客户端浏览器
String browser = userAgent.getBrowser().getName();
// 封装对象
SysLogininfor logininfor = new SysLogininfor();
logininfor.setUserName(username);
logininfor.setIpaddr(ip);
logininfor.setLoginLocation(address);
logininfor.setBrowser(browser);
logininfor.setOs(os);
logininfor.setMsg(message);
// 日志状态
if (Constants.LOGIN_SUCCESS.equals(status) || Constants.LOGOUT.equals(status))
{
logininfor.setStatus(Constants.SUCCESS);
}
else if (Constants.LOGIN_FAIL.equals(status))
{
logininfor.setStatus(Constants.FAIL);
}
// 插入数据
SpringUtils.getBean(ISysLogininforService.class).insertLogininfor(logininfor);
}
};
}
/**
* 操作日志记录
*
* @param operLog 操作日志信息
* @return 任务task
*/
public static TimerTask recordOper(final SysOperLog operLog)
{
return new TimerTask()
{
@Override
public void run()
{
// 远程查询操作地点
operLog.setOperLocation(AddressUtils.getRealAddressByIP(operLog.getOperIp()));
SpringUtils.getBean(ISysOperLogService.class).insertOperlog(operLog);
}
};
}
public static String getBlock(Object msg)
{
if (msg == null)
{
msg = "";
}
return "[" + msg.toString() + "]";
}
}

View File

@ -2,8 +2,8 @@ package com.ruoyi.framework.security.handle;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpStatus;
import com.alibaba.fastjson.JSON;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.JsonUtils;
import com.ruoyi.common.utils.ServletUtils;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
@ -30,6 +30,6 @@ public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint, S
{
int code = HttpStatus.HTTP_UNAUTHORIZED;
String msg = StrUtil.format("请求访问:{},认证失败,无法访问系统资源", request.getRequestURI());
ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.error(code, msg)));
ServletUtils.renderString(response, JsonUtils.toJsonString(AjaxResult.error(code, msg)));
}
}

View File

@ -2,13 +2,12 @@ package com.ruoyi.framework.security.handle;
import cn.hutool.core.lang.Validator;
import cn.hutool.http.HttpStatus;
import com.alibaba.fastjson.JSON;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.utils.JsonUtils;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.framework.manager.AsyncManager;
import com.ruoyi.framework.manager.factory.AsyncFactory;
import com.ruoyi.framework.web.service.AsyncService;
import com.ruoyi.framework.web.service.TokenService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
@ -26,29 +25,29 @@ import java.io.IOException;
* @author ruoyi
*/
@Configuration
public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler
{
@Autowired
private TokenService tokenService;
public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler {
@Autowired
private TokenService tokenService;
@Autowired
private AsyncService asyncService;
/**
* 退出处理
*/
@Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
throws IOException, ServletException {
LoginUser loginUser = tokenService.getLoginUser(request);
if (Validator.isNotNull(loginUser)) {
String userName = loginUser.getUsername();
// 删除用户缓存记录
tokenService.delLoginUser(loginUser.getToken());
// 记录用户退出日志
asyncService.recordLogininfor(userName, Constants.LOGOUT, "退出成功", request);
}
ServletUtils.renderString(response, JsonUtils.toJsonString(AjaxResult.error(HttpStatus.HTTP_OK, "退出成功")));
}
/**
* 退出处理
*
* @return
*/
@Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
throws IOException, ServletException
{
LoginUser loginUser = tokenService.getLoginUser(request);
if (Validator.isNotNull(loginUser))
{
String userName = loginUser.getUsername();
// 删除用户缓存记录
tokenService.delLoginUser(loginUser.getToken());
// 记录用户退出日志
AsyncManager.me().execute(AsyncFactory.recordLogininfor(userName, Constants.LOGOUT, "退出成功"));
}
ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.error(HttpStatus.HTTP_OK, "退出成功")));
}
}

View File

@ -0,0 +1,96 @@
package com.ruoyi.framework.web.service;
import cn.hutool.http.useragent.UserAgent;
import cn.hutool.http.useragent.UserAgentUtil;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.ip.AddressUtils;
import com.ruoyi.system.domain.SysLogininfor;
import com.ruoyi.system.domain.SysOperLog;
import com.ruoyi.system.service.ISysLogininforService;
import com.ruoyi.system.service.ISysOperLogService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
/**
* 异步工厂产生任务用
*
* @author Lion Li
*/
@Slf4j(topic = "sys-user")
@Async
@Component
public class AsyncService {
@Autowired
private ISysLogininforService iSysLogininforService;
@Autowired
private ISysOperLogService iSysOperLogService;
/**
* 记录登录信息
*
* @param username 用户名
* @param status 状态
* @param message 消息
* @param args 列表
*/
public void recordLogininfor(final String username, final String status, final String message,
HttpServletRequest request, final Object... args) {
final UserAgent userAgent = UserAgentUtil.parse(request.getHeader("User-Agent"));
final String ip = ServletUtils.getClientIP(request);
String address = AddressUtils.getRealAddressByIP(ip);
StringBuilder s = new StringBuilder();
s.append(getBlock(ip));
s.append(address);
s.append(getBlock(username));
s.append(getBlock(status));
s.append(getBlock(message));
// 打印信息到日志
log.info(s.toString(), args);
// 获取客户端操作系统
String os = userAgent.getOs().getName();
// 获取客户端浏览器
String browser = userAgent.getBrowser().getName();
// 封装对象
SysLogininfor logininfor = new SysLogininfor();
logininfor.setUserName(username);
logininfor.setIpaddr(ip);
logininfor.setLoginLocation(address);
logininfor.setBrowser(browser);
logininfor.setOs(os);
logininfor.setMsg(message);
// 日志状态
if (Constants.LOGIN_SUCCESS.equals(status) || Constants.LOGOUT.equals(status)) {
logininfor.setStatus(Constants.SUCCESS);
} else if (Constants.LOGIN_FAIL.equals(status)) {
logininfor.setStatus(Constants.FAIL);
}
// 插入数据
iSysLogininforService.insertLogininfor(logininfor);
}
/**
* 操作日志记录
*
* @param operLog 操作日志信息
*/
public void recordOper(final SysOperLog operLog) {
// 远程查询操作地点
operLog.setOperLocation(AddressUtils.getRealAddressByIP(operLog.getOperIp()));
iSysOperLogService.insertOperlog(operLog);
}
private String getBlock(Object msg) {
if (msg == null) {
msg = "";
}
return "[" + msg.toString() + "]";
}
}

View File

@ -11,10 +11,7 @@ import com.ruoyi.common.exception.user.UserPasswordNotMatchException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.MessageUtils;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.ip.IpUtils;
import com.ruoyi.framework.config.properties.CaptchaProperties;
import com.ruoyi.framework.manager.AsyncManager;
import com.ruoyi.framework.manager.factory.AsyncFactory;
import com.ruoyi.system.service.ISysUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
@ -24,6 +21,7 @@ import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
/**
* 登录校验方法
@ -48,6 +46,9 @@ public class SysLoginService
@Autowired
private ISysUserService userService;
@Autowired
private AsyncService asyncService;
/**
* 登录验证
*
@ -59,16 +60,17 @@ public class SysLoginService
*/
public String login(String username, String password, String code, String uuid)
{
HttpServletRequest request = ServletUtils.getRequest();
if(captchaProperties.getEnabled()) {
String verifyKey = Constants.CAPTCHA_CODE_KEY + uuid;
String captcha = redisCache.getCacheObject(verifyKey);
redisCache.deleteObject(verifyKey);
if (captcha == null) {
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")));
asyncService.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"), request);
throw new CaptchaExpireException();
}
if (!code.equalsIgnoreCase(captcha)) {
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error")));
asyncService.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error"), request);
throw new CaptchaException();
}
}
@ -84,16 +86,16 @@ public class SysLoginService
{
if (e instanceof BadCredentialsException)
{
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
asyncService.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match"), request);
throw new UserPasswordNotMatchException();
}
else
{
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage()));
asyncService.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage(), request);
throw new CustomException(e.getMessage());
}
}
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
asyncService.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"), request);
LoginUser loginUser = (LoginUser) authentication.getPrincipal();
recordLoginInfo(loginUser.getUser());
// 生成token
@ -105,7 +107,7 @@ public class SysLoginService
*/
public void recordLoginInfo(SysUser user)
{
user.setLoginIp(IpUtils.getIpAddr(ServletUtils.getRequest()));
user.setLoginIp(ServletUtils.getClientIP());
user.setLoginDate(DateUtils.getNowDate());
user.setUpdateBy(user.getUserName());
userService.updateUserProfile(user);

View File

@ -9,7 +9,6 @@ import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.ip.AddressUtils;
import com.ruoyi.common.utils.ip.IpUtils;
import com.ruoyi.framework.config.properties.TokenProperties;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
@ -131,7 +130,7 @@ public class TokenService {
*/
public void setUserAgent(LoginUser loginUser) {
UserAgent userAgent = UserAgentUtil.parse(ServletUtils.getRequest().getHeader("User-Agent"));
String ip = IpUtils.getIpAddr(ServletUtils.getRequest());
String ip = ServletUtils.getClientIP();
loginUser.setIpaddr(ip);
loginUser.setLoginLocation(AddressUtils.getRealAddressByIP(ip));
loginUser.setBrowser(userAgent.getBrowser().getName());

View File

@ -1,6 +1,6 @@
package com.ruoyi.generator.mapper;
import com.ruoyi.common.core.page.BaseMapperPlus;
import com.ruoyi.common.core.mybatisplus.core.BaseMapperPlus;
import com.ruoyi.generator.domain.GenTableColumn;
import java.util.List;

View File

@ -1,7 +1,7 @@
package com.ruoyi.generator.mapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.common.core.page.BaseMapperPlus;
import com.ruoyi.common.core.mybatisplus.core.BaseMapperPlus;
import com.ruoyi.generator.domain.GenTable;
import org.apache.ibatis.annotations.Param;

View File

@ -1,7 +1,7 @@
package com.ruoyi.generator.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl;
import com.ruoyi.generator.domain.GenTableColumn;
import com.ruoyi.generator.mapper.GenTableColumnMapper;
import org.springframework.stereotype.Service;
@ -15,7 +15,7 @@ import java.util.List;
* @author ruoyi
*/
@Service
public class GenTableColumnServiceImpl extends ServiceImpl<GenTableColumnMapper, GenTableColumn> implements IGenTableColumnService {
public class GenTableColumnServiceImpl extends ServicePlusImpl<GenTableColumnMapper, GenTableColumn> implements IGenTableColumnService {
/**
* 查询业务字段列表

View File

@ -1,17 +1,17 @@
package com.ruoyi.generator.service;
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 com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.constant.GenConstants;
import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.exception.CustomException;
import com.ruoyi.common.utils.JsonUtils;
import com.ruoyi.common.utils.PageUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.file.FileUtils;
@ -50,7 +50,7 @@ import java.util.zip.ZipOutputStream;
*/
@Slf4j
@Service
public class GenTableServiceImpl extends ServiceImpl<GenTableMapper, GenTable> implements IGenTableService {
public class GenTableServiceImpl extends ServicePlusImpl<GenTableMapper, GenTable> implements IGenTableService {
@Autowired
private GenTableColumnMapper genTableColumnMapper;
@ -130,7 +130,7 @@ public class GenTableServiceImpl extends ServiceImpl<GenTableMapper, GenTable> i
@Override
@Transactional
public void updateGenTable(GenTable genTable) {
String options = JSON.toJSONString(genTable.getParams());
String options = JsonUtils.toJsonString(genTable.getParams());
genTable.setOptions(options);
int row = baseMapper.updateById(genTable);
if (row > 0) {
@ -263,12 +263,8 @@ public class GenTableServiceImpl extends ServiceImpl<GenTableMapper, GenTable> i
StringWriter sw = new StringWriter();
Template tpl = Velocity.getTemplate(template, Constants.UTF8);
tpl.merge(context, sw);
try {
String path = getGenPath(table, template);
FileUtils.writeStringToFile(new File(path), sw.toString(), Constants.UTF8);
} catch (IOException e) {
throw new CustomException("渲染模板失败,表名:" + table.getTableName());
}
String path = getGenPath(table, template);
FileUtils.writeUtf8String(sw.toString(), path);
}
}
}
@ -365,13 +361,12 @@ public class GenTableServiceImpl extends ServiceImpl<GenTableMapper, GenTable> i
@Override
public void validateEdit(GenTable genTable) {
if (GenConstants.TPL_TREE.equals(genTable.getTplCategory())) {
String options = JSON.toJSONString(genTable.getParams());
JSONObject paramsObj = JSONObject.parseObject(options);
if (Validator.isEmpty(paramsObj.getString(GenConstants.TREE_CODE))) {
Map<String, Object> paramsObj = genTable.getParams();
if (Validator.isEmpty(paramsObj.get(GenConstants.TREE_CODE))) {
throw new CustomException("树编码字段不能为空");
} else if (Validator.isEmpty(paramsObj.getString(GenConstants.TREE_PARENT_CODE))) {
} else if (Validator.isEmpty(paramsObj.get(GenConstants.TREE_PARENT_CODE))) {
throw new CustomException("树父编码字段不能为空");
} else if (Validator.isEmpty(paramsObj.getString(GenConstants.TREE_NAME))) {
} else if (Validator.isEmpty(paramsObj.get(GenConstants.TREE_NAME))) {
throw new CustomException("树名称字段不能为空");
} else if (GenConstants.TPL_SUB.equals(genTable.getTplCategory())) {
if (Validator.isEmpty(genTable.getSubTableName())) {
@ -429,13 +424,13 @@ public class GenTableServiceImpl extends ServiceImpl<GenTableMapper, GenTable> i
* @param genTable 设置后的生成对象
*/
public void setTableFromOptions(GenTable genTable) {
JSONObject paramsObj = JSONObject.parseObject(genTable.getOptions());
Map<String, Object> paramsObj = JsonUtils.parseMap(genTable.getOptions());
if (Validator.isNotNull(paramsObj)) {
String treeCode = paramsObj.getString(GenConstants.TREE_CODE);
String treeParentCode = paramsObj.getString(GenConstants.TREE_PARENT_CODE);
String treeName = paramsObj.getString(GenConstants.TREE_NAME);
String parentMenuId = paramsObj.getString(GenConstants.PARENT_MENU_ID);
String parentMenuName = paramsObj.getString(GenConstants.PARENT_MENU_NAME);
String treeCode = Convert.toStr(paramsObj.get(GenConstants.TREE_CODE));
String treeParentCode = Convert.toStr(paramsObj.get(GenConstants.TREE_PARENT_CODE));
String treeName = Convert.toStr(paramsObj.get(GenConstants.TREE_NAME));
String parentMenuId = Convert.toStr(paramsObj.get(GenConstants.PARENT_MENU_ID));
String parentMenuName = Convert.toStr(paramsObj.get(GenConstants.PARENT_MENU_NAME));
genTable.setTreeCode(treeCode);
genTable.setTreeParentCode(treeParentCode);
@ -448,7 +443,7 @@ public class GenTableServiceImpl extends ServiceImpl<GenTableMapper, GenTable> i
/**
* 获取代码生成地址
*
* @param table 业务表信息
* @param table 业务表信息
* @param template 模板文件路径
* @return 生成地址
*/

View File

@ -1,6 +1,6 @@
package com.ruoyi.generator.service;
import com.ruoyi.common.core.page.IServicePlus;
import com.ruoyi.common.core.mybatisplus.core.IServicePlus;
import com.ruoyi.generator.domain.GenTableColumn;
import java.util.List;

View File

@ -1,6 +1,6 @@
package com.ruoyi.generator.service;
import com.ruoyi.common.core.page.IServicePlus;
import com.ruoyi.common.core.mybatisplus.core.IServicePlus;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.generator.domain.GenTable;

View File

@ -61,7 +61,7 @@ public class GenUtils
column.setHtmlType(GenConstants.HTML_INPUT);
// 如果是浮点型 统一用BigDecimal
String[] str = StrUtil.split(StrUtil.subBetween(column.getColumnType(), "(", ")"), ",");
String[] str = StrUtil.splitToArray(StrUtil.subBetween(column.getColumnType(), "(", ")"), ",");
if (str != null && str.length == 2 && Integer.parseInt(str[1]) > 0)
{
column.setJavaType(GenConstants.TYPE_BIGDECIMAL);
@ -182,7 +182,7 @@ public class GenUtils
String tablePrefix = GenConfig.getTablePrefix();
if (autoRemovePre && StrUtil.isNotEmpty(tablePrefix))
{
String[] searchList = StrUtil.split(tablePrefix, ",");
String[] searchList = StrUtil.splitToArray(tablePrefix, ",");
tableName = replaceFirst(tableName, searchList);
}
return StrUtil.upperFirst(StrUtil.toCamelCase(tableName));

View File

@ -1,10 +1,11 @@
package com.ruoyi.generator.util;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.ruoyi.common.constant.GenConstants;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.JsonUtils;
import com.ruoyi.generator.domain.GenTable;
import com.ruoyi.generator.domain.GenTableColumn;
import org.apache.velocity.VelocityContext;
@ -12,6 +13,7 @@ import org.apache.velocity.VelocityContext;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
/**
* 模板处理工具类
@ -75,7 +77,7 @@ public class VelocityUtils
public static void setMenuVelocityContext(VelocityContext context, GenTable genTable)
{
String options = genTable.getOptions();
JSONObject paramsObj = JSONObject.parseObject(options);
Map<String, Object> paramsObj = JsonUtils.parseMap(options);
String parentMenuId = getParentMenuId(paramsObj);
context.put("parentMenuId", parentMenuId);
}
@ -83,7 +85,7 @@ public class VelocityUtils
public static void setTreeVelocityContext(VelocityContext context, GenTable genTable)
{
String options = genTable.getOptions();
JSONObject paramsObj = JSONObject.parseObject(options);
Map<String, Object> paramsObj = JsonUtils.parseMap(options);
String treeCode = getTreecode(paramsObj);
String treeParentCode = getTreeParentCode(paramsObj);
String treeName = getTreeName(paramsObj);
@ -94,11 +96,11 @@ public class VelocityUtils
context.put("expandColumn", getExpandColumn(genTable));
if (paramsObj.containsKey(GenConstants.TREE_PARENT_CODE))
{
context.put("tree_parent_code", paramsObj.getString(GenConstants.TREE_PARENT_CODE));
context.put("tree_parent_code", paramsObj.get(GenConstants.TREE_PARENT_CODE));
}
if (paramsObj.containsKey(GenConstants.TREE_NAME))
{
context.put("tree_name", paramsObj.getString(GenConstants.TREE_NAME));
context.put("tree_name", paramsObj.get(GenConstants.TREE_NAME));
}
}
@ -300,11 +302,11 @@ public class VelocityUtils
* @param paramsObj 生成其他选项
* @return 上级菜单ID字段
*/
public static String getParentMenuId(JSONObject paramsObj)
public static String getParentMenuId(Map<String, Object> paramsObj)
{
if (Validator.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.PARENT_MENU_ID))
{
return paramsObj.getString(GenConstants.PARENT_MENU_ID);
return Convert.toStr(paramsObj.get(GenConstants.PARENT_MENU_ID));
}
return DEFAULT_PARENT_MENU_ID;
}
@ -315,11 +317,11 @@ public class VelocityUtils
* @param paramsObj 生成其他选项
* @return 树编码
*/
public static String getTreecode(JSONObject paramsObj)
public static String getTreecode(Map<String, Object> paramsObj)
{
if (paramsObj.containsKey(GenConstants.TREE_CODE))
if (Validator.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.TREE_CODE))
{
return StrUtil.toCamelCase(paramsObj.getString(GenConstants.TREE_CODE));
return StrUtil.toCamelCase(Convert.toStr(paramsObj.get(GenConstants.TREE_CODE)));
}
return StrUtil.EMPTY;
}
@ -330,11 +332,11 @@ public class VelocityUtils
* @param paramsObj 生成其他选项
* @return 树父编码
*/
public static String getTreeParentCode(JSONObject paramsObj)
public static String getTreeParentCode(Map<String, Object> paramsObj)
{
if (paramsObj.containsKey(GenConstants.TREE_PARENT_CODE))
if (Validator.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.TREE_PARENT_CODE))
{
return StrUtil.toCamelCase(paramsObj.getString(GenConstants.TREE_PARENT_CODE));
return StrUtil.toCamelCase(Convert.toStr(paramsObj.get(GenConstants.TREE_PARENT_CODE)));
}
return StrUtil.EMPTY;
}
@ -345,11 +347,11 @@ public class VelocityUtils
* @param paramsObj 生成其他选项
* @return 树名称
*/
public static String getTreeName(JSONObject paramsObj)
public static String getTreeName(Map<String, Object> paramsObj)
{
if (paramsObj.containsKey(GenConstants.TREE_NAME))
if (Validator.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.TREE_NAME))
{
return StrUtil.toCamelCase(paramsObj.getString(GenConstants.TREE_NAME));
return StrUtil.toCamelCase(Convert.toStr(paramsObj.get(GenConstants.TREE_NAME)));
}
return StrUtil.EMPTY;
}
@ -363,8 +365,8 @@ public class VelocityUtils
public static int getExpandColumn(GenTable genTable)
{
String options = genTable.getOptions();
JSONObject paramsObj = JSONObject.parseObject(options);
String treeName = paramsObj.getString(GenConstants.TREE_NAME);
Map<String, Object> paramsObj = JsonUtils.parseMap(options);
String treeName = Convert.toStr(paramsObj.get(GenConstants.TREE_NAME));
int num = 0;
for (GenTableColumn column : genTable.getColumns())
{

View File

@ -2,7 +2,6 @@ package ${packageName}.bo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.util.Date;
import javax.validation.constraints.*;
@ -27,9 +26,6 @@ public class ${ClassName}AddBo {
/** $column.columnComment */
@ApiModelProperty("$column.columnComment")
#if($column.javaType == 'Date')
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
#end
#if($column.isRequired==1)
#if($column.javaType == 'String')
@NotBlank(message = "$column.columnComment不能为空")

View File

@ -7,7 +7,6 @@ import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.Date;
import java.math.BigDecimal;
import com.ruoyi.common.annotation.Excel;
/**
* ${functionName}对象 ${tableName}

View File

@ -2,7 +2,6 @@ package ${packageName}.bo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.util.Date;
import javax.validation.constraints.*;
@ -26,9 +25,6 @@ public class ${ClassName}EditBo {
/** $column.columnComment */
@ApiModelProperty("$column.columnComment")
#if($column.javaType == 'Date')
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
#end
#if($column.isRequired==1)
#if($column.javaType == 'String')
@NotBlank(message = "$column.columnComment不能为空")

View File

@ -1,8 +1,8 @@
package ${packageName}.mapper;
import ${packageName}.domain.${ClassName};
import com.ruoyi.common.core.page.BaseMapperPlus;
import com.ruoyi.common.core.mybatisplus.MybatisPlusRedisCache;
import com.ruoyi.common.core.mybatisplus.core.BaseMapperPlus;
import com.ruoyi.common.core.mybatisplus.cache.MybatisPlusRedisCache;
import org.apache.ibatis.annotations.CacheNamespace;
/**
@ -11,6 +11,7 @@ import org.apache.ibatis.annotations.CacheNamespace;
* @author ${author}
* @date ${datetime}
*/
// 如使需切换数据源 请勿使用缓存 会造成数据不一致现象
@CacheNamespace(implementation = MybatisPlusRedisCache.class, eviction = MybatisPlusRedisCache.class)
public interface ${ClassName}Mapper extends BaseMapperPlus<${ClassName}> {

View File

@ -50,9 +50,6 @@ public class ${ClassName}QueryBo extends ${Entity} {
#foreach ($column in $columns)
#if(!$table.isSuperColumn($column.javaField) && $column.query)
/** $column.columnComment */
#if($column.javaType == 'Date')
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
#end
@ApiModelProperty("$column.columnComment")
private $column.javaType $column.javaField;
#end

View File

@ -5,7 +5,7 @@ import ${packageName}.vo.${ClassName}Vo;
import ${packageName}.bo.${ClassName}QueryBo;
import ${packageName}.bo.${ClassName}AddBo;
import ${packageName}.bo.${ClassName}EditBo;
import com.ruoyi.common.core.page.IServicePlus;
import com.ruoyi.common.core.mybatisplus.core.IServicePlus;
#if($table.crud || $table.sub)
import com.ruoyi.common.core.page.TableDataInfo;
#end
@ -58,5 +58,5 @@ public interface I${ClassName}Service extends IServicePlus<${ClassName}> {
* @param isValid 是否校验,true-删除前校验,false-不校验
* @return
*/
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
Boolean deleteWithValidByIds(Collection<${pkColumn.javaType}> ids, Boolean isValid);
}

View File

@ -8,7 +8,7 @@ import com.ruoyi.common.core.page.PagePlus;
import com.ruoyi.common.core.page.TableDataInfo;
#end
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import ${packageName}.bo.${ClassName}AddBo;
@ -30,7 +30,7 @@ import java.util.Collection;
* @date ${datetime}
*/
@Service
public class ${ClassName}ServiceImpl extends ServiceImpl<${ClassName}Mapper, ${ClassName}> implements I${ClassName}Service {
public class ${ClassName}ServiceImpl extends ServicePlusImpl<${ClassName}Mapper, ${ClassName}> implements I${ClassName}Service {
@Override
public ${ClassName}Vo queryById(${pkColumn.javaType} ${pkColumn.javaField}){
@ -101,7 +101,7 @@ public class ${ClassName}ServiceImpl extends ServiceImpl<${ClassName}Mapper, ${C
}
@Override
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
public Boolean deleteWithValidByIds(Collection<${pkColumn.javaType}> ids, Boolean isValid) {
if(isValid){
//TODO 做一些业务上的校验,判断是否需要校验
}

View File

@ -1,7 +1,6 @@
package ${packageName}.vo;
import com.ruoyi.common.annotation.Excel;
import com.fasterxml.jackson.annotation.JsonFormat;
#foreach ($import in $importList)
import ${import};
#end
@ -41,7 +40,6 @@ public class ${ClassName}Vo {
@Excel(name = "${comment}" , readConverterExp = "$column.readConverterExp()")
#elseif($column.javaType == 'Date')
@Excel(name = "${comment}" , width = 30, dateFormat = "yyyy-MM-dd")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
#else
@Excel(name = "${comment}")
#end

View File

@ -148,7 +148,6 @@
#foreach($column in $columns)
#set($field=$column.javaField)
#if($column.insert && !$column.pk)
#if(($column.usableColumn) || (!$column.superColumn))
#set($parentheseIndex=$column.columnComment.indexOf(""))
#if($parentheseIndex != -1)
#set($comment=$column.columnComment.substring(0, $parentheseIndex))
@ -243,7 +242,6 @@
</el-form-item>
#end
#end
#end
#end
</el-form>
<div slot="footer" class="dialog-footer">
@ -259,19 +257,19 @@ import { list${BusinessName}, get${BusinessName}, del${BusinessName}, add${Busin
import Treeselect from "@riophae/vue-treeselect";
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
#foreach($column in $columns)
#if($column.insert && !$column.superColumn && !$column.pk && $column.htmlType == "imageUpload")
#if($column.insert && !$column.pk && $column.htmlType == "imageUpload")
import ImageUpload from '@/components/ImageUpload';
#break
#end
#end
#foreach($column in $columns)
#if($column.insert && !$column.superColumn && !$column.pk && $column.htmlType == "fileUpload")
#if($column.insert && !$column.pk && $column.htmlType == "fileUpload")
import FileUpload from '@/components/FileUpload';
#break
#end
#end
#foreach($column in $columns)
#if($column.insert && !$column.superColumn && !$column.pk && $column.htmlType == "editor")
#if($column.insert && !$column.pk && $column.htmlType == "editor")
import Editor from '@/components/Editor';
#break
#end
@ -281,19 +279,19 @@ export default {
name: "${BusinessName}",
components: {
#foreach($column in $columns)
#if($column.insert && !$column.superColumn && !$column.pk && $column.htmlType == "imageUpload")
#if($column.insert && !$column.pk && $column.htmlType == "imageUpload")
ImageUpload,
#break
#end
#end
#foreach($column in $columns)
#if($column.insert && !$column.superColumn && !$column.pk && $column.htmlType == "fileUpload")
#if($column.insert && !$column.pk && $column.htmlType == "fileUpload")
FileUpload,
#break
#end
#end
#foreach($column in $columns)
#if($column.insert && !$column.superColumn && !$column.pk && $column.htmlType == "editor")
#if($column.insert && !$column.pk && $column.htmlType == "editor")
Editor,
#break
#end

View File

@ -174,7 +174,6 @@
#foreach($column in $columns)
#set($field=$column.javaField)
#if($column.insert && !$column.pk)
#if(($column.usableColumn) || (!$column.superColumn))
#set($parentheseIndex=$column.columnComment.indexOf(""))
#if($parentheseIndex != -1)
#set($comment=$column.columnComment.substring(0, $parentheseIndex))
@ -266,7 +265,6 @@
#end
#end
#end
#end
#if($table.sub)
<el-divider content-position="center">${subTable.functionName}信息</el-divider>
<el-row :gutter="10" class="mb8">
@ -311,19 +309,19 @@
<script>
import { list${BusinessName}, get${BusinessName}, del${BusinessName}, add${BusinessName}, update${BusinessName}, export${BusinessName} } from "@/api/${moduleName}/${businessName}";
#foreach($column in $columns)
#if($column.insert && !$column.superColumn && !$column.pk && $column.htmlType == "imageUpload")
#if($column.insert && !$column.pk && $column.htmlType == "imageUpload")
import ImageUpload from '@/components/ImageUpload';
#break
#end
#end
#foreach($column in $columns)
#if($column.insert && !$column.superColumn && !$column.pk && $column.htmlType == "fileUpload")
#if($column.insert && !$column.pk && $column.htmlType == "fileUpload")
import FileUpload from '@/components/FileUpload';
#break
#end
#end
#foreach($column in $columns)
#if($column.insert && !$column.superColumn && !$column.pk && $column.htmlType == "editor")
#if($column.insert && !$column.pk && $column.htmlType == "editor")
import Editor from '@/components/Editor';
#break
#end
@ -333,19 +331,19 @@ export default {
name: "${BusinessName}",
components: {
#foreach($column in $columns)
#if($column.insert && !$column.superColumn && !$column.pk && $column.htmlType == "imageUpload")
#if($column.insert && !$column.pk && $column.htmlType == "imageUpload")
ImageUpload,
#break
#end
#end
#foreach($column in $columns)
#if($column.insert && !$column.superColumn && !$column.pk && $column.htmlType == "fileUpload")
#if($column.insert && !$column.pk && $column.htmlType == "fileUpload")
FileUpload,
#break
#end
#end
#foreach($column in $columns)
#if($column.insert && !$column.superColumn && !$column.pk && $column.htmlType == "editor")
#if($column.insert && !$column.pk && $column.htmlType == "editor")
Editor,
#break
#end

View File

@ -1,6 +1,8 @@
package com.ruoyi.quartz.controller;
import cn.hutool.core.util.StrUtil;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
@ -73,7 +75,11 @@ public class SysJobController extends BaseController
{
if (!CronUtils.isValid(sysJob.getCronExpression()))
{
return AjaxResult.error("cron表达式不正确");
return AjaxResult.error("新增任务'" + sysJob.getJobName() + "'失败Cron表达式不正确");
}
else if (StrUtil.containsIgnoreCase(sysJob.getInvokeTarget(), Constants.LOOKUP_RMI))
{
return AjaxResult.error("新增任务'" + sysJob.getJobName() + "'失败,目标字符串不允许'rmi://'调用");
}
sysJob.setCreateBy(SecurityUtils.getUsername());
return toAjax(jobService.insertJob(sysJob));
@ -89,7 +95,11 @@ public class SysJobController extends BaseController
{
if (!CronUtils.isValid(sysJob.getCronExpression()))
{
return AjaxResult.error("cron表达式不正确");
return AjaxResult.error("修改任务'" + sysJob.getJobName() + "'失败Cron表达式不正确");
}
else if (StrUtil.containsIgnoreCase(sysJob.getInvokeTarget(), Constants.LOOKUP_RMI))
{
return AjaxResult.error("修改任务'" + sysJob.getJobName() + "'失败,目标字符串不允许'rmi://'调用");
}
sysJob.setUpdateBy(SecurityUtils.getUsername());
return toAjax(jobService.updateJob(sysJob));

View File

@ -1,6 +1,6 @@
package com.ruoyi.quartz.mapper;
import com.ruoyi.common.core.page.BaseMapperPlus;
import com.ruoyi.common.core.mybatisplus.core.BaseMapperPlus;
import com.ruoyi.quartz.domain.SysJobLog;
/**

View File

@ -1,6 +1,6 @@
package com.ruoyi.quartz.mapper;
import com.ruoyi.common.core.page.BaseMapperPlus;
import com.ruoyi.common.core.mybatisplus.core.BaseMapperPlus;
import com.ruoyi.quartz.domain.SysJob;
/**

View File

@ -1,6 +1,6 @@
package com.ruoyi.quartz.service;
import com.ruoyi.common.core.page.IServicePlus;
import com.ruoyi.common.core.mybatisplus.core.IServicePlus;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.quartz.domain.SysJobLog;

View File

@ -1,6 +1,6 @@
package com.ruoyi.quartz.service;
import com.ruoyi.common.core.page.IServicePlus;
import com.ruoyi.common.core.mybatisplus.core.IServicePlus;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.exception.job.TaskException;
import com.ruoyi.quartz.domain.SysJob;

View File

@ -3,7 +3,7 @@ package com.ruoyi.quartz.service.impl;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.utils.PageUtils;
import com.ruoyi.quartz.domain.SysJobLog;
@ -21,7 +21,7 @@ import java.util.Map;
* @author ruoyi
*/
@Service
public class SysJobLogServiceImpl extends ServiceImpl<SysJobLogMapper, SysJobLog> implements ISysJobLogService {
public class SysJobLogServiceImpl extends ServicePlusImpl<SysJobLogMapper, SysJobLog> implements ISysJobLogService {
@Override
public TableDataInfo<SysJobLog> selectPageJobLogList(SysJobLog jobLog) {

View File

@ -2,8 +2,8 @@ package com.ruoyi.quartz.service.impl;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.constant.ScheduleConstants;
import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.exception.job.TaskException;
import com.ruoyi.common.utils.PageUtils;
@ -29,7 +29,7 @@ import java.util.List;
* @author ruoyi
*/
@Service
public class SysJobServiceImpl extends ServiceImpl<SysJobMapper, SysJob> implements ISysJobService {
public class SysJobServiceImpl extends ServicePlusImpl<SysJobMapper, SysJob> implements ISysJobService {
@Autowired
private Scheduler scheduler;

View File

@ -1,6 +1,6 @@
package com.ruoyi.system.mapper;
import com.ruoyi.common.core.page.BaseMapperPlus;
import com.ruoyi.common.core.mybatisplus.core.BaseMapperPlus;
import com.ruoyi.system.domain.SysConfig;
/**

View File

@ -1,7 +1,7 @@
package com.ruoyi.system.mapper;
import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.common.core.page.BaseMapperPlus;
import com.ruoyi.common.core.mybatisplus.core.BaseMapperPlus;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@ -24,7 +24,7 @@ public interface SysDeptMapper extends BaseMapperPlus<SysDept> {
/**
* 根据角色ID查询部门树信息
*
* @param roleId 角色ID
* @param roleId 角色ID
* @param deptCheckStrictly 部门树选择项是否关联显示
* @return 选中部门列表
*/

View File

@ -2,7 +2,7 @@ package com.ruoyi.system.mapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ruoyi.common.core.domain.entity.SysDictData;
import com.ruoyi.common.core.page.BaseMapperPlus;
import com.ruoyi.common.core.mybatisplus.core.BaseMapperPlus;
import java.util.List;

View File

@ -1,7 +1,7 @@
package com.ruoyi.system.mapper;
import com.ruoyi.common.core.domain.entity.SysDictType;
import com.ruoyi.common.core.page.BaseMapperPlus;
import com.ruoyi.common.core.mybatisplus.core.BaseMapperPlus;
/**
* 字典表 数据层

Some files were not shown because too many files have changed in this diff Show More