Merge remote-tracking branch 'origin/dev' into satoken

# Conflicts:
#	pom.xml
#	ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysUserOnlineController.java
#	ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java
#	ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRoleController.java
#	ruoyi-common/pom.xml
#	ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java
#	ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/LoginUser.java
#	ruoyi-common/src/main/java/com/ruoyi/common/utils/SecurityUtils.java
#	ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestDemoController.java
#	ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java
#	ruoyi-framework/src/main/java/com/ruoyi/framework/security/handle/LogoutSuccessHandlerImpl.java
#	ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/PermissionService.java
#	ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableServiceImpl.java
#	ruoyi-system/src/main/java/com/ruoyi/system/service/SysLoginService.java
#	ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysRoleServiceImpl.java
#	ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java
#	ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TokenServiceImpl.java
#	ruoyi-system/src/main/java/com/ruoyi/system/service/impl/UserDetailsServiceImpl.java
This commit is contained in:
疯狂的狮子li 2021-12-27 09:50:42 +08:00
commit dd37247e65
176 changed files with 3502 additions and 1436 deletions

View File

@ -8,21 +8,21 @@
[![Spring Boot](https://img.shields.io/badge/Spring%20Boot-2.5-blue.svg)]()
[![JDK-8+](https://img.shields.io/badge/JDK-8-green.svg)]()
[![JDK-11](https://img.shields.io/badge/JDK-11-green.svg)]()
[![JDK-17](https://img.shields.io/badge/JDK-17-green.svg)]()
> RuoYi-Vue-Plus 是基于 RuoYi-Vue 针对 `分布式集群` 场景升级(不兼容原框架)
> RuoYi-Vue-Plus 是重写 RuoYi-Vue 针对 `分布式集群` 场景全方位升级(不兼容原框架)
> 系统演示: [传送门](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/系统演示?sort_id=4836388)
| 功能介绍 | 使用技术 | 文档地址 | 特性注意事项 |
|---|---|---|---|
| 当前框架 | RuoYi-Vue-Plus | [RuoYi-Vue-Plus文档](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/pages) | 重写RuoYi-Vue全方位升级(不兼容原框架) |
| satoken分支 | RuoYi-Vue-Plus-satoken | [satoken分支地址](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/tree/satoken/) | 使用satoken重构权限鉴权(公测 可尝试上生产) |
| satoken分支 | RuoYi-Vue-Plus-satoken | [satoken分支地址](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/tree/satoken/) | 高可读性 扩展性(推荐使用) |
| 单体分支 | RuoYi-Vue-Plus-fast | [fast分支地址](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/tree/fast/) | 单体应用结构 |
| Vue3分支 | RuoYi-Vue-Plus-UI | [UI地址](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus-UI) | 由于组件还未完善 仅供学习 |
| 原框架 | RuoYi-Vue | [RuoYi-Vue官网](http://ruoyi.vip/) | 定期同步需要的功能 |
| 前端开发框架 | Vue、Element UI | [Element UI官网](https://element.eleme.cn/#/zh-CN) | |
| 后端开发框架 | SpringBoot | [SpringBoot官网](https://spring.io/projects/spring-boot/#learn) | |
| 容器框架 | Undertow | [Undertow官网](https://undertow.io/) | 基于 Netty 的高性能容器 |
| 容器框架 | Undertow | [Undertow官网](https://undertow.io/) | 基于 XNIO 的高性能容器 |
| 权限认证框架 | Spring Security、Jwt | [SpringSecurity官网](https://spring.io/projects/spring-security#learn) | 支持多终端认证系统 |
| 关系数据库 | MySQL | [MySQL官网](https://dev.mysql.com/) | 适配 8.X 最低 5.7 |
| 缓存数据库 | Redis | [Redis官网](https://redis.io/) | 适配 6.X 最低 4.X |

69
pom.xml
View File

@ -14,7 +14,7 @@
<properties>
<ruoyi-vue-plus.version>3.4.0</ruoyi-vue-plus.version>
<spring-boot.version>2.5.7</spring-boot.version>
<spring-boot.version>2.5.8</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>
@ -24,27 +24,28 @@
<swagger-annotations.version>1.5.22</swagger-annotations.version>
<poi.version>4.1.2</poi.version>
<easyexcel.version>2.2.11</easyexcel.version>
<cglib.version>3.3.0</cglib.version>
<velocity.version>2.3</velocity.version>
<satoken.version>1.28.0</satoken.version>
<mybatis-plus.version>3.4.3.4</mybatis-plus.version>
<p6spy.version>3.9.1</p6spy.version>
<hutool.version>5.7.16</hutool.version>
<hutool.version>5.7.17</hutool.version>
<okhttp.version>4.9.2</okhttp.version>
<spring-boot-admin.version>2.5.4</spring-boot-admin.version>
<redisson.version>3.16.4</redisson.version>
<spring-boot-admin.version>2.5.5</spring-boot-admin.version>
<redisson.version>3.16.6</redisson.version>
<lock4j.version>2.2.1</lock4j.version>
<dynamic-ds.version>3.4.1</dynamic-ds.version>
<tlog.version>1.3.4</tlog.version>
<dynamic-ds.version>3.5.0</dynamic-ds.version>
<tlog.version>1.3.6</tlog.version>
<xxl-job.version>2.3.0</xxl-job.version>
<!-- jdk11 缺失依赖 jaxb-->
<jaxb.version>3.0.1</jaxb.version>
<!-- OSS 配置 -->
<qiniu.version>7.8.0</qiniu.version>
<qiniu.version>7.9.0</qiniu.version>
<aliyun.oss.version>3.13.1</aliyun.oss.version>
<qcloud.cos.version>5.6.58</qcloud.cos.version>
<minio.version>8.3.3</minio.version>
<minio.version>8.3.4</minio.version>
<!-- docker 配置 -->
<docker.registry.url>localhost</docker.registry.url>
@ -115,6 +116,12 @@
</exclusions>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>${cglib.version}</version>
</dependency>
<!-- velocity代码生成使用模板 -->
<dependency>
<groupId>org.apache.velocity</groupId>
@ -168,7 +175,31 @@
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<artifactId>hutool-core</artifactId>
<version>${hutool.version}</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-http</artifactId>
<version>${hutool.version}</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-captcha</artifactId>
<version>${hutool.version}</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-extra</artifactId>
<version>${hutool.version}</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-jwt</artifactId>
<version>${hutool.version}</version>
</dependency>
@ -210,29 +241,13 @@
<dependency>
<groupId>com.yomahub</groupId>
<artifactId>tlog-spring-boot-configuration</artifactId>
<artifactId>tlog-web-spring-boot-starter</artifactId>
<version>${tlog.version}</version>
</dependency>
<dependency>
<groupId>com.yomahub</groupId>
<artifactId>tlog-webroot</artifactId>
<version>${tlog.version}</version>
<exclusions>
<exclusion>
<artifactId>javassist</artifactId>
<groupId>org.javassist</groupId>
</exclusion>
<exclusion>
<artifactId>guava</artifactId>
<groupId>com.google.guava</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.yomahub</groupId>
<artifactId>tlog-xxl-job</artifactId>
<artifactId>tlog-xxljob-spring-boot-starter</artifactId>
<version>${tlog.version}</version>
</dependency>

View File

@ -4,6 +4,7 @@ import cn.dev33.satoken.annotation.SaCheckPermission;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.PageQuery;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.poi.ExcelUtil;
@ -36,8 +37,8 @@ public class SysLogininforController extends BaseController {
@ApiOperation("查询系统访问记录列表")
@SaCheckPermission("monitor:logininfor:list")
@GetMapping("/list")
public TableDataInfo<SysLogininfor> list(SysLogininfor logininfor) {
return logininforService.selectPageLogininforList(logininfor);
public TableDataInfo<SysLogininfor> list(SysLogininfor logininfor, PageQuery pageQuery) {
return logininforService.selectPageLogininforList(logininfor, pageQuery);
}
@ApiOperation("导出系统访问记录列表")

View File

@ -4,6 +4,7 @@ import cn.dev33.satoken.annotation.SaCheckPermission;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.PageQuery;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.poi.ExcelUtil;
@ -36,8 +37,8 @@ public class SysOperlogController extends BaseController {
@ApiOperation("查询操作日志记录列表")
@SaCheckPermission("monitor:operlog:list")
@GetMapping("/list")
public TableDataInfo<SysOperLog> list(SysOperLog operLog) {
return operLogService.selectPageOperLogList(operLog);
public TableDataInfo<SysOperLog> list(SysOperLog operLog, PageQuery pageQuery) {
return operLogService.selectPageOperLogList(operLog, pageQuery);
}
@ApiOperation("导出操作日志记录列表")

View File

@ -12,7 +12,6 @@ import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.dto.UserOnlineDTO;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.PageUtils;
import com.ruoyi.common.utils.RedisUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.domain.SysUserOnline;
@ -70,7 +69,7 @@ public class SysUserOnlineController extends BaseController {
Collections.reverse(userOnlineDTOList);
userOnlineDTOList.removeAll(Collections.singleton(null));
List<SysUserOnline> userOnlineList = BeanUtil.copyToList(userOnlineDTOList, SysUserOnline.class);
return PageUtils.buildDataInfo(userOnlineList);
return TableDataInfo.build(userOnlineList);
}
/**

View File

@ -5,6 +5,7 @@ import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.PageQuery;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.poi.ExcelUtil;
@ -41,8 +42,8 @@ public class SysConfigController extends BaseController {
@ApiOperation("获取参数配置列表")
@SaCheckPermission("system:config:list")
@GetMapping("/list")
public TableDataInfo<SysConfig> list(SysConfig config) {
return configService.selectPageConfigList(config);
public TableDataInfo<SysConfig> list(SysConfig config, PageQuery pageQuery) {
return configService.selectPageConfigList(config, pageQuery);
}
@ApiOperation("导出参数配置列表")

View File

@ -4,6 +4,7 @@ import cn.dev33.satoken.annotation.SaCheckPermission;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.PageQuery;
import com.ruoyi.common.core.domain.entity.SysDictData;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
@ -41,8 +42,8 @@ public class SysDictDataController extends BaseController {
@ApiOperation("查询字典数据列表")
@SaCheckPermission("system:dict:list")
@GetMapping("/list")
public TableDataInfo<SysDictData> list(SysDictData dictData) {
return dictDataService.selectPageDictDataList(dictData);
public TableDataInfo<SysDictData> list(SysDictData dictData, PageQuery pageQuery) {
return dictDataService.selectPageDictDataList(dictData, pageQuery);
}
@ApiOperation("导出字典数据列表")

View File

@ -5,6 +5,7 @@ import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.PageQuery;
import com.ruoyi.common.core.domain.entity.SysDictType;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
@ -38,8 +39,8 @@ public class SysDictTypeController extends BaseController {
@ApiOperation("查询字典类型列表")
@SaCheckPermission("system:dict:list")
@GetMapping("/list")
public TableDataInfo<SysDictType> list(SysDictType dictType) {
return dictTypeService.selectPageDictTypeList(dictType);
public TableDataInfo<SysDictType> list(SysDictType dictType, PageQuery pageQuery) {
return dictTypeService.selectPageDictTypeList(dictType, pageQuery);
}
@ApiOperation("导出字典类型列表")

View File

@ -4,6 +4,7 @@ import cn.dev33.satoken.annotation.SaCheckPermission;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.PageQuery;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.system.domain.SysNotice;
@ -36,8 +37,8 @@ public class SysNoticeController extends BaseController {
@ApiOperation("获取通知公告列表")
@SaCheckPermission("system:notice:list")
@GetMapping("/list")
public TableDataInfo<SysNotice> list(SysNotice notice) {
return noticeService.selectPageNoticeList(notice);
public TableDataInfo<SysNotice> list(SysNotice notice, PageQuery pageQuery) {
return noticeService.selectPageNoticeList(notice, pageQuery);
}
/**

View File

@ -5,6 +5,7 @@ import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.annotation.RepeatSubmit;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.PageQuery;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.core.validate.AddGroup;
import com.ruoyi.common.core.validate.EditGroup;
@ -47,8 +48,8 @@ public class SysOssConfigController extends BaseController {
@ApiOperation("查询对象存储配置列表")
@SaCheckPermission("system:oss:list")
@GetMapping("/list")
public TableDataInfo<SysOssConfigVo> list(@Validated(QueryGroup.class) SysOssConfigBo bo) {
return iSysOssConfigService.queryPageList(bo);
public TableDataInfo<SysOssConfigVo> list(@Validated(QueryGroup.class) SysOssConfigBo bo, PageQuery pageQuery) {
return iSysOssConfigService.queryPageList(bo, pageQuery);
}
/**

View File

@ -11,6 +11,7 @@ import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.annotation.RepeatSubmit;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.PageQuery;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.core.validate.QueryGroup;
import com.ruoyi.common.enums.BusinessType;
@ -61,8 +62,8 @@ public class SysOssController extends BaseController {
@ApiOperation("查询OSS对象存储列表")
@SaCheckPermission("system:oss:list")
@GetMapping("/list")
public TableDataInfo<SysOssVo> list(@Validated(QueryGroup.class) SysOssBo bo) {
return iSysOssService.queryPageList(bo);
public TableDataInfo<SysOssVo> list(@Validated(QueryGroup.class) SysOssBo bo, PageQuery pageQuery) {
return iSysOssService.queryPageList(bo, pageQuery);
}
/**

View File

@ -5,6 +5,7 @@ import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.PageQuery;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.poi.ExcelUtil;
@ -41,8 +42,8 @@ public class SysPostController extends BaseController {
@ApiOperation("获取岗位列表")
@SaCheckPermission("system:post:list")
@GetMapping("/list")
public TableDataInfo<SysPost> list(SysPost post) {
return postService.selectPagePostList(post);
public TableDataInfo<SysPost> list(SysPost post, PageQuery pageQuery) {
return postService.selectPagePostList(post, pageQuery);
}
@ApiOperation("导出岗位列表")

View File

@ -11,7 +11,10 @@ import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.domain.SysOss;
import com.ruoyi.system.service.ISysOssService;
import com.ruoyi.system.service.ISysUserService;
import io.swagger.annotations.*;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;

View File

@ -5,6 +5,7 @@ import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.PageQuery;
import com.ruoyi.common.core.domain.entity.SysRole;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.LoginUser;
@ -45,8 +46,8 @@ public class SysRoleController extends BaseController {
@ApiOperation("查询角色信息列表")
@SaCheckPermission("system:role:list")
@GetMapping("/list")
public TableDataInfo<SysRole> list(SysRole role) {
return roleService.selectPageRoleList(role);
public TableDataInfo<SysRole> list(SysRole role, PageQuery pageQuery) {
return roleService.selectPageRoleList(role, pageQuery);
}
@ApiOperation("导出角色信息列表")
@ -165,8 +166,8 @@ public class SysRoleController extends BaseController {
@ApiOperation("查询已分配用户角色列表")
@SaCheckPermission("system:role:list")
@GetMapping("/authUser/allocatedList")
public TableDataInfo<SysUser> allocatedList(SysUser user) {
return userService.selectAllocatedList(user);
public TableDataInfo<SysUser> allocatedList(SysUser user, PageQuery pageQuery) {
return userService.selectAllocatedList(user, pageQuery);
}
/**
@ -175,8 +176,8 @@ public class SysRoleController extends BaseController {
@ApiOperation("查询未分配用户角色列表")
@SaCheckPermission("system:role:list")
@GetMapping("/authUser/unallocatedList")
public TableDataInfo<SysUser> unallocatedList(SysUser user) {
return userService.selectUnallocatedList(user);
public TableDataInfo<SysUser> unallocatedList(SysUser user, PageQuery pageQuery) {
return userService.selectUnallocatedList(user, pageQuery);
}
/**

View File

@ -8,6 +8,7 @@ import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.PageQuery;
import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.common.core.domain.entity.SysRole;
import com.ruoyi.common.core.domain.entity.SysUser;
@ -59,8 +60,8 @@ public class SysUserController extends BaseController {
@ApiOperation("获取用户列表")
@SaCheckPermission("system:user:list")
@GetMapping("/list")
public TableDataInfo<SysUser> list(SysUser user) {
return userService.selectPageUserList(user);
public TableDataInfo<SysUser> list(SysUser user, PageQuery pageQuery) {
return userService.selectPageUserList(user, pageQuery);
}
@ApiOperation("导出用户列表")

View File

@ -51,7 +51,9 @@ spring:
# 主库数据源
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
# jdbc 所有参数配置参考 https://lionli.blog.csdn.net/article/details/122018562
# rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题)
url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true
username: root
password: root
# 从库数据源

View File

@ -58,7 +58,9 @@ spring:
# 主库数据源
master:
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://172.30.0.36:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true
# jdbc 所有参数配置参考 https://lionli.blog.csdn.net/article/details/122018562
# rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题)
url: jdbc:mysql://172.30.0.36:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true
username: root
password: root
# 从库数据源

View File

@ -11,7 +11,7 @@ ruoyi:
# 获取ip地址开关
addressEnabled: true
# 缓存懒加载
cacheLazy: true
cacheLazy: false
captcha:
# 页面 <参数设置> 可开启关闭 验证码校验
@ -53,10 +53,6 @@ logging:
org.springframework: warn
config: classpath:logback.xml
# tlog 全局访问性能拦截
tlog:
enable-invoke-time-print: true
# Spring配置
spring:
application:
@ -142,6 +138,8 @@ security:
- /*/api-docs
# druid 监控配置
- /druid/**
# 用户放行
permit-all:
# actuator 监控配置
- /actuator
- /actuator/**

View File

@ -2,12 +2,12 @@
not.null=* 必须填写
user.jcaptcha.error=验证码错误
user.jcaptcha.expire=验证码已失效
user.not.exists=用户不存在/密码错误
user.not.exists=对不起, 您的账号:{0} 不存在.
user.password.not.match=用户不存在/密码错误
user.password.retry.limit.count=密码输入错误{0}次
user.password.retry.limit.exceed=密码输入错误{0}次帐户锁定10分钟
user.password.delete=对不起,您的账号已被删除
user.blocked=用户已封禁,请联系管理员
user.password.retry.limit.exceed=密码错误次数过多,帐户锁定{0}分钟
user.password.delete=对不起,您的账号{0} 已被删除
user.blocked=对不起,您的账号:{0} 已禁用,请联系管理员
role.blocked=角色已封禁,请联系管理员
user.logout.success=退出成功
length.not.valid=长度必须在{min}到{max}个字符之间

View File

@ -2,12 +2,12 @@
not.null=* Required fill in
user.jcaptcha.error=Captcha error
user.jcaptcha.expire=Captcha invalid
user.not.exists=User does not exist/Password error
user.not.exists=Sorry, your account: {0} does not exist
user.password.not.match=User does not exist/Password error
user.password.retry.limit.count=Password input error {0} times
user.password.retry.limit.exceed=Password input error {0} times, account locked for 10 minutes
user.password.delete=Sorry, your account has been deleted
user.blocked=User disabledplease contact administrators
user.password.retry.limit.exceed=Too many password errors, account locked for {0} minutes
user.password.delete=Sorry, your account{0} has been deleted
user.blocked=Sorry, your account: {0} has been disabled. Please contact the administrator
role.blocked=Role disabledplease contact administrators
user.logout.success=Exit successful
length.not.valid=The length must be between {min} and {max} characters

View File

@ -2,12 +2,12 @@
not.null=* 必须填写
user.jcaptcha.error=验证码错误
user.jcaptcha.expire=验证码已失效
user.not.exists=用户不存在/密码错误
user.not.exists=对不起, 您的账号:{0} 不存在.
user.password.not.match=用户不存在/密码错误
user.password.retry.limit.count=密码输入错误{0}次
user.password.retry.limit.exceed=密码输入错误{0}次帐户锁定10分钟
user.password.delete=对不起,您的账号已被删除
user.blocked=用户已封禁,请联系管理员
user.password.retry.limit.exceed=密码错误次数过多,帐户锁定{0}分钟
user.password.delete=对不起,您的账号{0} 已被删除
user.blocked=对不起,您的账号:{0} 已禁用,请联系管理员
role.blocked=角色已封禁,请联系管理员
user.logout.success=退出成功
length.not.valid=长度必须在{min}到{max}个字符之间

View File

@ -74,18 +74,17 @@
<artifactId>easyexcel</artifactId>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
</dependency>
<!-- yml解析器 -->
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
</dependency>
<!-- &lt;!&ndash;Token生成与解析&ndash;&gt;-->
<!-- <dependency>-->
<!-- <groupId>io.jsonwebtoken</groupId>-->
<!-- <artifactId>jjwt</artifactId>-->
<!-- </dependency>-->
<!-- jdk11 缺失依赖 jaxb-->
<dependency>
<groupId>com.sun.xml.bind</groupId>
@ -106,18 +105,41 @@
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-extension</artifactId>
</dependency>
<!-- dynamic-datasource 多数据源-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
<groupId>cn.hutool</groupId>
<artifactId>hutool-core</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-http</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-captcha</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-jwt</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-extra</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
@ -130,10 +152,6 @@
<artifactId>swagger-annotations</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- 自动生成YML配置关联JSON文件 -->
<dependency>
<groupId>org.springframework.boot</groupId>
@ -151,16 +169,6 @@
<artifactId>lock4j-redisson-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.yomahub</groupId>
<artifactId>tlog-spring-boot-configuration</artifactId>
</dependency>
<dependency>
<groupId>com.yomahub</groupId>
<artifactId>tlog-webroot</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,26 @@
package com.ruoyi.common.annotation;
import java.lang.annotation.*;
/**
* 数据权限
*
* @author Lion Li
* @version 3.5.0
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataColumn {
/**
* 占位符关键字
*/
String key() default "deptName";
/**
* 占位符替换值
*/
String value() default "dept_id";
}

View File

@ -0,0 +1,18 @@
package com.ruoyi.common.annotation;
import java.lang.annotation.*;
/**
* 数据权限组
*
* @author Lion Li
* @version 3.5.0
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataPermission {
DataColumn[] value();
}

View File

@ -6,11 +6,14 @@ import java.lang.annotation.*;
* 数据权限过滤注解
*
* @author ruoyi
* @deprecated 3.6.0 移除 {@link com.ruoyi.common.annotation.DataPermission}
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Deprecated
public @interface DataScope {
/**
* 部门表的别名
*/
@ -25,4 +28,5 @@ public @interface DataScope {
* 是否过滤用户权限
*/
boolean isUser() default false;
}

View File

@ -10,11 +10,13 @@ import java.lang.annotation.*;
* 优先级先方法后类如果方法覆盖了类上的数据源类型以方法的为准否则以类上的为准
*
* @author ruoyi
* @deprecated 3.6.0 移除 使用原生注解处理 方法更全 {@link com.baomidou.dynamic.datasource.annotation.DS}
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Deprecated
public @interface DataSource {
/**
* 切换数据源名称

View File

@ -5,61 +5,62 @@ package com.ruoyi.common.constant;
*
* @author ruoyi
*/
public class Constants {
public interface Constants {
/**
* UTF-8 字符集
*/
public static final String UTF8 = "UTF-8";
String UTF8 = "UTF-8";
/**
* GBK 字符集
*/
public static final String GBK = "GBK";
String GBK = "GBK";
/**
* http请求
*/
public static final String HTTP = "http://";
String HTTP = "http://";
/**
* https请求
*/
public static final String HTTPS = "https://";
String HTTPS = "https://";
/**
* 通用成功标识
*/
public static final String SUCCESS = "0";
String SUCCESS = "0";
/**
* 通用失败标识
*/
public static final String FAIL = "1";
String FAIL = "1";
/**
* 登录成功
*/
public static final String LOGIN_SUCCESS = "Success";
String LOGIN_SUCCESS = "Success";
/**
* 注销
*/
public static final String LOGOUT = "Logout";
String LOGOUT = "Logout";
/**
* 注册
*/
public static final String REGISTER = "Register";
String REGISTER = "Register";
/**
* 登录失败
*/
public static final String LOGIN_FAIL = "Error";
String LOGIN_FAIL = "Error";
/**
* 验证码 redis key
*/
public static final String CAPTCHA_CODE_KEY = "captcha_codes:";
String CAPTCHA_CODE_KEY = "captcha_codes:";
/**
* 登录用户 redis key
@ -74,36 +75,51 @@ public class Constants {
/**
* 防重提交 redis key
*/
public static final String REPEAT_SUBMIT_KEY = "repeat_submit:";
String REPEAT_SUBMIT_KEY = "repeat_submit:";
/**
* 限流 redis key
*/
public static final String RATE_LIMIT_KEY = "rate_limit:";
String RATE_LIMIT_KEY = "rate_limit:";
/**
* 验证码有效期分钟
*/
public static final Integer CAPTCHA_EXPIRATION = 2;
Integer CAPTCHA_EXPIRATION = 2;
/**
* 登陆错误 redis key
*/
String LOGIN_ERROR = "login_error:";
/**
* 登录错误次数
*/
Integer LOGIN_ERROR_NUMBER = 5;
/**
* 登录错误限制时间(分钟)
*/
Integer LOGIN_ERROR_LIMIT_TIME = 10;
/**
* 令牌
*/
public static final String TOKEN = "token";
String TOKEN = "token";
/**
* 令牌前缀
*/
public static final String LOGIN_USER_KEY = "login_user_key";
String LOGIN_USER_KEY = "login_user_key";
/**
* 参数管理 cache key
*/
public static final String SYS_CONFIG_KEY = "sys_config:";
String SYS_CONFIG_KEY = "sys_config:";
/**
* 字典管理 cache key
*/
public static final String SYS_DICT_KEY = "sys_dict:";
String SYS_DICT_KEY = "sys_dict:";
}

View File

@ -5,184 +5,184 @@ package com.ruoyi.common.constant;
*
* @author ruoyi
*/
public class GenConstants {
public interface GenConstants {
/**
* 单表增删改查
*/
public static final String TPL_CRUD = "crud";
String TPL_CRUD = "crud";
/**
* 树表增删改查
*/
public static final String TPL_TREE = "tree";
String TPL_TREE = "tree";
/**
* 主子表增删改查
*/
public static final String TPL_SUB = "sub";
String TPL_SUB = "sub";
/**
* 树编码字段
*/
public static final String TREE_CODE = "treeCode";
String TREE_CODE = "treeCode";
/**
* 树父编码字段
*/
public static final String TREE_PARENT_CODE = "treeParentCode";
String TREE_PARENT_CODE = "treeParentCode";
/**
* 树名称字段
*/
public static final String TREE_NAME = "treeName";
String TREE_NAME = "treeName";
/**
* 上级菜单ID字段
*/
public static final String PARENT_MENU_ID = "parentMenuId";
String PARENT_MENU_ID = "parentMenuId";
/**
* 上级菜单名称字段
*/
public static final String PARENT_MENU_NAME = "parentMenuName";
String PARENT_MENU_NAME = "parentMenuName";
/**
* 数据库字符串类型
*/
public static final String[] COLUMNTYPE_STR = {"char", "varchar", "nvarchar", "varchar2"};
String[] COLUMNTYPE_STR = {"char", "varchar", "nvarchar", "varchar2"};
/**
* 数据库文本类型
*/
public static final String[] COLUMNTYPE_TEXT = {"tinytext", "text", "mediumtext", "longtext"};
String[] COLUMNTYPE_TEXT = {"tinytext", "text", "mediumtext", "longtext"};
/**
* 数据库时间类型
*/
public static final String[] COLUMNTYPE_TIME = {"datetime", "time", "date", "timestamp"};
String[] COLUMNTYPE_TIME = {"datetime", "time", "date", "timestamp"};
/**
* 数据库数字类型
*/
public static final String[] COLUMNTYPE_NUMBER = {"tinyint", "smallint", "mediumint", "int", "number", "integer",
"bit", "bigint", "float", "double", "decimal"};
String[] COLUMNTYPE_NUMBER = {"tinyint", "smallint", "mediumint", "int", "number", "integer",
"bit", "bigint", "float", "double", "decimal"};
/**
* BO对象 不需要添加字段
*/
public static final String[] COLUMNNAME_NOT_ADD = {"create_by", "create_time", "del_flag", "update_by",
"update_time", "version"};
String[] COLUMNNAME_NOT_ADD = {"create_by", "create_time", "del_flag", "update_by",
"update_time", "version"};
/**
* BO对象 不需要编辑字段
*/
public static final String[] COLUMNNAME_NOT_EDIT = {"create_by", "create_time", "del_flag", "update_by",
"update_time", "version"};
String[] COLUMNNAME_NOT_EDIT = {"create_by", "create_time", "del_flag", "update_by",
"update_time", "version"};
/**
* VO对象 不需要返回字段
*/
public static final String[] COLUMNNAME_NOT_LIST = {"create_by", "create_time", "del_flag", "update_by",
"update_time", "version"};
String[] COLUMNNAME_NOT_LIST = {"create_by", "create_time", "del_flag", "update_by",
"update_time", "version"};
/**
* BO对象 不需要查询字段
*/
public static final String[] COLUMNNAME_NOT_QUERY = {"id", "create_by", "create_time", "del_flag", "update_by",
"update_time", "remark", "version"};
String[] COLUMNNAME_NOT_QUERY = {"id", "create_by", "create_time", "del_flag", "update_by",
"update_time", "remark", "version"};
/**
* Entity基类字段
*/
public static final String[] BASE_ENTITY = {"createBy", "createTime", "updateBy", "updateTime"};
String[] BASE_ENTITY = {"createBy", "createTime", "updateBy", "updateTime"};
/**
* Tree基类字段
*/
public static final String[] TREE_ENTITY = {"parentName", "parentId", "children"};
String[] TREE_ENTITY = {"parentName", "parentId", "children"};
/**
* 文本框
*/
public static final String HTML_INPUT = "input";
String HTML_INPUT = "input";
/**
* 文本域
*/
public static final String HTML_TEXTAREA = "textarea";
String HTML_TEXTAREA = "textarea";
/**
* 下拉框
*/
public static final String HTML_SELECT = "select";
String HTML_SELECT = "select";
/**
* 单选框
*/
public static final String HTML_RADIO = "radio";
String HTML_RADIO = "radio";
/**
* 复选框
*/
public static final String HTML_CHECKBOX = "checkbox";
String HTML_CHECKBOX = "checkbox";
/**
* 日期控件
*/
public static final String HTML_DATETIME = "datetime";
String HTML_DATETIME = "datetime";
/**
* 图片上传控件
*/
public static final String HTML_IMAGE_UPLOAD = "imageUpload";
String HTML_IMAGE_UPLOAD = "imageUpload";
/**
* 文件上传控件
*/
public static final String HTML_FILE_UPLOAD = "fileUpload";
String HTML_FILE_UPLOAD = "fileUpload";
/**
* 富文本控件
*/
public static final String HTML_EDITOR = "editor";
String HTML_EDITOR = "editor";
/**
* 字符串类型
*/
public static final String TYPE_STRING = "String";
String TYPE_STRING = "String";
/**
* 整型
*/
public static final String TYPE_INTEGER = "Integer";
String TYPE_INTEGER = "Integer";
/**
* 长整型
*/
public static final String TYPE_LONG = "Long";
String TYPE_LONG = "Long";
/**
* 浮点型
*/
public static final String TYPE_DOUBLE = "Double";
String TYPE_DOUBLE = "Double";
/**
* 高精度计算类型
*/
public static final String TYPE_BIGDECIMAL = "BigDecimal";
String TYPE_BIGDECIMAL = "BigDecimal";
/**
* 时间类型
*/
public static final String TYPE_DATE = "Date";
String TYPE_DATE = "Date";
/**
* 模糊查询
*/
public static final String QUERY_LIKE = "LIKE";
String QUERY_LIKE = "LIKE";
/**
* 需要
*/
public static final String REQUIRE = "1";
String REQUIRE = "1";
}

View File

@ -5,108 +5,108 @@ package com.ruoyi.common.constant;
*
* @author ruoyi
*/
public class UserConstants {
public interface UserConstants {
/**
* 平台内系统用户的唯一标志
*/
public static final String SYS_USER = "SYS_USER";
String SYS_USER = "SYS_USER";
/**
* 正常状态
*/
public static final String NORMAL = "0";
String NORMAL = "0";
/**
* 异常状态
*/
public static final String EXCEPTION = "1";
String EXCEPTION = "1";
/**
* 用户封禁状态
*/
public static final String USER_DISABLE = "1";
String USER_DISABLE = "1";
/**
* 角色封禁状态
*/
public static final String ROLE_DISABLE = "1";
String ROLE_DISABLE = "1";
/**
* 部门正常状态
*/
public static final String DEPT_NORMAL = "0";
String DEPT_NORMAL = "0";
/**
* 部门停用状态
*/
public static final String DEPT_DISABLE = "1";
String DEPT_DISABLE = "1";
/**
* 字典正常状态
*/
public static final String DICT_NORMAL = "0";
String DICT_NORMAL = "0";
/**
* 是否为系统默认
*/
public static final String YES = "Y";
String YES = "Y";
/**
* 是否菜单外链
*/
public static final String YES_FRAME = "0";
String YES_FRAME = "0";
/**
* 是否菜单外链
*/
public static final String NO_FRAME = "1";
String NO_FRAME = "1";
/**
* 菜单类型目录
*/
public static final String TYPE_DIR = "M";
String TYPE_DIR = "M";
/**
* 菜单类型菜单
*/
public static final String TYPE_MENU = "C";
String TYPE_MENU = "C";
/**
* 菜单类型按钮
*/
public static final String TYPE_BUTTON = "F";
String TYPE_BUTTON = "F";
/**
* Layout组件标识
*/
public final static String LAYOUT = "Layout";
String LAYOUT = "Layout";
/**
* ParentView组件标识
*/
public final static String PARENT_VIEW = "ParentView";
String PARENT_VIEW = "ParentView";
/**
* InnerLink组件标识
*/
public final static String INNER_LINK = "InnerLink";
String INNER_LINK = "InnerLink";
/**
* 校验返回结果码
*/
public final static String UNIQUE = "0";
public final static String NOT_UNIQUE = "1";
String UNIQUE = "0";
String NOT_UNIQUE = "1";
/**
* 用户名长度限制
*/
public static final int USERNAME_MIN_LENGTH = 2;
public static final int USERNAME_MAX_LENGTH = 20;
int USERNAME_MIN_LENGTH = 2;
int USERNAME_MAX_LENGTH = 20;
/**
* 密码长度限制
*/
public static final int PASSWORD_MIN_LENGTH = 5;
public static final int PASSWORD_MAX_LENGTH = 20;
int PASSWORD_MIN_LENGTH = 5;
int PASSWORD_MAX_LENGTH = 20;
}

View File

@ -0,0 +1,93 @@
package com.ruoyi.common.core.domain;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.sql.SqlUtil;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
/**
* 分页查询实体类
*
* @author Lion Li
*/
@Data
@Accessors(chain = true)
public class PageQuery implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 分页大小
*/
@ApiModelProperty("分页大小")
private Integer pageSize;
/**
* 当前页数
*/
@ApiModelProperty("当前页数")
private Integer pageNum;
/**
* 排序列
*/
@ApiModelProperty("排序列")
private String orderByColumn;
/**
* 排序的方向desc或者asc
*/
@ApiModelProperty(value = "排序的方向", example = "asc,desc")
private String isAsc;
/**
* 当前记录起始索引 默认值
*/
public static final int DEFAULT_PAGE_NUM = 1;
/**
* 每页显示记录数 默认值 默认查全部
*/
public static final int DEFAULT_PAGE_SIZE = Integer.MAX_VALUE;
public <T> Page<T> build() {
Integer pageNum = ObjectUtil.defaultIfNull(getPageNum(), DEFAULT_PAGE_NUM);
Integer pageSize = ObjectUtil.defaultIfNull(getPageSize(), DEFAULT_PAGE_SIZE);
if (pageNum <= 0) {
pageNum = DEFAULT_PAGE_NUM;
}
Page<T> page = new Page<>(pageNum, pageSize);
OrderItem orderItem = buildOrderItem();
if (ObjectUtil.isNotNull(orderItem)) {
page.addOrder(orderItem);
}
return page;
}
private OrderItem buildOrderItem() {
// 兼容前端排序类型
if ("ascending".equals(isAsc)) {
isAsc = "asc";
} else if ("descending".equals(isAsc)) {
isAsc = "desc";
}
if (StringUtils.isNotBlank(orderByColumn)) {
String orderBy = SqlUtil.escapeOrderBySql(orderByColumn);
orderBy = StringUtils.toUnderScoreCase(orderBy);
if ("asc".equals(isAsc)) {
return OrderItem.asc(orderBy);
} else if ("desc".equals(isAsc)) {
return OrderItem.desc(orderBy);
}
}
return null;
}
}

View File

@ -1,5 +1,6 @@
package com.ruoyi.common.core.domain.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.ruoyi.common.core.domain.TreeEntity;
@ -65,6 +66,7 @@ public class SysMenu extends TreeEntity {
* 路由参数
*/
@ApiModelProperty(value = "路由参数")
@TableField("`query`")
private String query;
/**

View File

@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.ruoyi.common.core.domain.BaseEntity;
import com.ruoyi.common.xss.Xss;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@ -48,15 +49,17 @@ public class SysUser extends BaseEntity {
* 用户账号
*/
@ApiModelProperty(value = "用户账号")
@NotBlank(message = "用户账号不能为空")
@Xss(message = "用户账号不能包含脚本字符")
@NotBlank(message = "用户账号不能为空")
@Size(min = 0, max = 30, message = "用户账号长度不能超过30个字符")
private String userName;
/**
* 用户昵称
*/
@ApiModelProperty(value = "用户昵称")
@Size(min = 0, max = 30, message = "用户昵称长度不能超过30个字符")
@ApiModelProperty(value = "用户昵称")
@Xss(message = "用户昵称不能包含脚本字符")
@Size(min = 0, max = 30, message = "用户昵称长度不能超过30个字符")
private String nickName;
/**
@ -162,7 +165,7 @@ public class SysUser extends BaseEntity {
private Long[] postIds;
/**
* 角色ID
* 数据权限 当前角色ID
*/
@ApiModelProperty(value = "角色ID")
@TableField(exist = false)

View File

@ -1,9 +1,18 @@
package com.ruoyi.common.core.mybatisplus.core;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.common.utils.BeanCopyUtils;
import org.apache.ibatis.annotations.Param;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
* 自定义 Mapper 接口, 实现 自定义扩展
@ -18,4 +27,72 @@ public interface BaseMapperPlus<T> extends BaseMapper<T> {
*/
int insertAll(@Param("list") Collection<T> batchList);
/**
* 根据 ID 查询
*/
default <V> V selectVoById(Serializable id, Class<V> voClass){
T obj = this.selectById(id);
if (ObjectUtil.isNull(obj)) {
return null;
}
return BeanCopyUtils.copy(obj, voClass);
}
/**
* 查询根据ID 批量查询
*/
default <V> List<V> selectVoBatchIds(Collection<? extends Serializable> idList, Class<V> voClass){
List<T> list = this.selectBatchIds(idList);
if (CollUtil.isEmpty(list)) {
return CollUtil.newArrayList();
}
return BeanCopyUtils.copyList(list, voClass);
}
/**
* 查询根据 columnMap 条件
*/
default <V> List<V> selectVoByMap(Map<String, Object> map, Class<V> voClass){
List<T> list = this.selectByMap(map);
if (CollUtil.isEmpty(list)) {
return CollUtil.newArrayList();
}
return BeanCopyUtils.copyList(list, voClass);
}
/**
* 根据 entity 条件查询一条记录
*/
default <V> V selectVoOne(Wrapper<T> wrapper, Class<V> voClass) {
T obj = this.selectOne(wrapper);
if (ObjectUtil.isNull(obj)) {
return null;
}
return BeanCopyUtils.copy(obj, voClass);
}
/**
* 根据 entity 条件查询全部记录
*/
default <V> List<V> selectVoList(Wrapper<T> wrapper, Class<V> voClass) {
List<T> list = this.selectList(wrapper);
if (CollUtil.isEmpty(list)) {
return CollUtil.newArrayList();
}
return BeanCopyUtils.copyList(list, voClass);
}
/**
* 分页查询VO
*/
default <V, P extends IPage<V>> P selectVoPage(IPage<T> page, Wrapper<T> wrapper, Class<V> voClass) {
IPage<T> pageData = this.selectPage(page, wrapper);
IPage<V> voPage = new Page<>(pageData.getCurrent(), pageData.getSize(), pageData.getTotal());
if (CollUtil.isEmpty(pageData.getRecords())) {
return (P) voPage;
}
voPage.setRecords(BeanCopyUtils.copyList(pageData.getRecords(), voClass));
return (P) voPage;
}
}

View File

@ -1,7 +1,7 @@
package com.ruoyi.common.core.mybatisplus.core;
import cn.hutool.core.bean.copier.CopyOptions;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.common.core.page.PagePlus;
@ -23,14 +23,9 @@ public interface IServicePlus<T, V> extends IService<T> {
/**
* @param id 主键id
* @param copyOptions copy条件
* @return V对象
*/
V getVoById(Serializable id, CopyOptions copyOptions);
default V getVoById(Serializable id) {
return getVoById(id, new CopyOptions());
}
V getVoById(Serializable id);
/**
* @param convertor 自定义转换器
@ -41,14 +36,9 @@ public interface IServicePlus<T, V> extends IService<T> {
/**
* @param idList id列表
* @param copyOptions copy条件
* @return V对象
*/
List<V> listVoByIds(Collection<? extends Serializable> idList, CopyOptions copyOptions);
default List<V> listVoByIds(Collection<? extends Serializable> idList) {
return listVoByIds(idList, new CopyOptions());
}
List<V> listVoByIds(Collection<? extends Serializable> idList);
/**
* @param convertor 自定义转换器
@ -64,14 +54,9 @@ public interface IServicePlus<T, V> extends IService<T> {
/**
* @param columnMap 表字段 map 对象
* @param copyOptions copy条件
* @return V对象
*/
List<V> listVoByMap(Map<String, Object> columnMap, CopyOptions copyOptions);
default List<V> listVoByMap(Map<String, Object> columnMap) {
return listVoByMap(columnMap, new CopyOptions());
}
List<V> listVoByMap(Map<String, Object> columnMap);
/**
* @param convertor 自定义转换器
@ -87,14 +72,9 @@ public interface IServicePlus<T, V> extends IService<T> {
/**
* @param queryWrapper 查询条件
* @param copyOptions copy条件
* @return V对象
*/
V getVoOne(Wrapper<T> queryWrapper, CopyOptions copyOptions);
default V getVoOne(Wrapper<T> queryWrapper) {
return getVoOne(queryWrapper, new CopyOptions());
}
V getVoOne(Wrapper<T> queryWrapper);
/**
* @param convertor 自定义转换器
@ -105,14 +85,9 @@ public interface IServicePlus<T, V> extends IService<T> {
/**
* @param queryWrapper 查询条件
* @param copyOptions copy条件
* @return V对象
*/
List<V> listVo(Wrapper<T> queryWrapper, CopyOptions copyOptions);
default List<V> listVo(Wrapper<T> queryWrapper) {
return listVo(queryWrapper, new CopyOptions());
}
List<V> listVo(Wrapper<T> queryWrapper);
/**
* @param convertor 自定义转换器
@ -139,31 +114,36 @@ public interface IServicePlus<T, V> extends IService<T> {
/**
* @param page 分页对象
* @param queryWrapper 查询条件
* @param copyOptions copy条件
* @return V对象
* @deprecated 3.6.0 移除 请使用 {@link ServicePlusImpl#pageVo(IPage, Wrapper)}
*/
PagePlus<T, V> pageVo(PagePlus<T, V> page, Wrapper<T> queryWrapper, CopyOptions copyOptions);
@Deprecated
PagePlus<T, V> pageVo(PagePlus<T, V> page, Wrapper<T> queryWrapper);
default PagePlus<T, V> pageVo(PagePlus<T, V> page, Wrapper<T> queryWrapper) {
return pageVo(page, queryWrapper, new CopyOptions());
}
/**
* @param convertor 自定义转换器
*/
/**
* @param convertor 自定义转换器
* @deprecated 3.6.0 移除 请使用 {@link ServicePlusImpl#pageVo(IPage, Wrapper)}
*/
@Deprecated
default PagePlus<T, V> pageVo(PagePlus<T, V> page, Wrapper<T> queryWrapper,
Function<Collection<T>, List<V>> convertor) {
PagePlus<T, V> result = getBaseMapper().selectPage(page, queryWrapper);
return result.setRecordsVo(convertor.apply(result.getRecords()));
}
/**
* @deprecated 3.6.0 移除 请使用 {@link ServicePlusImpl#pageVo(IPage, Wrapper)}
*/
@Deprecated
default PagePlus<T, V> pageVo(PagePlus<T, V> page) {
return pageVo(page, Wrappers.emptyWrapper());
}
/**
* @param convertor 自定义转换器
* @deprecated 3.6.0 移除 请使用 {@link ServicePlusImpl#pageVo(IPage, Wrapper)}
*/
@Deprecated
default PagePlus<T, V> pageVo(PagePlus<T, V> page, Function<Collection<T>, List<V>> convertor) {
return pageVo(page, Wrappers.emptyWrapper(), convertor);
}

View File

@ -1,9 +1,9 @@
package com.ruoyi.common.core.mybatisplus.core;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.Assert;
@ -161,81 +161,66 @@ public class ServicePlusImpl<M extends BaseMapperPlus<T>, T, V> extends ServiceI
/**
* 根据 ID 查询
*
* @param id 主键ID
*/
@Override
public V getVoById(Serializable id, CopyOptions copyOptions) {
T t = getBaseMapper().selectById(id);
return BeanCopyUtils.oneCopy(t, copyOptions, voClass);
public V getVoById(Serializable id) {
return getBaseMapper().selectVoById(id, voClass);
}
/**
* 查询根据ID 批量查询
*
* @param idList 主键ID列表
*/
@Override
public List<V> listVoByIds(Collection<? extends Serializable> idList, CopyOptions copyOptions) {
List<T> list = getBaseMapper().selectBatchIds(idList);
if (list == null) {
return null;
}
return BeanCopyUtils.listCopy(list, copyOptions, voClass);
public List<V> listVoByIds(Collection<? extends Serializable> idList) {
return getBaseMapper().selectVoBatchIds(idList, voClass);
}
/**
* 查询根据 columnMap 条件
*
* @param columnMap 表字段 map 对象
*/
@Override
public List<V> listVoByMap(Map<String, Object> columnMap, CopyOptions copyOptions) {
List<T> list = getBaseMapper().selectByMap(columnMap);
if (list == null) {
return null;
}
return BeanCopyUtils.listCopy(list, copyOptions, voClass);
public List<V> listVoByMap(Map<String, Object> columnMap) {
return getBaseMapper().selectVoByMap(columnMap, voClass);
}
/**
* 根据 Wrapper查询一条记录 <br/>
* <p>结果集如果是多个会抛出异常随机取一条加上限制条件 wrapper.last("LIMIT 1")</p>
*
* @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
*/
@Override
public V getVoOne(Wrapper<T> queryWrapper, CopyOptions copyOptions) {
T t = getOne(queryWrapper, true);
return BeanCopyUtils.oneCopy(t, copyOptions, voClass);
public V getVoOne(Wrapper<T> queryWrapper) {
return getBaseMapper().selectVoOne(queryWrapper, voClass);
}
/**
* 查询列表
*
* @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
*/
@Override
public List<V> listVo(Wrapper<T> queryWrapper, CopyOptions copyOptions) {
List<T> list = getBaseMapper().selectList(queryWrapper);
if (list == null) {
return null;
}
return BeanCopyUtils.listCopy(list, copyOptions, voClass);
public List<V> listVo(Wrapper<T> queryWrapper) {
return getBaseMapper().selectVoList(queryWrapper, voClass);
}
/**
* 翻页查询
*
* @param page 翻页对象
* @param queryWrapper 实体对象封装操作类
* @deprecated 3.6.0 移除 请使用 {@link #pageVo(IPage, Wrapper)}
*/
@Override
public PagePlus<T, V> pageVo(PagePlus<T, V> page, Wrapper<T> queryWrapper, CopyOptions copyOptions) {
@Deprecated
public PagePlus<T, V> pageVo(PagePlus<T, V> page, Wrapper<T> queryWrapper) {
PagePlus<T, V> result = getBaseMapper().selectPage(page, queryWrapper);
List<V> volist = BeanCopyUtils.listCopy(result.getRecords(), copyOptions, voClass);
List<V> volist = BeanCopyUtils.copyList(result.getRecords(), voClass);
result.setRecordsVo(volist);
return result;
}
/**
* 翻页查询
*
* @param page 翻页对象
* @param queryWrapper 实体对象封装操作类
*/
public <P extends IPage<V>> P pageVo(IPage<T> page, Wrapper<T> queryWrapper) {
return getBaseMapper().selectVoPage(page, queryWrapper, voClass);
}
}

View File

@ -16,9 +16,11 @@ import java.util.List;
* @param <T> 数据库实体
* @param <K> vo实体
* @author Lion Li
* @deprecated 3.6.0 删除 请使用 {@link com.ruoyi.common.core.domain.PageQuery#build()}
*/
@Data
@Accessors(chain = true)
@Deprecated
public class PagePlus<T,K> implements IPage<T> {
/**

View File

@ -1,5 +1,7 @@
package com.ruoyi.common.core.page;
import cn.hutool.http.HttpStatus;
import com.baomidou.mybatisplus.core.metadata.IPage;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@ -57,4 +59,29 @@ public class TableDataInfo<T> implements Serializable {
this.total = total;
}
public static <T> TableDataInfo<T> build(IPage<T> page) {
TableDataInfo<T> rspData = new TableDataInfo<>();
rspData.setCode(HttpStatus.HTTP_OK);
rspData.setMsg("查询成功");
rspData.setRows(page.getRecords());
rspData.setTotal(page.getTotal());
return rspData;
}
public static <T> TableDataInfo<T> build(List<T> list) {
TableDataInfo<T> rspData = new TableDataInfo<>();
rspData.setCode(HttpStatus.HTTP_OK);
rspData.setMsg("查询成功");
rspData.setRows(list);
rspData.setTotal(list.size());
return rspData;
}
public static <T> TableDataInfo<T> build() {
TableDataInfo<T> rspData = new TableDataInfo<>();
rspData.setCode(HttpStatus.HTTP_OK);
rspData.setMsg("查询成功");
return rspData;
}
}

View File

@ -0,0 +1,72 @@
package com.ruoyi.common.enums;
import com.ruoyi.common.utils.StringUtils;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 数据权限类型
*
* 语法支持 spel 模板表达式
*
* 内置数据 user 当前用户 内容参考 SysUser
* 如需扩展数据 可使用 {@link com.ruoyi.common.helper.DataPermissionHelper} 操作
* 内置服务 sdss 系统数据权限服务 内容参考 SysDataScopeService
* 如需扩展更多自定义服务 可以参考 sdss 自行编写
*
* @author Lion Li
* @version 3.5.0
*/
@Getter
@AllArgsConstructor
public enum DataScopeType {
/**
* 全部数据权限
*/
ALL("1", "", ""),
/**
* 自定数据权限
*/
CUSTOM("2", " #{#deptName} IN ( #{@sdss.getRoleCustom( #user.roleId )} ) ", ""),
/**
* 部门数据权限
*/
DEPT("3", " #{#deptName} = #{#user.deptId} ", ""),
/**
* 部门及以下数据权限
*/
DEPT_AND_CHILD("4", " #{#deptName} IN ( #{@sdss.getDeptAndChild( #user.deptId )} )", ""),
/**
* 仅本人数据权限
*/
SELF("5", " #{#userName} = #{#user.userId} " , " 1 = 0 ");
private final String code;
/**
* 语法 采用 spel 模板表达式
*/
private final String sqlTemplate;
/**
* 不满足 sqlTemplate 则填充
*/
private final String elseSql;
public static DataScopeType findCode(String code) {
if (StringUtils.isBlank(code)) {
return null;
}
for (DataScopeType type : values()) {
if (type.getCode().equals(code)) {
return type;
}
}
return null;
}
}

View File

@ -7,8 +7,10 @@ import lombok.Getter;
* 数据源
*
* @author Lion Li
* @deprecated 3.6.0 移除
*/
@AllArgsConstructor
@Deprecated
public enum DataSourceType {
/**
* 主库

View File

@ -5,7 +5,7 @@ import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.exception.ExcelAnalysisException;
import com.alibaba.excel.exception.ExcelDataConvertException;
import com.alibaba.fastjson.JSON;
import com.ruoyi.common.utils.JsonUtils;
import com.ruoyi.common.utils.ValidatorUtils;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@ -84,7 +84,7 @@ public class DefaultExcelListener<T> extends AnalysisEventListener<T> implements
@Override
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
this.headMap = headMap;
log.debug("解析到一条表头数据: {}", JSON.toJSONString(headMap));
log.debug("解析到一条表头数据: {}", JsonUtils.toJsonString(headMap));
}
@Override

View File

@ -9,6 +9,6 @@ public class CaptchaException extends UserException {
private static final long serialVersionUID = 1L;
public CaptchaException() {
super("user.jcaptcha.error", null);
super("user.jcaptcha.error");
}
}

View File

@ -9,6 +9,6 @@ public class CaptchaExpireException extends UserException {
private static final long serialVersionUID = 1L;
public CaptchaExpireException() {
super("user.jcaptcha.expire", null);
super("user.jcaptcha.expire");
}
}

View File

@ -9,6 +9,6 @@ public class UserPasswordNotMatchException extends UserException {
private static final long serialVersionUID = 1L;
public UserPasswordNotMatchException() {
super("user.password.not.match", null);
super("user.password.not.match");
}
}

View File

@ -0,0 +1,45 @@
package com.ruoyi.common.helper;
import cn.hutool.core.util.ObjectUtil;
import com.ruoyi.common.utils.ServletUtils;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
/**
* 数据权限助手
*
* @author Lion Li
* @version 3.5.0
*/
@SuppressWarnings("unchecked cast")
public class DataPermissionHelper {
private static final String DATA_PERMISSION_KEY = "data:permission";
public static <T> T getVariable(String key) {
Map<String, Object> context = getContext();
return (T) context.get(key);
}
public static void setVariable(String key, Object value) {
Map<String, Object> context = getContext();
context.put(key, value);
}
public static Map<String, Object> getContext() {
HttpServletRequest request = ServletUtils.getRequest();
Object attribute = request.getAttribute(DATA_PERMISSION_KEY);
if (ObjectUtil.isNull(attribute)) {
request.setAttribute(DATA_PERMISSION_KEY, new HashMap<>());
attribute = request.getAttribute(DATA_PERMISSION_KEY);
}
if (attribute instanceof Map) {
return (Map<String, Object>) attribute;
}
throw new NullPointerException("data permission context type exception");
}
}

View File

@ -1,66 +1,120 @@
package com.ruoyi.common.utils;
import cn.hutool.core.bean.copier.BeanCopier;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.extra.cglib.CglibUtil;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import java.util.List;
import java.util.stream.Collectors;
import java.util.Map;
/**
* bean深拷贝工具
* bean深拷贝工具(基于 cglib 性能优异)
*
* @author Lion Li
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class BeanCopyUtils {
/**
* 单对象基于class创建拷贝
*
* @param source 数据来源实体
* @param copyOptions copy条件
* @param desc 描述对象 转换后的对象
* @param source 数据来源实体
* @param desc 描述对象 转换后的对象
* @return desc
*/
public static <T, V> V oneCopy(T source, CopyOptions copyOptions, Class<V> desc) {
V v = ReflectUtil.newInstanceIfPossible(desc);
return oneCopy(source, copyOptions, v);
public static <T, V> V copy(T source, Class<V> desc) {
if (ObjectUtil.isNull(source)) {
return null;
}
if (ObjectUtil.isNull(desc)) {
return null;
}
return CglibUtil.copy(source, desc);
}
/**
* 单对象基于对象创建拷贝
*
* @param source 数据来源实体
* @param copyOptions copy条件
* @param desc 转换后的对象
* @param source 数据来源实体
* @param desc 转换后的对象
* @return desc
*/
public static <T, V> V oneCopy(T source, CopyOptions copyOptions, V desc) {
public static <T, V> V copy(T source, V desc) {
if (ObjectUtil.isNull(source)) {
return null;
}
return BeanCopier.create(source, desc, copyOptions).copy();
if (ObjectUtil.isNull(desc)) {
return null;
}
CglibUtil.copy(source, desc);
return desc;
}
/**
* 列表对象基于class创建拷贝
*
* @param sourceList 数据来源实体列表
* @param copyOptions copy条件
* @param desc 描述对象 转换后的对象
* @param sourceList 数据来源实体列表
* @param desc 描述对象 转换后的对象
* @return desc
*/
public static <T, V> List<V> listCopy(List<T> sourceList, CopyOptions copyOptions, Class<V> desc) {
public static <T, V> List<V> copyList(List<T> sourceList, Class<V> desc) {
if (ObjectUtil.isNull(sourceList)) {
return null;
}
if (CollUtil.isEmpty(sourceList)) {
return CollUtil.newArrayList();
}
return sourceList.stream()
.map(source -> oneCopy(source, copyOptions, desc))
.collect(Collectors.toList());
return CglibUtil.copyList(sourceList, () -> ReflectUtil.newInstanceIfPossible(desc));
}
/**
* bean拷贝到map
*
* @param bean 数据来源实体
* @return map对象
*/
public static <T> Map<String, Object> copyToMap(T bean) {
if (ObjectUtil.isNull(bean)) {
return null;
}
return CglibUtil.toMap(bean);
}
/**
* map拷贝到bean
*
* @param map 数据来源
* @param beanClass bean类
* @return bean对象
*/
public static <T> T mapToBean(Map<String, Object> map, Class<T> beanClass) {
if (MapUtil.isEmpty(map)) {
return null;
}
if (ObjectUtil.isNull(beanClass)) {
return null;
}
return CglibUtil.toBean(map, beanClass);
}
/**
* map拷贝到bean
*
* @param map 数据来源
* @param bean bean对象
* @return bean对象
*/
public static <T> T mapToBean(Map<String, Object> map, T bean) {
if (MapUtil.isEmpty(map)) {
return null;
}
if (ObjectUtil.isNull(bean)) {
return null;
}
return CglibUtil.fillBean(map, bean);
}
}

View File

@ -1,5 +1,7 @@
package com.ruoyi.common.utils;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.apache.commons.lang3.time.DateFormatUtils;
import java.lang.management.ManagementFactory;
@ -12,6 +14,7 @@ import java.util.Date;
*
* @author ruoyi
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class DateUtils extends org.apache.commons.lang3.time.DateUtils {
public static String YYYY = "yyyy";

View File

@ -1,158 +0,0 @@
package com.ruoyi.common.utils;
import cn.hutool.core.collection.CollUtil;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.entity.SysDictData;
import java.util.Collection;
import java.util.List;
/**
* 字典工具类
*
* @author ruoyi
* @deprecated 3.5.0 版本删除 迁移至 {@link com.ruoyi.common.core.service.DictService}
*/
@Deprecated
public class DictUtils {
/**
* 分隔符
*/
public static final String SEPARATOR = ",";
/**
* 设置字典缓存
*
* @param key 参数键
* @param dictDatas 字典数据列表
*/
public static void setDictCache(String key, List<SysDictData> dictDatas) {
RedisUtils.setCacheObject(getCacheKey(key), dictDatas);
}
/**
* 获取字典缓存
*
* @param key 参数键
* @return dictDatas 字典数据列表
*/
public static List<SysDictData> getDictCache(String key) {
List<SysDictData> dictDatas = RedisUtils.getCacheObject(getCacheKey(key));
if (StringUtils.isNotNull(dictDatas)) {
return dictDatas;
}
return null;
}
/**
* 根据字典类型和字典值获取字典标签
*
* @param dictType 字典类型
* @param dictValue 字典值
* @return 字典标签
*/
public static String getDictLabel(String dictType, String dictValue) {
return getDictLabel(dictType, dictValue, SEPARATOR);
}
/**
* 根据字典类型和字典标签获取字典值
*
* @param dictType 字典类型
* @param dictLabel 字典标签
* @return 字典值
*/
public static String getDictValue(String dictType, String dictLabel) {
return getDictValue(dictType, dictLabel, SEPARATOR);
}
/**
* 根据字典类型和字典值获取字典标签
*
* @param dictType 字典类型
* @param dictValue 字典值
* @param separator 分隔符
* @return 字典标签
*/
public static String getDictLabel(String dictType, String dictValue, String separator) {
StringBuilder propertyString = new StringBuilder();
List<SysDictData> datas = getDictCache(dictType);
if (StringUtils.containsAny(dictValue, separator) && CollUtil.isNotEmpty(datas)) {
for (SysDictData dict : datas) {
for (String value : dictValue.split(separator)) {
if (value.equals(dict.getDictValue())) {
propertyString.append(dict.getDictLabel() + separator);
break;
}
}
}
} else {
for (SysDictData dict : datas) {
if (dictValue.equals(dict.getDictValue())) {
return dict.getDictLabel();
}
}
}
return StringUtils.stripEnd(propertyString.toString(), separator);
}
/**
* 根据字典类型和字典标签获取字典值
*
* @param dictType 字典类型
* @param dictLabel 字典标签
* @param separator 分隔符
* @return 字典值
*/
public static String getDictValue(String dictType, String dictLabel, String separator) {
StringBuilder propertyString = new StringBuilder();
List<SysDictData> datas = getDictCache(dictType);
if (StringUtils.containsAny(dictLabel, separator) && CollUtil.isNotEmpty(datas)) {
for (SysDictData dict : datas) {
for (String label : dictLabel.split(separator)) {
if (label.equals(dict.getDictLabel())) {
propertyString.append(dict.getDictValue() + separator);
break;
}
}
}
} else {
for (SysDictData dict : datas) {
if (dictLabel.equals(dict.getDictLabel())) {
return dict.getDictValue();
}
}
}
return StringUtils.stripEnd(propertyString.toString(), separator);
}
/**
* 删除指定字典缓存
*
* @param key 字典键
*/
public static void removeDictCache(String key) {
RedisUtils.deleteObject(getCacheKey(key));
}
/**
* 清空字典缓存
*/
public static void clearDictCache() {
Collection<String> keys = RedisUtils.keys(Constants.SYS_DICT_KEY + "*");
RedisUtils.deleteObject(keys);
}
/**
* 设置cache key
*
* @param configKey 参数键
* @return 缓存键key
*/
public static String getCacheKey(String configKey) {
return Constants.SYS_DICT_KEY + configKey;
}
}

View File

@ -21,14 +21,18 @@ import java.util.Map;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class JsonUtils {
private static ObjectMapper objectMapper = SpringUtils.getBean(ObjectMapper.class);
private static final ObjectMapper OBJECT_MAPPER = SpringUtils.getBean(ObjectMapper.class);
public static ObjectMapper getObjectMapper() {
return OBJECT_MAPPER;
}
public static String toJsonString(Object object) {
if (StringUtils.isNull(object)) {
return null;
}
try {
return objectMapper.writeValueAsString(object);
return OBJECT_MAPPER.writeValueAsString(object);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
@ -39,7 +43,7 @@ public class JsonUtils {
return null;
}
try {
return objectMapper.readValue(text, clazz);
return OBJECT_MAPPER.readValue(text, clazz);
} catch (IOException e) {
throw new RuntimeException(e);
}
@ -50,7 +54,7 @@ public class JsonUtils {
return null;
}
try {
return objectMapper.readValue(bytes, clazz);
return OBJECT_MAPPER.readValue(bytes, clazz);
} catch (IOException e) {
throw new RuntimeException(e);
}
@ -61,7 +65,7 @@ public class JsonUtils {
return null;
}
try {
return objectMapper.readValue(text, typeReference);
return OBJECT_MAPPER.readValue(text, typeReference);
} catch (IOException e) {
throw new RuntimeException(e);
}
@ -72,7 +76,7 @@ public class JsonUtils {
return null;
}
try {
return objectMapper.readValue(text, new TypeReference<Map<String, T>>() {
return OBJECT_MAPPER.readValue(text, new TypeReference<Map<String, T>>() {
});
} catch (IOException e) {
throw new RuntimeException(e);
@ -84,7 +88,7 @@ public class JsonUtils {
return new ArrayList<>();
}
try {
return objectMapper.readValue(text, objectMapper.getTypeFactory().constructCollectionType(List.class, clazz));
return OBJECT_MAPPER.readValue(text, OBJECT_MAPPER.getTypeFactory().constructCollectionType(List.class, clazz));
} catch (IOException e) {
throw new RuntimeException(e);
}

View File

@ -1,15 +1,21 @@
package com.ruoyi.common.utils;
import com.ruoyi.common.utils.spring.SpringUtils;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
/**
* 获取i18n资源文件
*
* @author ruoyi
* @author Lion Li
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class MessageUtils {
private static final MessageSource MESSAGE_SOURCE = SpringUtils.getBean(MessageSource.class);
/**
* 根据消息键和参数 获取消息 委托给spring messageSource
*
@ -18,7 +24,6 @@ public class MessageUtils {
* @return 获取国际化翻译值
*/
public static String message(String code, Object... args) {
MessageSource messageSource = SpringUtils.getBean(MessageSource.class);
return messageSource.getMessage(code, args, LocaleContextHolder.getLocale());
return MESSAGE_SOURCE.getMessage(code, args, LocaleContextHolder.getLocale());
}
}

View File

@ -2,11 +2,15 @@ package com.ruoyi.common.utils;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.HttpStatus;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.common.core.domain.PageQuery;
import com.ruoyi.common.core.page.PagePlus;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.utils.sql.SqlUtil;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import java.util.List;
@ -14,37 +18,46 @@ import java.util.List;
* 分页工具
*
* @author Lion Li
* @deprecated 3.6.0 删除 请使用 {@link PageQuery} {@link TableDataInfo}
*/
@Deprecated
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class PageUtils {
/**
* 当前记录起始索引
*/
@Deprecated
public static final String PAGE_NUM = "pageNum";
/**
* 每页显示记录数
*/
@Deprecated
public static final String PAGE_SIZE = "pageSize";
/**
* 排序列
*/
@Deprecated
public static final String ORDER_BY_COLUMN = "orderByColumn";
/**
* 排序的方向 "desc" 或者 "asc".
*/
@Deprecated
public static final String IS_ASC = "isAsc";
/**
* 当前记录起始索引 默认值
*/
@Deprecated
public static final int DEFAULT_PAGE_NUM = 1;
/**
* 每页显示记录数 默认值 默认查全部
*/
@Deprecated
public static final int DEFAULT_PAGE_SIZE = Integer.MAX_VALUE;
/**
@ -53,7 +66,10 @@ public class PageUtils {
* @param <T> domain 实体
* @param <K> vo 实体
* @return 分页对象
* @deprecated 3.6.0 删除 请使用 {@link PageQuery#build()}
* 由于使用 Servlet 获取只能从 param 获取 灵活性降低 故将传参操作交给用户
*/
@Deprecated
public static <T, K> PagePlus<T, K> buildPagePlus() {
Integer pageNum = ServletUtils.getParameterToInt(PAGE_NUM, DEFAULT_PAGE_NUM);
Integer pageSize = ServletUtils.getParameterToInt(PAGE_SIZE, DEFAULT_PAGE_SIZE);
@ -70,6 +86,7 @@ public class PageUtils {
return page;
}
@Deprecated
public static <T> Page<T> buildPage() {
return buildPage(null, null);
}
@ -79,7 +96,10 @@ public class PageUtils {
*
* @param <T> domain 实体
* @return 分页对象
* @deprecated 3.6.0 删除 请使用 {@link PageQuery#build()}
* 由于使用 Servlet 获取只能从 param 获取 灵活性降低 故将传参操作交给用户
*/
@Deprecated
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);
@ -115,6 +135,15 @@ public class PageUtils {
return null;
}
/**
* 构建 MP 普通分页对象
*
* @param <T> domain 实体
* @return 分页对象
* @deprecated 3.6.0 删除 请使用 {@link PageQuery#build()}
* 由于使用 Servlet 获取只能从 param 获取 灵活性降低 故将传参操作交给用户
*/
@Deprecated
public static <T, K> TableDataInfo<K> buildDataInfo(PagePlus<T, K> page) {
TableDataInfo<K> rspData = new TableDataInfo<>();
rspData.setCode(HttpStatus.HTTP_OK);
@ -124,6 +153,10 @@ public class PageUtils {
return rspData;
}
/**
* @deprecated 3.6.0 删除 请使用 {@link TableDataInfo#build(IPage)}
*/
@Deprecated
public static <T> TableDataInfo<T> buildDataInfo(Page<T> page) {
TableDataInfo<T> rspData = new TableDataInfo<>();
rspData.setCode(HttpStatus.HTTP_OK);
@ -133,6 +166,10 @@ public class PageUtils {
return rspData;
}
/**
* @deprecated 3.6.0 删除 请使用 {@link TableDataInfo#build(List)}
*/
@Deprecated
public static <T> TableDataInfo<T> buildDataInfo(List<T> list) {
TableDataInfo<T> rspData = new TableDataInfo<>();
rspData.setCode(HttpStatus.HTTP_OK);
@ -142,6 +179,10 @@ public class PageUtils {
return rspData;
}
/**
* @deprecated 3.6.0 删除 请使用 {@link TableDataInfo#build()}
*/
@Deprecated
public static <T> TableDataInfo<T> buildDataInfo() {
TableDataInfo<T> rspData = new TableDataInfo<>();
rspData.setCode(HttpStatus.HTTP_OK);

View File

@ -1,6 +1,6 @@
package com.ruoyi.common.utils;
import com.google.common.collect.Lists;
import cn.hutool.core.collection.IterUtil;
import com.ruoyi.common.utils.spring.SpringUtils;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
@ -23,7 +23,7 @@ import java.util.function.Consumer;
@SuppressWarnings(value = {"unchecked", "rawtypes"})
public class RedisUtils {
private static RedissonClient client = SpringUtils.getBean(RedissonClient.class);
private static final RedissonClient CLIENT = SpringUtils.getBean(RedissonClient.class);
/**
* 限流
@ -35,7 +35,7 @@ public class RedisUtils {
* @return -1 表示失败
*/
public static long rateLimiter(String key, RateType rateType, int rate, int rateInterval) {
RRateLimiter rateLimiter = client.getRateLimiter(key);
RRateLimiter rateLimiter = CLIENT.getRateLimiter(key);
rateLimiter.trySetRate(rateType, rate, rateInterval, RateIntervalUnit.SECONDS);
if (rateLimiter.tryAcquire()) {
return rateLimiter.availablePermits();
@ -45,10 +45,10 @@ public class RedisUtils {
}
/**
* 获取实例id
* 获取客户端实例
*/
public static String getClientId() {
return client.getId();
public static RedissonClient getClient() {
return CLIENT;
}
/**
@ -59,13 +59,13 @@ public class RedisUtils {
* @param consumer 自定义处理
*/
public static <T> void publish(String channelKey, T msg, Consumer<T> consumer) {
RTopic topic = client.getTopic(channelKey);
RTopic topic = CLIENT.getTopic(channelKey);
topic.publish(msg);
consumer.accept(msg);
}
public static <T> void publish(String channelKey, T msg) {
RTopic topic = client.getTopic(channelKey);
RTopic topic = CLIENT.getTopic(channelKey);
topic.publish(msg);
}
@ -77,7 +77,7 @@ public class RedisUtils {
* @param consumer 自定义处理
*/
public static <T> void subscribe(String channelKey, Class<T> clazz, Consumer<T> consumer) {
RTopic topic = client.getTopic(channelKey);
RTopic topic = CLIENT.getTopic(channelKey);
topic.addListener(clazz, (channel, msg) -> consumer.accept(msg));
}
@ -94,13 +94,13 @@ public class RedisUtils {
/**
* 缓存基本的对象保留当前对象 TTL 有效期
*
* @param key 缓存的键值
* @param value 缓存的值
* @param key 缓存的键值
* @param value 缓存的值
* @param isSaveTtl 是否保留TTL有效期(例如: set之前ttl剩余90 set之后还是为90)
* @since Redis 6.X 以上使用 setAndKeepTTL 兼容 5.X 方案
*/
public static <T> void setCacheObject(final String key, final T value, final boolean isSaveTtl) {
RBucket<Object> bucket = client.getBucket(key);
RBucket<Object> bucket = CLIENT.getBucket(key);
if (isSaveTtl) {
try {
bucket.setAndKeepTTL(value);
@ -123,11 +123,24 @@ public class RedisUtils {
* @param timeUnit 时间颗粒度
*/
public static <T> void setCacheObject(final String key, final T value, final long timeout, final TimeUnit timeUnit) {
RBucket<T> result = client.getBucket(key);
RBucket<T> result = CLIENT.getBucket(key);
result.set(value);
result.expire(timeout, timeUnit);
}
/**
* 注册对象监听器
*
* key 监听器需开启 `notify-keyspace-events` redis 相关配置
*
* @param key 缓存的键值
* @param listener 监听器配置
*/
public static <T> void addObjectListener(final String key, final ObjectListener listener) {
RBucket<T> result = CLIENT.getBucket(key);
result.addListener(listener);
}
/**
* 设置有效时间
*
@ -148,7 +161,7 @@ public class RedisUtils {
* @return true=设置成功false=设置失败
*/
public static boolean expire(final String key, final long timeout, final TimeUnit unit) {
RBucket rBucket = client.getBucket(key);
RBucket rBucket = CLIENT.getBucket(key);
return rBucket.expire(timeout, unit);
}
@ -159,7 +172,7 @@ public class RedisUtils {
* @return 缓存键值对应的数据
*/
public static <T> T getCacheObject(final String key) {
RBucket<T> rBucket = client.getBucket(key);
RBucket<T> rBucket = CLIENT.getBucket(key);
return rBucket.get();
}
@ -170,29 +183,26 @@ public class RedisUtils {
* @return 剩余存活时间
*/
public static <T> long getTimeToLive(final String key) {
RBucket<T> rBucket = client.getBucket(key);
RBucket<T> rBucket = CLIENT.getBucket(key);
return rBucket.remainTimeToLive();
}
/**
* 删除单个对象
*
* @param key
* @param key 缓存的键值
*/
public static boolean deleteObject(final String key) {
return client.getBucket(key).delete();
return CLIENT.getBucket(key).delete();
}
/* */
/**
* 删除集合对象
*
* @param collection 多个对象
* @return
*/
public static void deleteObject(final Collection collection) {
RBatch batch = client.createBatch();
RBatch batch = CLIENT.createBatch();
collection.forEach(t -> {
batch.getBucket(t.toString()).deleteAsync();
});
@ -207,10 +217,23 @@ public class RedisUtils {
* @return 缓存的对象
*/
public static <T> boolean setCacheList(final String key, final List<T> dataList) {
RList<T> rList = client.getList(key);
RList<T> rList = CLIENT.getList(key);
return rList.addAll(dataList);
}
/**
* 注册List监听器
*
* key 监听器需开启 `notify-keyspace-events` redis 相关配置
*
* @param key 缓存的键值
* @param listener 监听器配置
*/
public static <T> void addListListener(final String key, final ObjectListener listener) {
RList<T> rList = CLIENT.getList(key);
rList.addListener(listener);
}
/**
* 获得缓存的list对象
*
@ -218,7 +241,7 @@ public class RedisUtils {
* @return 缓存键值对应的数据
*/
public static <T> List<T> getCacheList(final String key) {
RList<T> rList = client.getList(key);
RList<T> rList = CLIENT.getList(key);
return rList.readAll();
}
@ -230,42 +253,68 @@ public class RedisUtils {
* @return 缓存数据的对象
*/
public static <T> boolean setCacheSet(final String key, final Set<T> dataSet) {
RSet<T> rSet = client.getSet(key);
RSet<T> rSet = CLIENT.getSet(key);
return rSet.addAll(dataSet);
}
/**
* 注册Set监听器
*
* key 监听器需开启 `notify-keyspace-events` redis 相关配置
*
* @param key 缓存的键值
* @param listener 监听器配置
*/
public static <T> void addSetListener(final String key, final ObjectListener listener) {
RSet<T> rSet = CLIENT.getSet(key);
rSet.addListener(listener);
}
/**
* 获得缓存的set
*
* @param key
* @return
* @param key 缓存的key
* @return set对象
*/
public static <T> Set<T> getCacheSet(final String key) {
RSet<T> rSet = client.getSet(key);
RSet<T> rSet = CLIENT.getSet(key);
return rSet.readAll();
}
/**
* 缓存Map
*
* @param key
* @param dataMap
* @param key 缓存的键值
* @param dataMap 缓存的数据
*/
public static <T> void setCacheMap(final String key, final Map<String, T> dataMap) {
if (dataMap != null) {
RMap<String, T> rMap = client.getMap(key);
RMap<String, T> rMap = CLIENT.getMap(key);
rMap.putAll(dataMap);
}
}
/**
* 注册Map监听器
*
* key 监听器需开启 `notify-keyspace-events` redis 相关配置
*
* @param key 缓存的键值
* @param listener 监听器配置
*/
public static <T> void addMapListener(final String key, final ObjectListener listener) {
RMap<String, T> rMap = CLIENT.getMap(key);
rMap.addListener(listener);
}
/**
* 获得缓存的Map
*
* @param key
* @return
* @param key 缓存的键值
* @return map对象
*/
public static <T> Map<String, T> getCacheMap(final String key) {
RMap<String, T> rMap = client.getMap(key);
RMap<String, T> rMap = CLIENT.getMap(key);
return rMap.getAll(rMap.keySet());
}
@ -277,7 +326,7 @@ public class RedisUtils {
* @param value
*/
public static <T> void setCacheMapValue(final String key, final String hKey, final T value) {
RMap<String, T> rMap = client.getMap(key);
RMap<String, T> rMap = CLIENT.getMap(key);
rMap.put(hKey, value);
}
@ -289,7 +338,7 @@ public class RedisUtils {
* @return Hash中的对象
*/
public static <T> T getCacheMapValue(final String key, final String hKey) {
RMap<String, T> rMap = client.getMap(key);
RMap<String, T> rMap = CLIENT.getMap(key);
return rMap.get(hKey);
}
@ -301,7 +350,7 @@ public class RedisUtils {
* @return Hash中的对象
*/
public static <T> T delCacheMapValue(final String key, final String hKey) {
RMap<String, T> rMap = client.getMap(key);
RMap<String, T> rMap = CLIENT.getMap(key);
return rMap.remove(hKey);
}
@ -313,7 +362,7 @@ public class RedisUtils {
* @return Hash对象集合
*/
public static <K, V> Map<K, V> getMultiCacheMapValue(final String key, final Set<K> hKeys) {
RMap<K, V> rMap = client.getMap(key);
RMap<K, V> rMap = CLIENT.getMap(key);
return rMap.getAll(hKeys);
}
@ -324,7 +373,7 @@ public class RedisUtils {
* @return 对象列表
*/
public static Collection<String> keys(final String pattern) {
Iterable<String> iterable = client.getKeys().getKeysByPattern(pattern);
return Lists.newArrayList(iterable);
Iterable<String> iterable = CLIENT.getKeys().getKeysByPattern(pattern);
return IterUtil.toList(iterable);
}
}

View File

@ -5,6 +5,10 @@ import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.service.UserService;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.spring.SpringUtils;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
/**
@ -12,6 +16,7 @@ import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
*
* @author Long Li
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class SecurityUtils {
/**

View File

@ -3,6 +3,8 @@ package com.ruoyi.common.utils;
import cn.hutool.core.convert.Convert;
import cn.hutool.extra.servlet.ServletUtil;
import cn.hutool.http.HttpStatus;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.springframework.http.MediaType;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
@ -19,6 +21,7 @@ import java.nio.charset.StandardCharsets;
*
* @author ruoyi
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class ServletUtils extends ServletUtil {
/**

View File

@ -7,6 +7,8 @@ import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import java.util.*;
@ -15,6 +17,7 @@ import java.util.*;
*
* @author Lion Li
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class StringUtils extends org.apache.commons.lang3.StringUtils {
/**

View File

@ -1,7 +1,8 @@
package com.ruoyi.common.utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.*;
@ -10,8 +11,9 @@ import java.util.concurrent.*;
*
* @author ruoyi
*/
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class Threads {
private static final Logger logger = LoggerFactory.getLogger(Threads.class);
/**
* sleep等待,单位为毫秒
@ -38,7 +40,7 @@ public class Threads {
if (!pool.awaitTermination(120, TimeUnit.SECONDS)) {
pool.shutdownNow();
if (!pool.awaitTermination(120, TimeUnit.SECONDS)) {
logger.info("Pool did not terminate");
log.info("Pool did not terminate");
}
}
} catch (InterruptedException ie) {
@ -67,7 +69,7 @@ public class Threads {
}
}
if (t != null) {
logger.error(t.getMessage(), t);
log.error(t.getMessage(), t);
}
}
}

View File

@ -4,6 +4,8 @@ import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeNodeConfig;
import cn.hutool.core.lang.tree.TreeUtil;
import cn.hutool.core.lang.tree.parser.NodeParser;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import java.util.List;
@ -12,6 +14,7 @@ import java.util.List;
*
* @author Lion Li
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class TreeBuildUtils extends TreeUtil {
/**
@ -19,13 +22,8 @@ public class TreeBuildUtils extends TreeUtil {
*/
public static final TreeNodeConfig DEFAULT_CONFIG = TreeNodeConfig.DEFAULT_CONFIG.setNameKey("label");
/**
* 默认树父节点id
*/
public static final Long DEFAULT_PARENT_ID = 0L;
public static <T> List<Tree<Long>> build(List<T> list, NodeParser<T, Long> nodeParser) {
return TreeUtil.build(list, DEFAULT_PARENT_ID, DEFAULT_CONFIG, nodeParser);
public static <T> List<Tree<Long>> build(List<T> list, Long parentId, NodeParser<T, Long> nodeParser) {
return TreeUtil.build(list, parentId, DEFAULT_CONFIG, nodeParser);
}
}

View File

@ -1,8 +1,11 @@
package com.ruoyi.common.utils;
import com.ruoyi.common.utils.spring.SpringUtils;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.Validation;
import javax.validation.Validator;
import java.util.Set;
@ -11,9 +14,10 @@ import java.util.Set;
*
* @author Lion Li
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class ValidatorUtils {
private static final Validator VALID = Validation.buildDefaultValidatorFactory().getValidator();
private static final Validator VALID = SpringUtils.getBean(Validator.class);
public static <T> void validate(T object, Class<?>... groups) {
Set<ConstraintViolation<T>> validate = VALID.validate(object, groups);

View File

@ -1,6 +1,8 @@
package com.ruoyi.common.utils.file;
import cn.hutool.core.io.FileUtil;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import javax.servlet.http.HttpServletResponse;
import java.io.UnsupportedEncodingException;
@ -12,6 +14,7 @@ import java.nio.charset.StandardCharsets;
*
* @author Lion Li
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class FileUtils extends FileUtil {
/**

View File

@ -7,6 +7,8 @@ import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.utils.JsonUtils;
import com.ruoyi.common.utils.StringUtils;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import java.util.Map;
@ -17,6 +19,7 @@ import java.util.Map;
* @author Lion Li
*/
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class AddressUtils {
// IP地址查询

View File

@ -9,6 +9,8 @@ import com.ruoyi.common.excel.ExcelListener;
import com.ruoyi.common.excel.ExcelResult;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.file.FileUtils;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
@ -21,6 +23,7 @@ import java.util.List;
*
* @author Lion Li
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class ExcelUtil {
/**

View File

@ -2,6 +2,8 @@ package com.ruoyi.common.utils.reflect;
import cn.hutool.core.util.ReflectUtil;
import com.ruoyi.common.utils.StringUtils;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import java.lang.reflect.Method;
@ -11,6 +13,7 @@ import java.lang.reflect.Method;
* @author Lion Li
*/
@SuppressWarnings("rawtypes")
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class ReflectUtils extends ReflectUtil {
private static final String SETTER_PREFIX = "set";

View File

@ -2,17 +2,26 @@ package com.ruoyi.common.utils.sql;
import com.ruoyi.common.exception.UtilException;
import com.ruoyi.common.utils.StringUtils;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
/**
* sql操作工具类
*
* @author ruoyi
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class SqlUtil {
/**
* 定义常用的 sql关键字
*/
public static String SQL_REGEX = "select |insert |delete |update |drop |count |exec |chr |mid |master |truncate |char |and |declare ";
/**
* 仅支持字母数字下划线空格逗号小数点支持多个字段排序
*/
public static String SQL_PATTERN = "[a-zA-Z0-9_\\ \\,\\.]+";
public static final String SQL_PATTERN = "[a-zA-Z0-9_\\ \\,\\.]+";
/**
* 检查字符防止注入绕过
@ -30,4 +39,19 @@ public class SqlUtil {
public static boolean isValidOrderBySql(String value) {
return value.matches(SQL_PATTERN);
}
/**
* SQL关键字检查
*/
public static void filterKeyword(String value) {
if (StringUtils.isEmpty(value)) {
return;
}
String[] sqlKeywords = StringUtils.split(SQL_REGEX, "\\|");
for (String sqlKeyword : sqlKeywords) {
if (StringUtils.indexOfIgnoreCase(value, sqlKeyword) > -1) {
throw new UtilException("参数存在SQL注入风险");
}
}
}
}

View File

@ -0,0 +1,26 @@
package com.ruoyi.common.xss;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 自定义xss校验注解
*
* @author Lion Li
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(value = {ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.PARAMETER})
@Constraint(validatedBy = {XssValidator.class})
public @interface Xss {
String message() default "不允许任何脚本运行";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}

View File

@ -0,0 +1,21 @@
package com.ruoyi.common.xss;
import cn.hutool.core.util.ReUtil;
import cn.hutool.http.HtmlUtil;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
/**
* 自定义xss校验注解实现
*
* @author Lion Li
*/
public class XssValidator implements ConstraintValidator<Xss, String> {
@Override
public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) {
return !ReUtil.contains(HtmlUtil.RE_HTML_MARK, value);
}
}

View File

@ -33,10 +33,12 @@ public class TestBatchController extends BaseController {
/**
* 新增批量方法 可完美替代 saveBatch 秒级插入上万数据 (对mysql负荷较大)
*
* 3.5.0 版本 增加 rewriteBatchedStatements=true 批处理参数 使 MP 原生批处理可以达到同样的速度
*/
@ApiOperation(value = "新增批量方法")
@PostMapping("/add")
// @DataSource(DataSourceType.SLAVE)
// @DS("slave")
public AjaxResult<Void> add() {
List<TestDemo> list = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
@ -47,10 +49,12 @@ public class TestBatchController extends BaseController {
/**
* 新增或更新 可完美替代 saveOrUpdateBatch 高性能
*
* 3.5.0 版本 增加 rewriteBatchedStatements=true 批处理参数 使 MP 原生批处理可以达到同样的速度
*/
@ApiOperation(value = "新增或更新批量方法")
@PostMapping("/addOrUpdate")
// @DataSource(DataSourceType.SLAVE)
// @DS("slave")
public AjaxResult<Void> addOrUpdate() {
List<TestDemo> list = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
@ -72,7 +76,7 @@ public class TestBatchController extends BaseController {
*/
@ApiOperation(value = "删除批量方法")
@DeleteMapping()
// @DataSource(DataSourceType.SLAVE)
// @DS("slave")
public AjaxResult<Void> remove() {
return toAjax(iTestDemoService.remove(new LambdaQueryWrapper<TestDemo>()
.eq(TestDemo::getOrderNum, -1L)) ? 1 : 0);

View File

@ -6,6 +6,7 @@ import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.annotation.RepeatSubmit;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.PageQuery;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.core.validate.AddGroup;
import com.ruoyi.common.core.validate.EditGroup;
@ -54,8 +55,8 @@ public class TestDemoController extends BaseController {
@ApiOperation("查询测试单表列表")
@SaCheckPermission("demo:demo:list")
@GetMapping("/list")
public TableDataInfo<TestDemoVo> list(@Validated(QueryGroup.class) TestDemoBo bo) {
return iTestDemoService.queryPageList(bo);
public TableDataInfo<TestDemoVo> list(@Validated(QueryGroup.class) TestDemoBo bo, PageQuery pageQuery) {
return iTestDemoService.queryPageList(bo, pageQuery);
}
/**
@ -65,7 +66,7 @@ public class TestDemoController extends BaseController {
@SaCheckPermission("demo:demo:list")
@GetMapping("/page")
public TableDataInfo<TestDemoVo> page(@Validated(QueryGroup.class) TestDemoBo bo) {
return iTestDemoService.customPageList(bo);
return iTestDemoService.customPageList(bo, pageQuery);
}
@ApiOperation("导入测试-校验")

View File

@ -1,16 +1,15 @@
package com.ruoyi.demo.domain.bo;
import com.ruoyi.common.core.domain.BaseEntity;
import com.ruoyi.common.core.validate.AddGroup;
import com.ruoyi.common.core.validate.EditGroup;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.*;
import java.util.Date;
import com.ruoyi.common.core.domain.BaseEntity;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
/**
* 测试单表业务对象 test_demo
@ -66,29 +65,4 @@ public class TestDemoBo extends BaseEntity {
@NotBlank(message = "值不能为空", groups = { AddGroup.class, EditGroup.class })
private String value;
/**
* 分页大小
*/
@ApiModelProperty("分页大小")
private Integer pageSize;
/**
* 当前页数
*/
@ApiModelProperty("当前页数")
private Integer pageNum;
/**
* 排序列
*/
@ApiModelProperty("排序列")
private String orderByColumn;
/**
* 排序的方向desc或者asc
*/
@ApiModelProperty(value = "排序的方向", example = "asc,desc")
private String isAsc;
}

View File

@ -51,29 +51,4 @@ public class TestTreeBo extends TreeEntity {
@NotBlank(message = "树节点名不能为空", groups = { AddGroup.class, EditGroup.class })
private String treeName;
/**
* 分页大小
*/
@ApiModelProperty("分页大小")
private Integer pageSize;
/**
* 当前页数
*/
@ApiModelProperty("当前页数")
private Integer pageNum;
/**
* 排序列
*/
@ApiModelProperty("排序列")
private String orderByColumn;
/**
* 排序的方向desc或者asc
*/
@ApiModelProperty(value = "排序的方向", example = "asc,desc")
private String isAsc;
}

View File

@ -1,12 +1,20 @@
package com.ruoyi.demo.mapper;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.common.annotation.DataColumn;
import com.ruoyi.common.annotation.DataPermission;
import com.ruoyi.common.core.mybatisplus.core.BaseMapperPlus;
import com.ruoyi.demo.domain.TestDemo;
import com.ruoyi.demo.domain.vo.TestDemoVo;
import org.apache.ibatis.annotations.Param;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
/**
* 测试单表Mapper接口
*
@ -15,6 +23,37 @@ import org.apache.ibatis.annotations.Param;
*/
public interface TestDemoMapper extends BaseMapperPlus<TestDemo> {
@DataPermission({
@DataColumn(key = "deptName", value = "dept_id"),
@DataColumn(key = "userName", value = "user_id")
})
Page<TestDemoVo> customPageList(@Param("page") Page<TestDemo> page, @Param("ew") Wrapper<TestDemo> wrapper);
@Override
@DataPermission({
@DataColumn(key = "deptName", value = "dept_id"),
@DataColumn(key = "userName", value = "user_id")
})
<P extends IPage<TestDemo>> P selectPage(P page, @Param(Constants.WRAPPER) Wrapper<TestDemo> queryWrapper);
@Override
@DataPermission({
@DataColumn(key = "deptName", value = "dept_id"),
@DataColumn(key = "userName", value = "user_id")
})
List<TestDemo> selectList(@Param(Constants.WRAPPER) Wrapper<TestDemo> queryWrapper);
@Override
@DataPermission({
@DataColumn(key = "deptName", value = "dept_id"),
@DataColumn(key = "userName", value = "user_id")
})
int updateById(@Param(Constants.ENTITY) TestDemo entity);
@Override
@DataPermission({
@DataColumn(key = "deptName", value = "dept_id"),
@DataColumn(key = "userName", value = "user_id")
})
int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
}

View File

@ -1,5 +1,7 @@
package com.ruoyi.demo.mapper;
import com.ruoyi.common.annotation.DataColumn;
import com.ruoyi.common.annotation.DataPermission;
import com.ruoyi.common.core.mybatisplus.core.BaseMapperPlus;
import com.ruoyi.demo.domain.TestTree;
@ -9,6 +11,10 @@ import com.ruoyi.demo.domain.TestTree;
* @author Lion Li
* @date 2021-07-26
*/
@DataPermission({
@DataColumn(key = "deptName", value = "dept_id"),
@DataColumn(key = "userName", value = "user_id")
})
public interface TestTreeMapper extends BaseMapperPlus<TestTree> {
}

View File

@ -1,5 +1,6 @@
package com.ruoyi.demo.service;
import com.ruoyi.common.core.domain.PageQuery;
import com.ruoyi.demo.domain.TestDemo;
import com.ruoyi.demo.domain.vo.TestDemoVo;
import com.ruoyi.demo.domain.bo.TestDemoBo;
@ -26,12 +27,12 @@ public interface ITestDemoService extends IServicePlus<TestDemo, TestDemoVo> {
/**
* 查询列表
*/
TableDataInfo<TestDemoVo> queryPageList(TestDemoBo bo);
TableDataInfo<TestDemoVo> queryPageList(TestDemoBo bo, PageQuery pageQuery);
/**
* 自定义分页查询
*/
TableDataInfo<TestDemoVo> customPageList(TestDemoBo bo);
TableDataInfo<TestDemoVo> customPageList(TestDemoBo bo, PageQuery pageQuery);
/**
* 查询列表

View File

@ -1,15 +1,13 @@
package com.ruoyi.demo.service.impl;
import cn.hutool.core.bean.BeanUtil;
import com.ruoyi.common.utils.StringUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.common.annotation.DataScope;
import com.ruoyi.common.core.domain.PageQuery;
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;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.demo.domain.TestDemo;
import com.ruoyi.demo.domain.bo.TestDemoBo;
import com.ruoyi.demo.domain.vo.TestDemoVo;
@ -35,24 +33,23 @@ public class TestDemoServiceImpl extends ServicePlusImpl<TestDemoMapper, TestDem
return getVoById(id);
}
@DataScope(isUser = true)
@Override
public TableDataInfo<TestDemoVo> queryPageList(TestDemoBo bo) {
PagePlus<TestDemo, TestDemoVo> result = pageVo(PageUtils.buildPagePlus(), buildQueryWrapper(bo));
return PageUtils.buildDataInfo(result);
public TableDataInfo<TestDemoVo> queryPageList(TestDemoBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<TestDemo> lqw = buildQueryWrapper(bo);
Page<TestDemoVo> result = pageVo(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
/**
* 自定义分页查询
*/
@DataScope(isUser = true)
@Override
public TableDataInfo<TestDemoVo> customPageList(TestDemoBo bo) {
Page<TestDemoVo> result = baseMapper.customPageList(PageUtils.buildPage(), buildQueryWrapper(bo));
return PageUtils.buildDataInfo(result);
public TableDataInfo<TestDemoVo> customPageList(TestDemoBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<TestDemo> lqw = buildQueryWrapper(bo);
Page<TestDemoVo> result = baseMapper.customPageList(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
@DataScope(isUser = true)
@Override
public List<TestDemoVo> queryList(TestDemoBo bo) {
return listVo(buildQueryWrapper(bo));
@ -60,14 +57,11 @@ public class TestDemoServiceImpl extends ServicePlusImpl<TestDemoMapper, TestDem
private LambdaQueryWrapper<TestDemo> buildQueryWrapper(TestDemoBo bo) {
Map<String, Object> params = bo.getParams();
Object dataScope = params.get("dataScope");
LambdaQueryWrapper<TestDemo> lqw = Wrappers.lambdaQuery();
lqw.like(StringUtils.isNotBlank(bo.getTestKey()), TestDemo::getTestKey, bo.getTestKey());
lqw.eq(StringUtils.isNotBlank(bo.getValue()), TestDemo::getValue, bo.getValue());
lqw.between(params.get("beginCreateTime") != null && params.get("endCreateTime") != null,
TestDemo::getCreateTime, params.get("beginCreateTime"), params.get("endCreateTime"));
lqw.apply(dataScope != null && StringUtils.isNotBlank(dataScope.toString()),
dataScope != null ? dataScope.toString() : null);
return lqw;
}

View File

@ -3,7 +3,6 @@ package com.ruoyi.demo.service.impl;
import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ruoyi.common.annotation.DataScope;
import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.demo.domain.TestTree;
@ -23,7 +22,7 @@ import java.util.Map;
* @author Lion Li
* @date 2021-07-26
*/
//@DataSource(DataSourceType.SLAVE) // 切换从库查询
// @DS("slave") // 切换从库查询
@Service
public class TestTreeServiceImpl extends ServicePlusImpl<TestTreeMapper, TestTree, TestTreeVo> implements ITestTreeService {
@ -32,22 +31,19 @@ public class TestTreeServiceImpl extends ServicePlusImpl<TestTreeMapper, TestTre
return getVoById(id);
}
// @DataSource(DataSourceType.SLAVE) // 切换从库查询
@DataScope(isUser = true)
// @DS("slave") // 切换从库查询
@Override
public List<TestTreeVo> queryList(TestTreeBo bo) {
return listVo(buildQueryWrapper(bo));
LambdaQueryWrapper<TestTree> lqw = buildQueryWrapper(bo);
return listVo(lqw);
}
private LambdaQueryWrapper<TestTree> buildQueryWrapper(TestTreeBo bo) {
Map<String, Object> params = bo.getParams();
Object dataScope = params.get("dataScope");
LambdaQueryWrapper<TestTree> lqw = Wrappers.lambdaQuery();
lqw.like(StringUtils.isNotBlank(bo.getTreeName()), TestTree::getTreeName, bo.getTreeName());
lqw.between(params.get("beginCreateTime") != null && params.get("endCreateTime") != null,
TestTree::getCreateTime, params.get("beginCreateTime"), params.get("endCreateTime"));
lqw.apply(dataScope != null && StringUtils.isNotBlank(dataScope.toString()),
dataScope != null ? dataScope.toString() : null);
return lqw;
}

View File

@ -2,7 +2,6 @@ package com.ruoyi.monitor.admin.config;
import de.codecentric.boot.admin.server.config.AdminServerProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@ -15,7 +14,6 @@ import org.springframework.security.web.authentication.SavedRequestAwareAuthenti
*/
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, proxyTargetClass = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final String adminContextPath;
@ -34,8 +32,8 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
//授予对所有静态资产和登录页面的公共访问权限
.antMatchers(adminContextPath + "/assets/**").permitAll()
.antMatchers(adminContextPath + "/login").permitAll()
.antMatchers("/actuator").anonymous()
.antMatchers("/actuator/**").anonymous()
.antMatchers("/actuator").permitAll()
.antMatchers("/actuator/**").permitAll()
//必须对每个其他请求进行身份验证
.anyRequest().authenticated().and()
//配置登录和注销

View File

@ -102,6 +102,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>2.6</version>
<configuration>
<nonFilteredFileExtensions>
<nonFilteredFileExtension>ttf</nonFilteredFileExtension>

View File

@ -3,8 +3,8 @@ package com.xxl.job.admin.controller.interceptor;
import com.xxl.job.admin.core.util.FtlUtil;
import com.xxl.job.admin.core.util.I18nUtil;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.AsyncHandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
@ -17,7 +17,7 @@ import java.util.HashMap;
* @author xuxueli 2015-12-12 18:09:04
*/
@Component
public class CookieInterceptor extends HandlerInterceptorAdapter {
public class CookieInterceptor implements AsyncHandlerInterceptor {
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
@ -36,8 +36,8 @@ public class CookieInterceptor extends HandlerInterceptorAdapter {
if (modelAndView != null) {
modelAndView.addObject("I18nUtil", FtlUtil.generateStaticModel(I18nUtil.class.getName()));
}
super.postHandle(request, response, handler, modelAndView);
AsyncHandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
}

View File

@ -6,7 +6,7 @@ import com.xxl.job.admin.core.util.I18nUtil;
import com.xxl.job.admin.service.LoginService;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import org.springframework.web.servlet.AsyncHandlerInterceptor;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
@ -18,16 +18,16 @@ import javax.servlet.http.HttpServletResponse;
* @author xuxueli 2015-12-12 18:09:04
*/
@Component
public class PermissionInterceptor extends HandlerInterceptorAdapter {
public class PermissionInterceptor implements AsyncHandlerInterceptor {
@Resource
private LoginService loginService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (!(handler instanceof HandlerMethod)) {
return super.preHandle(request, response, handler);
return AsyncHandlerInterceptor.super.preHandle(request, response, handler);
}
// if need login
@ -53,7 +53,7 @@ public class PermissionInterceptor extends HandlerInterceptorAdapter {
request.setAttribute(LoginService.LOGIN_IDENTITY_KEY, loginUser);
}
return super.preHandle(request, response, handler);
return AsyncHandlerInterceptor.super.preHandle(request, response, handler);
}
}

View File

@ -46,23 +46,33 @@
<artifactId>druid-spring-boot-starter</artifactId>
</dependency>
<!-- dynamic-datasource 多数据源-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
</dependency>
<!-- sql性能分析插件 -->
<dependency>
<groupId>p6spy</groupId>
<artifactId>p6spy</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
</dependency>
<!-- 系统模块-->
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-common</artifactId>
</dependency>
<dependency>
<groupId>com.yomahub</groupId>
<artifactId>tlog-web-spring-boot-starter</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -12,15 +12,15 @@ import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* 数据过滤处理
*
* @author Lion Li
* @deprecated 3.6.0 移除 {@link com.ruoyi.framework.handler.PlusDataPermissionHandler}
*/
@Aspect
@Component
@Deprecated
public class DataScopeAspect {
/**
@ -131,9 +131,6 @@ public class DataScopeAspect {
if (params instanceof BaseEntity) {
BaseEntity baseEntity = (BaseEntity) params;
baseEntity.getParams().put(DATA_SCOPE, sql);
} else {
Map<String, Object> invoke = ReflectUtils.invokeGetter(params, "params");
invoke.put(DATA_SCOPE, sql);
}
}
}

View File

@ -18,10 +18,12 @@ import java.util.Objects;
* 多数据源处理
*
* @author Lion Li
* @deprecated 3.6.0 移除 使用原生方法处理 功能更全
*/
@Aspect
@Order(-500)
@Component
@Deprecated
public class DataSourceAspect {
@Pointcut("@annotation(com.ruoyi.common.annotation.DataSource)"

View File

@ -54,7 +54,7 @@ public class RateLimiterAspect {
stringBuffer.append(ServletUtils.getClientIP()).append("-");
} else if (rateLimiter.limitType() == LimitType.CLUSTER){
// 获取客户端实例id
stringBuffer.append(RedisUtils.getClientId()).append("-");
stringBuffer.append(RedisUtils.getClient().getId()).append("-");
}
MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod();

View File

@ -1,6 +1,9 @@
package com.ruoyi.framework.config;
import cn.hutool.core.net.NetUtil;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator;
import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector;
import com.baomidou.mybatisplus.core.injector.ISqlInjector;
@ -10,6 +13,7 @@ import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInt
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.ruoyi.common.core.mybatisplus.methods.InsertAll;
import com.ruoyi.framework.handler.CreateAndUpdateMetaObjectHandler;
import com.ruoyi.framework.interceptor.PlusDataPermissionInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@ -30,6 +34,8 @@ public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 数据权限处理
interceptor.addInnerInterceptor(dataPermissionInterceptor());
// 分页插件
interceptor.addInnerInterceptor(paginationInnerInterceptor());
// 乐观锁插件
@ -37,6 +43,13 @@ public class MybatisPlusConfig {
return interceptor;
}
/**
* 数据权限拦截器
*/
public PlusDataPermissionInterceptor dataPermissionInterceptor() {
return new PlusDataPermissionInterceptor();
}
/**
* 分页插件自动识别数据库类型
*/
@ -79,24 +92,33 @@ public class MybatisPlusConfig {
};
}
/**
* 使用网卡信息绑定雪花生成器
* 防止集群雪花ID重复
*/
@Bean
public IdentifierGenerator idGenerator() {
return new DefaultIdentifierGenerator(NetUtil.getLocalhost());
}
/**
* PaginationInnerInterceptor 分页插件自动识别数据库类型
* https://baomidou.com/guide/interceptor-pagination.html
* https://baomidou.com/pages/97710a/
* OptimisticLockerInnerInterceptor 乐观锁插件
* https://baomidou.com/guide/interceptor-optimistic-locker.html
* https://baomidou.com/pages/0d93c0/
* MetaObjectHandler 元对象字段填充控制器
* https://baomidou.com/guide/auto-fill-metainfo.html
* https://baomidou.com/pages/4c6bcf/
* ISqlInjector sql注入器
* https://baomidou.com/guide/sql-injector.html
* https://baomidou.com/pages/42ea4a/
* BlockAttackInnerInterceptor 如果是对全表的删除或更新操作就会终止该操作
* https://baomidou.com/guide/interceptor-block-attack.html
* https://baomidou.com/pages/f9a237/
* IllegalSQLInnerInterceptor sql性能规范插件(垃圾SQL拦截)
* IdentifierGenerator 自定义主键策略
* https://baomidou.com/guide/id-generator.html
* https://baomidou.com/pages/568eb2/
* TenantLineInnerInterceptor 多租户插件
* https://baomidou.com/guide/interceptor-tenant-line.html
* https://baomidou.com/pages/aef2f2/
* DynamicTableNameInnerInterceptor 动态表名插件
* https://baomidou.com/guide/interceptor-dynamic-table-name.html
* https://baomidou.com/pages/2a45ff/
*/
}

View File

@ -23,6 +23,7 @@ import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* redis配置
@ -34,158 +35,164 @@ import java.util.Map;
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {
private static final String REDIS_PROTOCOL_PREFIX = "redis://";
private static final String REDISS_PROTOCOL_PREFIX = "rediss://";
private static final String REDIS_PROTOCOL_PREFIX = "redis://";
private static final String REDISS_PROTOCOL_PREFIX = "rediss://";
@Autowired
private RedisProperties redisProperties;
@Autowired
private RedisProperties redisProperties;
@Autowired
private RedissonProperties redissonProperties;
@Autowired
private RedissonProperties redissonProperties;
@Bean(destroyMethod = "shutdown")
@ConditionalOnMissingBean(RedissonClient.class)
public RedissonClient redisson() throws IOException {
String prefix = REDIS_PROTOCOL_PREFIX;
if (redisProperties.isSsl()) {
prefix = REDISS_PROTOCOL_PREFIX;
}
Config config = new Config();
config.setThreads(redissonProperties.getThreads())
.setNettyThreads(redissonProperties.getNettyThreads())
.setCodec(JsonJacksonCodec.INSTANCE)
.setTransportMode(redissonProperties.getTransportMode());
@Bean(destroyMethod = "shutdown")
@ConditionalOnMissingBean(RedissonClient.class)
public RedissonClient redisson() throws IOException {
String prefix = REDIS_PROTOCOL_PREFIX;
if (redisProperties.isSsl()) {
prefix = REDISS_PROTOCOL_PREFIX;
}
Config config = new Config();
config.setThreads(redissonProperties.getThreads())
.setNettyThreads(redissonProperties.getNettyThreads())
.setCodec(JsonJacksonCodec.INSTANCE)
.setTransportMode(redissonProperties.getTransportMode());
RedissonProperties.SingleServerConfig singleServerConfig = redissonProperties.getSingleServerConfig();
if (ObjectUtil.isNotNull(singleServerConfig)) {
// 使用单机模式
config.useSingleServer()
.setAddress(prefix + redisProperties.getHost() + ":" + redisProperties.getPort())
.setConnectTimeout(((Long) redisProperties.getTimeout().toMillis()).intValue())
.setDatabase(redisProperties.getDatabase())
.setPassword(StringUtils.isNotBlank(redisProperties.getPassword()) ? redisProperties.getPassword() : null)
.setTimeout(singleServerConfig.getTimeout())
.setRetryAttempts(singleServerConfig.getRetryAttempts())
.setRetryInterval(singleServerConfig.getRetryInterval())
.setSubscriptionsPerConnection(singleServerConfig.getSubscriptionsPerConnection())
.setClientName(singleServerConfig.getClientName())
.setIdleConnectionTimeout(singleServerConfig.getIdleConnectionTimeout())
.setSubscriptionConnectionMinimumIdleSize(singleServerConfig.getSubscriptionConnectionMinimumIdleSize())
.setSubscriptionConnectionPoolSize(singleServerConfig.getSubscriptionConnectionPoolSize())
.setConnectionMinimumIdleSize(singleServerConfig.getConnectionMinimumIdleSize())
.setConnectionPoolSize(singleServerConfig.getConnectionPoolSize())
.setDnsMonitoringInterval(singleServerConfig.getDnsMonitoringInterval());
}
// 集群配置方式 参考下方注释
RedissonProperties.ClusterServersConfig clusterServersConfig = redissonProperties.getClusterServersConfig();
if (ObjectUtil.isNotNull(clusterServersConfig)) {
// 使用集群模式
config.useClusterServers()
.setConnectTimeout(((Long) redisProperties.getTimeout().toMillis()).intValue())
.setPassword(StringUtils.isNotBlank(redisProperties.getPassword()) ? redisProperties.getPassword() : null)
.setTimeout(clusterServersConfig.getTimeout())
.setRetryAttempts(clusterServersConfig.getRetryAttempts())
.setRetryInterval(clusterServersConfig.getRetryInterval())
.setSubscriptionsPerConnection(clusterServersConfig.getSubscriptionsPerConnection())
.setClientName(clusterServersConfig.getClientName())
.setIdleConnectionTimeout(clusterServersConfig.getIdleConnectionTimeout())
.setPingConnectionInterval(clusterServersConfig.getPingConnectionInterval())
.setSubscriptionConnectionMinimumIdleSize(clusterServersConfig.getSubscriptionConnectionMinimumIdleSize())
.setSubscriptionConnectionPoolSize(clusterServersConfig.getSubscriptionConnectionPoolSize())
.setMasterConnectionMinimumIdleSize(clusterServersConfig.getMasterConnectionMinimumIdleSize())
.setMasterConnectionPoolSize(clusterServersConfig.getMasterConnectionPoolSize())
.setSlaveConnectionMinimumIdleSize(clusterServersConfig.getSlaveConnectionMinimumIdleSize())
.setSlaveConnectionPoolSize(clusterServersConfig.getSlaveConnectionPoolSize())
.setDnsMonitoringInterval(clusterServersConfig.getDnsMonitoringInterval())
.setFailedSlaveReconnectionInterval(clusterServersConfig.getFailedSlaveReconnectionInterval())
.setScanInterval(clusterServersConfig.getScanInterval())
.setReadMode(clusterServersConfig.getReadMode())
.setSubscriptionMode(clusterServersConfig.getSubscriptionMode())
.setNodeAddresses(redisProperties.getCluster().getNodes());
}
RedissonClient redissonClient = Redisson.create(config);
log.info("初始化 redis 配置");
return redissonClient;
}
RedissonProperties.SingleServerConfig singleServerConfig = redissonProperties.getSingleServerConfig();
if (ObjectUtil.isNotNull(singleServerConfig)) {
// 使用单机模式
config.useSingleServer()
.setAddress(prefix + redisProperties.getHost() + ":" + redisProperties.getPort())
.setConnectTimeout(((Long) redisProperties.getTimeout().toMillis()).intValue())
.setDatabase(redisProperties.getDatabase())
.setPassword(StringUtils.isNotBlank(redisProperties.getPassword()) ? redisProperties.getPassword() : null)
.setTimeout(singleServerConfig.getTimeout())
.setRetryAttempts(singleServerConfig.getRetryAttempts())
.setRetryInterval(singleServerConfig.getRetryInterval())
.setSubscriptionsPerConnection(singleServerConfig.getSubscriptionsPerConnection())
.setClientName(singleServerConfig.getClientName())
.setIdleConnectionTimeout(singleServerConfig.getIdleConnectionTimeout())
.setSubscriptionConnectionMinimumIdleSize(singleServerConfig.getSubscriptionConnectionMinimumIdleSize())
.setSubscriptionConnectionPoolSize(singleServerConfig.getSubscriptionConnectionPoolSize())
.setConnectionMinimumIdleSize(singleServerConfig.getConnectionMinimumIdleSize())
.setConnectionPoolSize(singleServerConfig.getConnectionPoolSize())
.setDnsMonitoringInterval(singleServerConfig.getDnsMonitoringInterval());
}
// 集群配置方式 参考下方注释
RedissonProperties.ClusterServersConfig clusterServersConfig = redissonProperties.getClusterServersConfig();
if (ObjectUtil.isNotNull(clusterServersConfig)) {
// 使用集群模式
String finalPrefix = prefix;
List<String> nodes = redisProperties.getCluster().getNodes()
.stream()
.map(node -> finalPrefix + node)
.collect(Collectors.toList());
/**
* 整合spring-cache
*/
@Bean
public CacheManager cacheManager(RedissonClient redissonClient) {
List<RedissonProperties.CacheGroup> cacheGroup = redissonProperties.getCacheGroup();
Map<String, CacheConfig> config = new HashMap<>();
for (RedissonProperties.CacheGroup group : cacheGroup) {
CacheConfig cacheConfig = new CacheConfig(group.getTtl(), group.getMaxIdleTime());
cacheConfig.setMaxSize(group.getMaxSize());
config.put(group.getGroupId(), cacheConfig);
}
return new RedissonSpringCacheManager(redissonClient, config, JsonJacksonCodec.INSTANCE);
}
config.useClusterServers()
.setConnectTimeout(((Long) redisProperties.getTimeout().toMillis()).intValue())
.setPassword(StringUtils.isNotBlank(redisProperties.getPassword()) ? redisProperties.getPassword() : null)
.setTimeout(clusterServersConfig.getTimeout())
.setRetryAttempts(clusterServersConfig.getRetryAttempts())
.setRetryInterval(clusterServersConfig.getRetryInterval())
.setSubscriptionsPerConnection(clusterServersConfig.getSubscriptionsPerConnection())
.setClientName(clusterServersConfig.getClientName())
.setIdleConnectionTimeout(clusterServersConfig.getIdleConnectionTimeout())
.setPingConnectionInterval(clusterServersConfig.getPingConnectionInterval())
.setSubscriptionConnectionMinimumIdleSize(clusterServersConfig.getSubscriptionConnectionMinimumIdleSize())
.setSubscriptionConnectionPoolSize(clusterServersConfig.getSubscriptionConnectionPoolSize())
.setMasterConnectionMinimumIdleSize(clusterServersConfig.getMasterConnectionMinimumIdleSize())
.setMasterConnectionPoolSize(clusterServersConfig.getMasterConnectionPoolSize())
.setSlaveConnectionMinimumIdleSize(clusterServersConfig.getSlaveConnectionMinimumIdleSize())
.setSlaveConnectionPoolSize(clusterServersConfig.getSlaveConnectionPoolSize())
.setDnsMonitoringInterval(clusterServersConfig.getDnsMonitoringInterval())
.setFailedSlaveReconnectionInterval(clusterServersConfig.getFailedSlaveReconnectionInterval())
.setScanInterval(clusterServersConfig.getScanInterval())
.setReadMode(clusterServersConfig.getReadMode())
.setSubscriptionMode(clusterServersConfig.getSubscriptionMode())
.setNodeAddresses(nodes);
}
RedissonClient redissonClient = Redisson.create(config);
log.info("初始化 redis 配置");
return redissonClient;
}
/**
* redis集群配置 yml
*
* --- # redis 集群配置(单机与集群只能开启一个另一个需要注释掉)
* spring:
* redis:
* cluster:
* nodes:
* - 192.168.0.100:6379
* - 192.168.0.101:6379
* - 192.168.0.102:6379
* # 密码
* password:
* # 连接超时时间
* timeout: 10s
* # 是否开启ssl
* ssl: false
*
* redisson:
* # 线程池数量
* threads: 16
* # Netty线程池数量
* nettyThreads: 32
* # 传输模式
* transportMode: "NIO"
* # 集群配置
* clusterServersConfig:
* # 客户端名称
* clientName: ${ruoyi.name}
* # master最小空闲连接数
* masterConnectionMinimumIdleSize: 32
* # master连接池大小
* masterConnectionPoolSize: 64
* # slave最小空闲连接数
* slaveConnectionMinimumIdleSize: 32
* # slave连接池大小
* slaveConnectionPoolSize: 64
* # 连接空闲超时单位毫秒
* idleConnectionTimeout: 10000
* # ping连接间隔
* pingConnectionInterval: 1000
* # 命令等待超时单位毫秒
* timeout: 3000
* # 如果尝试在此限制之内发送成功则开始启用 timeout 计时
* retryAttempts: 3
* # 命令重试发送时间间隔单位毫秒
* retryInterval: 1500
* # 从可用服务器的内部列表中排除 Redis Slave 重新连接尝试的间隔
* failedSlaveReconnectionInterval: 3000
* # 发布和订阅连接池最小空闲连接数
* subscriptionConnectionMinimumIdleSize: 1
* # 发布和订阅连接池大小
* subscriptionConnectionPoolSize: 50
* # 单个连接最大订阅数量
* subscriptionsPerConnection: 5
* # 扫描间隔
* scanInterval: 1000
* # DNS监测时间间隔单位毫秒
* dnsMonitoringInterval: 5000
* # 读取模式
* readMode: "SLAVE"
* # 订阅模式
* subscriptionMode: "MASTER"
*/
/**
* 整合spring-cache
*/
@Bean
public CacheManager cacheManager(RedissonClient redissonClient) {
List<RedissonProperties.CacheGroup> cacheGroup = redissonProperties.getCacheGroup();
Map<String, CacheConfig> config = new HashMap<>();
for (RedissonProperties.CacheGroup group : cacheGroup) {
CacheConfig cacheConfig = new CacheConfig(group.getTtl(), group.getMaxIdleTime());
cacheConfig.setMaxSize(group.getMaxSize());
config.put(group.getGroupId(), cacheConfig);
}
return new RedissonSpringCacheManager(redissonClient, config, JsonJacksonCodec.INSTANCE);
}
/**
* redis集群配置 yml
*
* --- # redis 集群配置(单机与集群只能开启一个另一个需要注释掉)
* spring:
* redis:
* cluster:
* nodes:
* - 192.168.0.100:6379
* - 192.168.0.101:6379
* - 192.168.0.102:6379
* # 密码
* password:
* # 连接超时时间
* timeout: 10s
* # 是否开启ssl
* ssl: false
*
* redisson:
* # 线程池数量
* threads: 16
* # Netty线程池数量
* nettyThreads: 32
* # 传输模式
* transportMode: "NIO"
* # 集群配置
* clusterServersConfig:
* # 客户端名称
* clientName: ${ruoyi.name}
* # master最小空闲连接数
* masterConnectionMinimumIdleSize: 32
* # master连接池大小
* masterConnectionPoolSize: 64
* # slave最小空闲连接数
* slaveConnectionMinimumIdleSize: 32
* # slave连接池大小
* slaveConnectionPoolSize: 64
* # 连接空闲超时单位毫秒
* idleConnectionTimeout: 10000
* # ping连接间隔
* pingConnectionInterval: 1000
* # 命令等待超时单位毫秒
* timeout: 3000
* # 如果尝试在此限制之内发送成功则开始启用 timeout 计时
* retryAttempts: 3
* # 命令重试发送时间间隔单位毫秒
* retryInterval: 1500
* # 从可用服务器的内部列表中排除 Redis Slave 重新连接尝试的间隔
* failedSlaveReconnectionInterval: 3000
* # 发布和订阅连接池最小空闲连接数
* subscriptionConnectionMinimumIdleSize: 1
* # 发布和订阅连接池大小
* subscriptionConnectionPoolSize: 50
* # 单个连接最大订阅数量
* subscriptionsPerConnection: 5
* # 扫描间隔
* scanInterval: 1000
* # DNS监测时间间隔单位毫秒
* dnsMonitoringInterval: 5000
* # 读取模式
* readMode: "SLAVE"
* # 订阅模式
* subscriptionMode: "MASTER"
*/
}

View File

@ -1,6 +1,6 @@
package com.ruoyi.framework.config;
import com.ruoyi.framework.Interceptor.PlusWebInvokeTimeInterceptor;
import com.ruoyi.framework.interceptor.PlusWebInvokeTimeInterceptor;
import com.yomahub.tlog.web.interceptor.TLogWebInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

View File

@ -1,13 +1,8 @@
package com.ruoyi.framework.config;
import com.yomahub.tlog.core.aop.AspectLogAop;
import com.yomahub.tlog.spring.TLogPropertyInit;
import com.yomahub.tlog.spring.TLogSpringAware;
import com.yomahub.tlog.springboot.property.TLogProperty;
import org.springframework.context.annotation.Bean;
import com.yomahub.tlog.springboot.TLogWebAutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.annotation.Order;
/**
* 整合 TLog 框架配置
@ -15,29 +10,9 @@ import org.springframework.core.annotation.Order;
* @author Lion Li
* @since 3.3.0
*/
@Order(-999)
@Configuration
@Import(TLogProperty.class)
// 排除 web 自动配置 自定义实现
@EnableAutoConfiguration(exclude = TLogWebAutoConfiguration.class)
public class TLogConfig {
@Bean
public TLogPropertyInit tLogPropertyInit(TLogProperty tLogProperty) {
TLogPropertyInit tLogPropertyInit = new TLogPropertyInit();
tLogPropertyInit.setPattern(tLogProperty.getPattern());
tLogPropertyInit.setEnableInvokeTimePrint(tLogProperty.enableInvokeTimePrint());
tLogPropertyInit.setIdGenerator(tLogProperty.getIdGenerator());
tLogPropertyInit.setMdcEnable(tLogProperty.getMdcEnable());
return tLogPropertyInit;
}
@Bean
public TLogSpringAware tLogSpringAware(){
return new TLogSpringAware();
}
@Bean
public AspectLogAop aspectLogAop() {
return new AspectLogAop();
}
}

View File

@ -0,0 +1,192 @@
package com.ruoyi.framework.handler;
import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.collection.ConcurrentHashSet;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ObjectUtil;
import com.ruoyi.common.annotation.DataColumn;
import com.ruoyi.common.annotation.DataPermission;
import com.ruoyi.common.core.domain.entity.SysRole;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.service.UserService;
import com.ruoyi.common.enums.DataScopeType;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.helper.DataPermissionHelper;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.Parenthesis;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.expression.BeanResolver;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.ParserContext;
import org.springframework.expression.common.TemplateParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
/**
* 数据权限过滤
*
* @author Lion Li
* @version 3.5.0
*/
@Slf4j
public class PlusDataPermissionHandler {
/**
* 方法或类(名称) 注解的映射关系缓存
*/
private final Map<String, DataPermission> dataPermissionCacheMap = new ConcurrentHashMap<>();
/**
* 无效注解方法缓存用于快速返回
*/
private final Set<String> inavlidCacheSet = new ConcurrentHashSet<>();
/**
* spel 解析器
*/
private final ExpressionParser parser = new SpelExpressionParser();
private final ParserContext parserContext = new TemplateParserContext();
/**
* bean解析器 用于处理 spel 表达式中对 bean 的调用
*/
private final BeanResolver beanResolver = new BeanFactoryResolver(SpringUtils.getBeanFactory());
public Expression getSqlSegment(Expression where, String mappedStatementId, boolean isSelect) {
DataColumn[] dataColumns = findAnnotation(mappedStatementId);
if (ArrayUtil.isEmpty(dataColumns)) {
inavlidCacheSet.add(mappedStatementId);
return where;
}
SysUser currentUser = DataPermissionHelper.getVariable("user");
if (ObjectUtil.isNull(currentUser)) {
currentUser = SpringUtils.getBean(UserService.class).selectUserById(SecurityUtils.getUserId());
DataPermissionHelper.setVariable("user", currentUser);
}
// 如果是超级管理员则不过滤数据
if (ObjectUtil.isNull(currentUser) || currentUser.isAdmin()) {
return where;
}
String dataFilterSql = buildDataFilter(dataColumns, isSelect);
if (StringUtils.isBlank(dataFilterSql)) {
return where;
}
try {
Expression expression = CCJSqlParserUtil.parseExpression(dataFilterSql);
// 数据权限使用单独的括号 防止与其他条件冲突
Parenthesis parenthesis = new Parenthesis(expression);
if (ObjectUtil.isNotNull(where)) {
return new AndExpression(where, parenthesis);
} else {
return parenthesis;
}
} catch (JSQLParserException e) {
throw new ServiceException("数据权限解析异常 => " + e.getMessage());
}
}
/**
* 构造数据过滤sql
*/
private String buildDataFilter(DataColumn[] dataColumns, boolean isSelect) {
StringBuilder sqlString = new StringBuilder();
// 更新或删除需满足所有条件
String joinStr = isSelect ? " OR " : " AND ";
SysUser user = DataPermissionHelper.getVariable("user");
StandardEvaluationContext context = new StandardEvaluationContext();
context.setBeanResolver(beanResolver);
DataPermissionHelper.getContext().forEach(context::setVariable);
for (SysRole role : user.getRoles()) {
user.setRoleId(role.getRoleId());
// 获取角色权限泛型
DataScopeType type = DataScopeType.findCode(role.getDataScope());
if (ObjectUtil.isNull(type)) {
throw new ServiceException("角色数据范围异常 => " + role.getDataScope());
}
// 全部数据权限直接返回
if (type == DataScopeType.ALL) {
return "";
}
boolean isSuccess = false;
for (DataColumn dataColumn : dataColumns) {
// 不包含 key 变量 则不处理
if (!StringUtils.contains(type.getSqlTemplate(), "#" + dataColumn.key())) {
continue;
}
// 设置注解变量 key 为表达式变量 value 为变量值
context.setVariable(dataColumn.key(), dataColumn.value());
// 解析sql模板并填充
String sql = parser.parseExpression(type.getSqlTemplate(), parserContext).getValue(context, String.class);
sqlString.append(joinStr).append(sql);
isSuccess = true;
}
// 未处理成功则填充兜底方案
if (!isSuccess) {
sqlString.append(joinStr).append(type.getElseSql());
}
}
if (StringUtils.isNotBlank(sqlString.toString())) {
return sqlString.substring(joinStr.length());
}
return "";
}
private DataColumn[] findAnnotation(String mappedStatementId) {
StringBuilder sb = new StringBuilder(mappedStatementId);
int index = sb.lastIndexOf(".");
String clazzName = sb.substring(0, index);
String methodName = sb.substring(index + 1, sb.length());
Class<?> clazz = ClassUtil.loadClass(clazzName);
List<Method> methods = Arrays.stream(ClassUtil.getDeclaredMethods(clazz))
.filter(method -> method.getName().equals(methodName)).collect(Collectors.toList());
DataPermission dataPermission;
// 获取方法注解
for (Method method : methods) {
dataPermission = dataPermissionCacheMap.get(method.getName());
if (ObjectUtil.isNotNull(dataPermission)) {
return dataPermission.value();
}
if (AnnotationUtil.hasAnnotation(method, DataPermission.class)) {
dataPermission = AnnotationUtil.getAnnotation(method, DataPermission.class);
dataPermissionCacheMap.put(method.getName(), dataPermission);
return dataPermission.value();
}
}
dataPermission = dataPermissionCacheMap.get(clazz.getName());
if (ObjectUtil.isNotNull(dataPermission)) {
return dataPermission.value();
}
// 获取类注解
if (AnnotationUtil.hasAnnotation(clazz, DataPermission.class)) {
dataPermission = AnnotationUtil.getAnnotation(clazz, DataPermission.class);
dataPermissionCacheMap.put(clazz.getName(), dataPermission);
return dataPermission.value();
}
return null;
}
/**
* 是否为无效方法 无数据权限
*/
public boolean isInvalid(String mappedStatementId) {
return inavlidCacheSet.contains(mappedStatementId);
}
}

View File

@ -0,0 +1,108 @@
package com.ruoyi.framework.interceptor;
import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper;
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.baomidou.mybatisplus.extension.parser.JsqlParserSupport;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import com.ruoyi.framework.handler.PlusDataPermissionHandler;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.statement.delete.Delete;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.Select;
import net.sf.jsqlparser.statement.select.SelectBody;
import net.sf.jsqlparser.statement.select.SetOperationList;
import net.sf.jsqlparser.statement.update.Update;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
/**
* 数据权限拦截器
*
* @author Lion Li
* @version 3.5.0
*/
public class PlusDataPermissionInterceptor extends JsqlParserSupport implements InnerInterceptor {
private final PlusDataPermissionHandler dataPermissionHandler = new PlusDataPermissionHandler();
@Override
public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
// 检查忽略注解
if (InterceptorIgnoreHelper.willIgnoreDataPermission(ms.getId())) {
return;
}
// 检查是否无效 无数据权限注解
if (dataPermissionHandler.isInvalid(ms.getId())) {
return;
}
// 解析 sql 分配对应方法
PluginUtils.MPBoundSql mpBs = PluginUtils.mpBoundSql(boundSql);
mpBs.sql(parserSingle(mpBs.sql(), ms.getId()));
}
@Override
public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {
PluginUtils.MPStatementHandler mpSh = PluginUtils.mpStatementHandler(sh);
MappedStatement ms = mpSh.mappedStatement();
SqlCommandType sct = ms.getSqlCommandType();
if (sct == SqlCommandType.UPDATE || sct == SqlCommandType.DELETE) {
if (InterceptorIgnoreHelper.willIgnoreDataPermission(ms.getId())) {
return;
}
PluginUtils.MPBoundSql mpBs = mpSh.mPBoundSql();
mpBs.sql(parserMulti(mpBs.sql(), ms.getId()));
}
}
@Override
protected void processSelect(Select select, int index, String sql, Object obj) {
SelectBody selectBody = select.getSelectBody();
if (selectBody instanceof PlainSelect) {
this.setWhere((PlainSelect) selectBody, (String) obj);
} else if (selectBody instanceof SetOperationList) {
SetOperationList setOperationList = (SetOperationList) selectBody;
List<SelectBody> selectBodyList = setOperationList.getSelects();
selectBodyList.forEach(s -> this.setWhere((PlainSelect) s, (String) obj));
}
}
@Override
protected void processUpdate(Update update, int index, String sql, Object obj) {
Expression sqlSegment = dataPermissionHandler.getSqlSegment(update.getWhere(), (String) obj, false);
if (null != sqlSegment) {
update.setWhere(sqlSegment);
}
}
@Override
protected void processDelete(Delete delete, int index, String sql, Object obj) {
Expression sqlSegment = dataPermissionHandler.getSqlSegment(delete.getWhere(), (String) obj, false);
if (null != sqlSegment) {
delete.setWhere(sqlSegment);
}
}
/**
* 设置 where 条件
*
* @param plainSelect 查询对象
* @param mappedStatementId 执行方法id
*/
protected void setWhere(PlainSelect plainSelect, String mappedStatementId) {
Expression sqlSegment = dataPermissionHandler.getSqlSegment(plainSelect.getWhere(), mappedStatementId, true);
if (null != sqlSegment) {
plainSelect.setWhere(sqlSegment);
}
}
}

View File

@ -1,48 +1,55 @@
package com.ruoyi.framework.Interceptor;
package com.ruoyi.framework.interceptor;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.map.MapUtil;
import com.alibaba.ttl.TransmittableThreadLocal;
import com.ruoyi.common.filter.RepeatedlyRequestWrapper;
import com.ruoyi.common.utils.JsonUtils;
import com.ruoyi.common.utils.StringUtils;
import com.yomahub.tlog.context.TLogContext;
import com.yomahub.tlog.web.interceptor.AbsTLogWebHandlerMethodInterceptor;
import com.yomahub.tlog.web.wrapper.RequestWrapper;
import com.ruoyi.common.utils.spring.SpringUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.StopWatch;
import org.springframework.http.MediaType;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.util.Map;
/**
* 重写Tlog web的调用时间统计拦截器
* web的调用时间统计拦截器
* dev环境有效
*
* @author Lion Li
* @since 3.3.0
*/
@Slf4j
public class PlusWebInvokeTimeInterceptor extends AbsTLogWebHandlerMethodInterceptor {
public class PlusWebInvokeTimeInterceptor implements HandlerInterceptor {
private final TransmittableThreadLocal<StopWatch> invokeTimeTL = new TransmittableThreadLocal<>();
@Override
public boolean preHandleByHandlerMethod(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (TLogContext.enableInvokeTimePrint()) {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (!"prod".equals(SpringUtils.getActiveProfile())) {
String url = request.getMethod() + " " + request.getRequestURI();
// 打印请求参数
if (isJsonRequest(request)) {
String jsonParam = new RequestWrapper(request).getBodyString();
log.info("[PLUS]开始请求 => URL[{}],参数类型[json],参数:[{}]", url, jsonParam);
String jsonParam = "";
if (request instanceof RepeatedlyRequestWrapper) {
BufferedReader reader = request.getReader();
jsonParam = IoUtil.read(reader);
}
log.debug("[PLUS]开始请求 => URL[{}],参数类型[json],参数:[{}]", url, jsonParam);
} else {
Map<String, String[]> parameterMap = request.getParameterMap();
if (MapUtil.isNotEmpty(parameterMap)) {
String parameters = JsonUtils.toJsonString(parameterMap);
log.info("[PLUS]开始请求 => URL[{}],参数类型[param],参数:[{}]", url, parameters);
log.debug("[PLUS]开始请求 => URL[{}],参数类型[param],参数:[{}]", url, parameters);
} else {
log.info("[PLUS]开始请求 => URL[{}],无参数", url);
log.debug("[PLUS]开始请求 => URL[{}],无参数", url);
}
}
@ -54,16 +61,16 @@ public class PlusWebInvokeTimeInterceptor extends AbsTLogWebHandlerMethodInterce
}
@Override
public void postHandleByHandlerMethod(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletionByHandlerMethod(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
if (TLogContext.enableInvokeTimePrint()) {
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
if (!"prod".equals(SpringUtils.getActiveProfile())) {
StopWatch stopWatch = invokeTimeTL.get();
stopWatch.stop();
log.info("[PLUS]结束请求 => URL[{}],耗时:[{}]毫秒", request.getMethod() + " " + request.getRequestURI(), stopWatch.getTime());
log.debug("[PLUS]结束请求 => URL[{}],耗时:[{}]毫秒", request.getMethod() + " " + request.getRequestURI(), stopWatch.getTime());
invokeTimeTL.remove();
}
}

View File

@ -6,6 +6,7 @@ import cn.hutool.core.io.IoUtil;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.PageQuery;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.generator.domain.GenTable;
@ -46,8 +47,8 @@ public class GenController extends BaseController {
@ApiOperation("查询代码生成列表")
@SaCheckPermission("tool:gen:list")
@GetMapping("/list")
public TableDataInfo<GenTable> genList(GenTable genTable) {
return genTableService.selectPageGenTableList(genTable);
public TableDataInfo<GenTable> genList(GenTable genTable, PageQuery pageQuery) {
return genTableService.selectPageGenTableList(genTable, pageQuery);
}
/**
@ -73,8 +74,8 @@ public class GenController extends BaseController {
@ApiOperation("查询数据库列表")
@SaCheckPermission("tool:gen:list")
@GetMapping("/db/list")
public TableDataInfo<GenTable> dataList(GenTable genTable) {
return genTableService.selectPageDbTableList(genTable);
public TableDataInfo<GenTable> dataList(GenTable genTable, PageQuery pageQuery) {
return genTableService.selectPageDbTableList(genTable, pageQuery);
}
/**

View File

@ -1,5 +1,6 @@
package com.ruoyi.generator.mapper;
import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
import com.ruoyi.common.core.mybatisplus.core.BaseMapperPlus;
import com.ruoyi.generator.domain.GenTableColumn;
@ -10,6 +11,7 @@ import java.util.List;
*
* @author Lion Li
*/
@InterceptorIgnore(dataPermission = "true")
public interface GenTableColumnMapper extends BaseMapperPlus<GenTableColumn> {
/**
* 根据表名称查询列信息

View File

@ -1,5 +1,6 @@
package com.ruoyi.generator.mapper;
import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.common.core.mybatisplus.core.BaseMapperPlus;
import com.ruoyi.generator.domain.GenTable;
@ -12,6 +13,7 @@ import java.util.List;
*
* @author Lion Li
*/
@InterceptorIgnore(dataPermission = "true")
public interface GenTableMapper extends BaseMapperPlus<GenTable> {

View File

@ -4,8 +4,10 @@ import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.io.IoUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.constant.GenConstants;
import com.ruoyi.common.core.domain.PageQuery;
import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.exception.ServiceException;
@ -63,13 +65,15 @@ public class GenTableServiceImpl extends ServicePlusImpl<GenTableMapper, GenTabl
}
@Override
public TableDataInfo<GenTable> selectPageGenTableList(GenTable genTable) {
return PageUtils.buildDataInfo(baseMapper.selectPageGenTableList(PageUtils.buildPage(), genTable));
public TableDataInfo<GenTable> selectPageGenTableList(GenTable genTable, PageQuery pageQuery) {
Page<GenTable> page = baseMapper.selectPageGenTableList(pageQuery.build(), genTable);
return TableDataInfo.build(page);
}
@Override
public TableDataInfo<GenTable> selectPageDbTableList(GenTable genTable) {
return PageUtils.buildDataInfo(baseMapper.selectPageDbTableList(PageUtils.buildPage(), genTable));
public TableDataInfo<GenTable> selectPageDbTableList(GenTable genTable, PageQuery pageQuery) {
Page<GenTable> page = baseMapper.selectPageDbTableList(pageQuery.build(), genTable);
return TableDataInfo.build(page);
}
/**
@ -122,7 +126,7 @@ public class GenTableServiceImpl extends ServicePlusImpl<GenTableMapper, GenTabl
* @return 结果
*/
@Override
@Transactional
@Transactional(rollbackFor = Exception.class)
public void updateGenTable(GenTable genTable) {
String options = JsonUtils.toJsonString(genTable.getParams());
genTable.setOptions(options);
@ -141,7 +145,7 @@ public class GenTableServiceImpl extends ServicePlusImpl<GenTableMapper, GenTabl
* @return 结果
*/
@Override
@Transactional
@Transactional(rollbackFor = Exception.class)
public void deleteGenTableByIds(Long[] tableIds) {
List<Long> ids = Arrays.asList(tableIds);
removeByIds(ids);
@ -154,7 +158,7 @@ public class GenTableServiceImpl extends ServicePlusImpl<GenTableMapper, GenTabl
* @param tableList 导入表列表
*/
@Override
@Transactional
@Transactional(rollbackFor = Exception.class)
public void importGenTable(List<GenTable> tableList) {
String operName = LoginUtils.getUsername();
try {
@ -268,7 +272,7 @@ public class GenTableServiceImpl extends ServicePlusImpl<GenTableMapper, GenTabl
* @param tableName 表名称
*/
@Override
@Transactional
@Transactional(rollbackFor = Exception.class)
public void synchDb(String tableName) {
GenTable table = baseMapper.selectGenTableByName(tableName);
List<GenTableColumn> tableColumns = table.getColumns();

View File

@ -1,6 +1,7 @@
package com.ruoyi.generator.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.common.core.domain.PageQuery;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.generator.domain.GenTable;
@ -15,10 +16,10 @@ import java.util.Map;
public interface IGenTableService extends IService<GenTable> {
TableDataInfo<GenTable> selectPageGenTableList(GenTable genTable);
TableDataInfo<GenTable> selectPageGenTableList(GenTable genTable, PageQuery pageQuery);
TableDataInfo<GenTable> selectPageDbTableList(GenTable genTable);
TableDataInfo<GenTable> selectPageDbTableList(GenTable genTable, PageQuery pageQuery);
/**
* 查询业务列表

View File

@ -9,10 +9,7 @@ import com.ruoyi.generator.domain.GenTable;
import com.ruoyi.generator.domain.GenTableColumn;
import org.apache.velocity.VelocityContext;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.*;
/**
* 模板处理工具类
@ -244,7 +241,7 @@ public class VelocityUtils {
*/
public static String getDicts(GenTable genTable) {
List<GenTableColumn> columns = genTable.getColumns();
List<String> dicts = new ArrayList<String>();
Set<String> dicts = new HashSet<String>();
for (GenTableColumn column : columns) {
if (!column.isSuperColumn() && StringUtils.isNotEmpty(column.getDictType()) && StringUtils.equalsAny(
column.getHtmlType(),

View File

@ -27,7 +27,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="remark" column="remark" />
<collection property="columns" javaType="java.util.List" resultMap="GenTableColumnResult" />
</resultMap>
<resultMap type="GenTableColumn" id="GenTableColumnResult">
<id property="columnId" column="column_id" />
<result property="tableId" column="table_id" />
@ -52,7 +52,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="updateBy" column="update_by" />
<result property="updateTime" column="update_time" />
</resultMap>
<sql id="selectGenTableVo">
select table_id, table_name, table_comment, sub_table_name, sub_table_fk_name, class_name, tpl_category, package_name, module_name, business_name, function_name, function_author, gen_type, gen_path, options, create_by, create_time, update_by, update_time, remark from gen_table
</sql>
@ -78,7 +78,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<select id="selectPageDbTableList" parameterType="GenTable" resultMap="GenTableResult">
select table_name, table_comment, create_time, update_time from information_schema.tables
where table_schema = (select database())
AND table_name NOT LIKE 'qrtz_%' AND table_name NOT LIKE 'gen_%'
AND table_name NOT LIKE 'xxl_job_%' AND table_name NOT LIKE 'gen_%'
AND table_name NOT IN (select table_name from gen_table)
<if test="genTable.tableName != null and genTable.tableName != ''">
AND lower(table_name) like lower(concat('%', #{genTable.tableName}, '%'))
@ -117,7 +117,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<select id="selectDbTableList" parameterType="GenTable" resultMap="GenTableResult">
select table_name, table_comment, create_time, update_time from information_schema.tables
where table_schema = (select database())
AND table_name NOT LIKE 'qrtz_%' AND table_name NOT LIKE 'gen_%'
AND table_name NOT LIKE 'xxl_job_%' AND table_name NOT LIKE 'gen_%'
AND table_name NOT IN (select table_name from gen_table)
<if test="tableName != null and tableName != ''">
AND lower(table_name) like lower(concat('%', #{tableName}, '%'))
@ -133,22 +133,22 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</if>
order by create_time desc
</select>
<select id="selectDbTableListByNames" resultMap="GenTableResult">
select table_name, table_comment, create_time, update_time from information_schema.tables
where table_name NOT LIKE 'qrtz_%' and table_name NOT LIKE 'gen_%' and table_schema = (select database())
where table_name NOT LIKE 'xxl_job_%' and table_name NOT LIKE 'gen_%' and table_schema = (select database())
and table_name in
<foreach collection="array" item="name" open="(" separator="," close=")">
#{name}
</foreach>
</foreach>
</select>
<select id="selectTableByName" parameterType="String" resultMap="GenTableResult">
select table_name, table_comment, create_time, update_time from information_schema.tables
where table_comment <![CDATA[ <> ]]> '' and table_schema = (select database())
and table_name = #{tableName}
</select>
<select id="selectGenTableById" parameterType="Long" resultMap="GenTableResult">
SELECT t.table_id, t.table_name, t.table_comment, t.sub_table_name, t.sub_table_fk_name, t.class_name, t.tpl_category, t.package_name, t.module_name, t.business_name, t.function_name, t.function_author, t.gen_type, t.gen_path, t.options, t.remark,
c.column_id, c.column_name, c.column_comment, c.column_type, c.java_type, c.java_field, c.is_pk, c.is_increment, c.is_required, c.is_insert, c.is_edit, c.is_list, c.is_query, c.query_type, c.html_type, c.dict_type, c.sort
@ -156,7 +156,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
LEFT JOIN gen_table_column c ON t.table_id = c.table_id
where t.table_id = #{tableId} order by c.sort
</select>
<select id="selectGenTableByName" parameterType="String" resultMap="GenTableResult">
SELECT t.table_id, t.table_name, t.table_comment, t.sub_table_name, t.sub_table_fk_name, t.class_name, t.tpl_category, t.package_name, t.module_name, t.business_name, t.function_name, t.function_author, t.gen_type, t.gen_path, t.options, t.remark,
c.column_id, c.column_name, c.column_comment, c.column_type, c.java_type, c.java_field, c.is_pk, c.is_increment, c.is_required, c.is_insert, c.is_edit, c.is_list, c.is_query, c.query_type, c.html_type, c.dict_type, c.sort
@ -164,7 +164,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
LEFT JOIN gen_table_column c ON t.table_id = c.table_id
where t.table_name = #{tableName} order by c.sort
</select>
<select id="selectGenTableAll" parameterType="String" resultMap="GenTableResult">
SELECT t.table_id, t.table_name, t.table_comment, t.sub_table_name, t.sub_table_fk_name, t.class_name, t.tpl_category, t.package_name, t.module_name, t.business_name, t.function_name, t.function_author, t.options, t.remark,
c.column_id, c.column_name, c.column_comment, c.column_type, c.java_type, c.java_field, c.is_pk, c.is_increment, c.is_required, c.is_insert, c.is_edit, c.is_list, c.is_query, c.query_type, c.html_type, c.dict_type, c.sort
@ -173,4 +173,4 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
order by c.sort
</select>
</mapper>
</mapper>

View File

@ -63,28 +63,4 @@ public class ${ClassName}Bo extends ${Entity} {
#end
#end
/**
* 分页大小
*/
@ApiModelProperty("分页大小")
private Integer pageSize;
/**
* 当前页数
*/
@ApiModelProperty("当前页数")
private Integer pageNum;
/**
* 排序列
*/
@ApiModelProperty("排序列")
private String orderByColumn;
/**
* 排序的方向desc或者asc
*/
@ApiModelProperty(value = "排序的方向", example = "asc,desc")
private String isAsc;
}

View File

@ -14,6 +14,7 @@ import org.springframework.validation.annotation.Validated;
import com.ruoyi.common.annotation.RepeatSubmit;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.PageQuery;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.validate.AddGroup;
import com.ruoyi.common.core.validate.EditGroup;
@ -53,8 +54,8 @@ public class ${ClassName}Controller extends BaseController {
@SaCheckPermission("${permissionPrefix}:list")
@GetMapping("/list")
#if($table.crud || $table.sub)
public TableDataInfo<${ClassName}Vo> list(@Validated(QueryGroup.class) ${ClassName}Bo bo) {
return i${ClassName}Service.queryPageList(bo);
public TableDataInfo<${ClassName}Vo> list(@Validated(QueryGroup.class) ${ClassName}Bo bo, PageQuery pageQuery) {
return i${ClassName}Service.queryPageList(bo, pageQuery);
}
#elseif($table.tree)
public AjaxResult<List<${ClassName}Vo>> list(@Validated(QueryGroup.class) ${ClassName}Bo bo) {

View File

@ -6,6 +6,7 @@ import ${packageName}.domain.bo.${ClassName}Bo;
import com.ruoyi.common.core.mybatisplus.core.IServicePlus;
#if($table.crud || $table.sub)
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.core.domain.PageQuery;
#end
import java.util.Collection;
@ -28,7 +29,7 @@ public interface I${ClassName}Service extends IServicePlus<${ClassName}, ${Class
/**
* 查询列表
*/
TableDataInfo<${ClassName}Vo> queryPageList(${ClassName}Bo bo);
TableDataInfo<${ClassName}Vo> queryPageList(${ClassName}Bo bo, PageQuery pageQuery);
#end
/**

View File

@ -3,12 +3,12 @@ package ${packageName}.service.impl;
import cn.hutool.core.bean.BeanUtil;
import com.ruoyi.common.utils.StringUtils;
#if($table.crud || $table.sub)
import com.ruoyi.common.utils.PageUtils;
import com.ruoyi.common.core.page.PagePlus;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.core.domain.PageQuery;
#end
import org.springframework.stereotype.Service;
import com.ruoyi.common.core.mybatisplus.core.ServicePlusImpl;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import ${packageName}.domain.bo.${ClassName}Bo;
@ -37,15 +37,17 @@ public class ${ClassName}ServiceImpl extends ServicePlusImpl<${ClassName}Mapper,
#if($table.crud || $table.sub)
@Override
public TableDataInfo<${ClassName}Vo> queryPageList(${ClassName}Bo bo) {
PagePlus<${ClassName}, ${ClassName}Vo> result = pageVo(PageUtils.buildPagePlus(), buildQueryWrapper(bo));
return PageUtils.buildDataInfo(result);
public TableDataInfo<${ClassName}Vo> queryPageList(${ClassName}Bo bo, PageQuery pageQuery) {
LambdaQueryWrapper<${ClassName}> lqw = buildQueryWrapper(bo);
Page<${ClassName}Vo> result = pageVo(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
#end
@Override
public List<${ClassName}Vo> queryList(${ClassName}Bo bo) {
return listVo(buildQueryWrapper(bo));
LambdaQueryWrapper<${ClassName}> lqw = buildQueryWrapper(bo);
return listVo(lqw);
}
private LambdaQueryWrapper<${ClassName}> buildQueryWrapper(${ClassName}Bo bo) {

View File

@ -0,0 +1,476 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px">
#foreach($column in $columns)
#if($column.query)
#set($dictType=$column.dictType)
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
#set($parentheseIndex=$column.columnComment.indexOf(""))
#if($parentheseIndex != -1)
#set($comment=$column.columnComment.substring(0, $parentheseIndex))
#else
#set($comment=$column.columnComment)
#end
#if($column.htmlType == "input" || $column.htmlType == "textarea")
<el-form-item label="${comment}" prop="${column.javaField}">
<el-input
v-model="queryParams.${column.javaField}"
placeholder="请输入${comment}"
clearable
size="small"
@keyup.enter="handleQuery"
/>
</el-form-item>
#elseif(($column.htmlType == "select" || $column.htmlType == "radio") && "" != $dictType)
<el-form-item label="${comment}" prop="${column.javaField}">
<el-select v-model="queryParams.${column.javaField}" placeholder="请选择${comment}" clearable size="small">
<el-option
v-for="dict in ${dictType}"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
#elseif(($column.htmlType == "select" || $column.htmlType == "radio") && $dictType)
<el-form-item label="${comment}" prop="${column.javaField}">
<el-select v-model="queryParams.${column.javaField}" placeholder="请选择${comment}" clearable size="small">
<el-option label="请选择字典生成" value="" />
</el-select>
</el-form-item>
#elseif($column.htmlType == "datetime" && $column.queryType != "BETWEEN")
<el-form-item label="${comment}" prop="${column.javaField}">
<el-date-picker clearable size="small"
v-model="queryParams.${column.javaField}"
type="date"
value-format="YYYY-MM-DD"
placeholder="选择${comment}">
</el-date-picker>
</el-form-item>
#elseif($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
<el-form-item label="${comment}">
<el-date-picker
v-model="daterange${AttrName}"
size="small"
style="width: 240px"
value-format="YYYY-MM-DD"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
></el-date-picker>
</el-form-item>
#end
#end
#end
<el-form-item>
<el-button type="primary" icon="Search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="Plus"
size="mini"
@click="handleAdd"
v-hasPermi="['${moduleName}:${businessName}:add']"
>新增</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table
v-loading="loading"
:data="${businessName}List"
row-key="${treeCode}"
default-expand-all
:tree-props="{children: 'children', hasChildren: 'hasChildren'}"
>
#foreach($column in $columns)
#set($javaField=$column.javaField)
#set($parentheseIndex=$column.columnComment.indexOf(""))
#if($parentheseIndex != -1)
#set($comment=$column.columnComment.substring(0, $parentheseIndex))
#else
#set($comment=$column.columnComment)
#end
#if($column.pk)
#elseif($column.list && $column.htmlType == "datetime")
<el-table-column label="${comment}" align="center" prop="${javaField}" width="180">
<template #default="scope">
<span>{{ parseTime(scope.row.${javaField}, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
#elseif($column.list && $column.dictType && "" != $column.dictType)
<el-table-column label="${comment}" align="center" prop="${javaField}">
<template #default="scope">
#if($column.htmlType == "checkbox")
<dict-tag :options="${column.dictType}" :value="scope.row.${javaField} ? scope.row.${javaField}.split(',') : []"/>
#else
<dict-tag :options="${column.dictType}" :value="scope.row.${javaField}"/>
#end
</template>
</el-table-column>
#elseif($column.list && "" != $javaField)
#if(${foreach.index} == 1)
<el-table-column label="${comment}" prop="${javaField}" />
#else
<el-table-column label="${comment}" align="center" prop="${javaField}" />
#end
#end
#end
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-button
size="mini"
type="text"
icon="Edit"
@click="handleUpdate(scope.row)"
v-hasPermi="['${moduleName}:${businessName}:edit']"
>修改</el-button>
<el-button
size="mini"
type="text"
icon="Plus"
@click="handleAdd(scope.row)"
v-hasPermi="['${moduleName}:${businessName}:add']"
>新增</el-button>
<el-button
size="mini"
type="text"
icon="Delete"
@click="handleDelete(scope.row)"
v-hasPermi="['${moduleName}:${businessName}:remove']"
>删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 添加或修改${functionName}对话框 -->
<el-dialog :title="title" v-model="open" width="500px" append-to-body>
<el-form ref="${businessName}Ref" :model="form" :rules="rules" label-width="80px">
#foreach($column in $columns)
#set($field=$column.javaField)
#if($column.insert && !$column.pk)
#set($parentheseIndex=$column.columnComment.indexOf(""))
#if($parentheseIndex != -1)
#set($comment=$column.columnComment.substring(0, $parentheseIndex))
#else
#set($comment=$column.columnComment)
#end
#set($dictType=$column.dictType)
#if("" != $treeParentCode && $column.javaField == $treeParentCode)
<el-form-item label="${comment}" prop="${treeParentCode}">
<tree-select
v-model:value="form.${treeParentCode}"
:options="${businessName}Options"
:objMap="{ value: '${treeCode}', label: '${treeName}', children: 'children' }"
placeholder="请选择${comment}"
/>
</el-form-item>
#elseif($column.htmlType == "input")
<el-form-item label="${comment}" prop="${field}">
<el-input v-model="form.${field}" placeholder="请输入${comment}" />
</el-form-item>
#elseif($column.htmlType == "imageUpload")
<el-form-item label="${comment}">
<imageUpload v-model="form.${field}"/>
</el-form-item>
#elseif($column.htmlType == "fileUpload")
<el-form-item label="${comment}">
<fileUpload v-model="form.${field}"/>
</el-form-item>
#elseif($column.htmlType == "editor")
<el-form-item label="${comment}">
<editor v-model="form.${field}" :min-height="192"/>
</el-form-item>
#elseif($column.htmlType == "select" && "" != $dictType)
<el-form-item label="${comment}" prop="${field}">
<el-select v-model="form.${field}" placeholder="请选择${comment}">
<el-option
v-for="dict in ${dictType}"
:key="dict.value"
:label="dict.label"
#if($column.javaType == "Integer" || $column.javaType == "Long"):value="parseInt(dict.value)"#else:value="dict.value"#end
></el-option>
</el-select>
</el-form-item>
#elseif($column.htmlType == "select" && $dictType)
<el-form-item label="${comment}" prop="${field}">
<el-select v-model="form.${field}" placeholder="请选择${comment}">
<el-option label="请选择字典生成" value="" />
</el-select>
</el-form-item>
#elseif($column.htmlType == "checkbox" && "" != $dictType)
<el-form-item label="${comment}">
<el-checkbox-group v-model="form.${field}">
<el-checkbox
v-for="dict in ${dictType}"
:key="dict.value"
:label="dict.value">
{{dict.label}}
</el-checkbox>
</el-checkbox-group>
</el-form-item>
#elseif($column.htmlType == "checkbox" && $dictType)
<el-form-item label="${comment}">
<el-checkbox-group v-model="form.${field}">
<el-checkbox>请选择字典生成</el-checkbox>
</el-checkbox-group>
</el-form-item>
#elseif($column.htmlType == "radio" && "" != $dictType)
<el-form-item label="${comment}">
<el-radio-group v-model="form.${field}">
<el-radio
v-for="dict in ${dictType}"
:key="dict.value"
#if($column.javaType == "Integer" || $column.javaType == "Long"):label="parseInt(dict.value)"#else:label="dict.value"#end
>{{dict.label}}</el-radio>
</el-radio-group>
</el-form-item>
#elseif($column.htmlType == "radio" && $dictType)
<el-form-item label="${comment}">
<el-radio-group v-model="form.${field}">
<el-radio label="1">请选择字典生成</el-radio>
</el-radio-group>
</el-form-item>
#elseif($column.htmlType == "datetime")
<el-form-item label="${comment}" prop="${field}">
<el-date-picker clearable size="small"
v-model="form.${field}"
type="datetime"
value-format="YYYY-MM-DD HH:mm:ss"
placeholder="选择${comment}">
</el-date-picker>
</el-form-item>
#elseif($column.htmlType == "textarea")
<el-form-item label="${comment}" prop="${field}">
<el-input v-model="form.${field}" type="textarea" placeholder="请输入内容" />
</el-form-item>
#end
#end
#end
#end
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitForm">确 定</el-button>
<el-button @click="cancel">取 消</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup name="${BusinessName}">
import { list${BusinessName}, get${BusinessName}, del${BusinessName}, add${BusinessName}, update${BusinessName} } from "@/api/${moduleName}/${businessName}";
const { proxy } = getCurrentInstance();
#if(${dicts} != '')
#set($dictsNoSymbol=$dicts.replace("'", ""))
const { ${dictsNoSymbol} } = proxy.useDict(${dicts});
#end
const ${businessName}List = ref([]);
const ${businessName}Options = ref([]);
const open = ref(false);
const buttonLoading = ref(false);
const loading = ref(true);
const showSearch = ref(true);
const title = ref("");
#foreach ($column in $columns)
#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
const daterange${AttrName} = ref([]);
#end
#end
const data = reactive({
form: {},
queryParams: {
#foreach ($column in $columns)
#if($column.query)
$column.javaField: undefined#if($foreach.count != $columns.size()),#end
#end
#end
},
rules: {
#foreach ($column in $columns)
#if($column.required)
#set($parentheseIndex=$column.columnComment.indexOf(""))
#if($parentheseIndex != -1)
#set($comment=$column.columnComment.substring(0, $parentheseIndex))
#else
#set($comment=$column.columnComment)
#end
$column.javaField: [
{ required: true, message: "$comment不能为空", trigger: #if($column.htmlType == "select")"change"#else"blur"#end }
]#if($foreach.count != $columns.size()),#end
#end
#end
}
});
const { queryParams, form, rules } = toRefs(data);
/** 查询${functionName}列表 */
function getList() {
loading.value = true;
#foreach ($column in $columns)
#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
queryParams.value.params = {};
#break
#end
#end
#foreach ($column in $columns)
#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
if (null != daterange${AttrName} && '' != daterange${AttrName}) {
queryParams.value.params["begin${AttrName}"] = daterange${AttrName}.value[0];
queryParams.value.params["end${AttrName}"] = daterange${AttrName}.value[1];
}
#end
#end
list${BusinessName}(queryParams.value).then(response => {
${businessName}List.value = proxy.handleTree(response.data, "${treeCode}", "${treeParentCode}");
loading.value = false;
});
}
/** 查询${functionName}下拉树结构 */
async function getTreeselect() {
await list${BusinessName}().then(response => {
${businessName}Options.value = [];
const data = { ${treeCode}: 0, ${treeName}: '顶级节点', children: [] };
data.children = proxy.handleTree(response.data, "${treeCode}", "${treeParentCode}");
${businessName}Options.value.push(data);
});
}
// 取消按钮
function cancel() {
open.value = false;
reset();
}
// 表单重置
function reset() {
form.value = {
#foreach ($column in $columns)
#if($column.htmlType == "radio")
$column.javaField: #if($column.javaType == "Integer" || $column.javaType == "Long")0#else"0"#end#if($foreach.count != $columns.size()),#end
#elseif($column.htmlType == "checkbox")
$column.javaField: []#if($foreach.count != $columns.size()),#end
#else
$column.javaField: null#if($foreach.count != $columns.size()),#end
#end
#end
};
proxy.resetForm("${businessName}Ref");
}
/** 搜索按钮操作 */
function handleQuery() {
getList();
}
/** 重置按钮操作 */
function resetQuery() {
#foreach ($column in $columns)
#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
daterange${AttrName}.value = [];
#end
#end
proxy.resetForm("queryRef");
handleQuery();
}
/** 新增按钮操作 */
async function handleAdd(row) {
reset();
await getTreeselect();
if (row != null && row.${treeCode}) {
form.value.${treeParentCode} = row.${treeCode};
} else {
form.value.${treeParentCode} = 0;
}
open.value = true;
title.value = "添加${functionName}";
}
/** 修改按钮操作 */
async function handleUpdate(row) {
loading.value = true;
reset();
await getTreeselect();
if (row != null) {
form.value.${treeParentCode} = row.${treeCode};
}
get${BusinessName}(row.${pkColumn.javaField}).then(response => {
loading.value = false;
form.value = response.data;
#foreach ($column in $columns)
#if($column.htmlType == "checkbox")
form.value.$column.javaField = form.value.${column.javaField}.split(",");
#end
#end
open.value = true;
title.value = "修改${functionName}";
});
}
/** 提交按钮 */
function submitForm() {
proxy.#[[$]]#refs["${businessName}Ref"].validate(valid => {
if (valid) {
buttonLoading.value = true;
#foreach ($column in $columns)
#if($column.htmlType == "checkbox")
form.value.$column.javaField = form.value.${column.javaField}.join(",");
#end
#end
if (form.value.${pkColumn.javaField} != null) {
update${BusinessName}(form.value).then(response => {
proxy.#[[$modal]]#.msgSuccess("修改成功");
open.value = false;
getList();
}).finally(() => {
buttonLoading.value = false;
});
} else {
add${BusinessName}(form.value).then(response => {
proxy.#[[$modal]]#.msgSuccess("新增成功");
open.value = false;
getList();
}).finally(() => {
buttonLoading.value = false;
});
}
}
});
}
/** 删除按钮操作 */
function handleDelete(row) {
proxy.#[[$modal]]#.confirm('是否确认删除${functionName}编号为"' + row.${pkColumn.javaField} + '"的数据项?').then(function() {
loading.value = true;
return del${BusinessName}(row.${pkColumn.javaField});
}).then(() => {
loading.value = false;
getList();
proxy.#[[$modal]]#.msgSuccess("删除成功");
}).finally(() => {
loading.value = false;
});
}
getList();
</script>

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