mirror of
https://gitee.com/y_project/RuoYi-Cloud.git
synced 2026-01-26 19:51:56 +08:00
Compare commits
77 Commits
v3.6.4
...
55fafbddb6
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
55fafbddb6 | ||
|
|
a6bcebb62b | ||
|
|
1cb262daa3 | ||
|
|
8c096cba8d | ||
|
|
088cec8adf | ||
|
|
85ff6a9910 | ||
|
|
cab5beaca7 | ||
|
|
fc8069a250 | ||
|
|
924e705dca | ||
|
|
049ba453d1 | ||
|
|
4aa261e8f7 | ||
|
|
914a6620f5 | ||
|
|
3dcee7057d | ||
|
|
0467631319 | ||
|
|
a11df90255 | ||
|
|
596e4fe756 | ||
|
|
37219e4ae6 | ||
|
|
adaa3e1db8 | ||
|
|
88ad5a2c19 | ||
|
|
b45dc2ec25 | ||
|
|
67b17da06f | ||
|
|
b25a280ebb | ||
|
|
cc026e75a3 | ||
|
|
7216b56a56 | ||
|
|
a326e880a1 | ||
|
|
92c6d21855 | ||
|
|
2335157f6e | ||
|
|
6c3b01c3c5 | ||
|
|
8faea60191 | ||
|
|
28a16d9878 | ||
|
|
dd3cf18e27 | ||
|
|
65d03dc014 | ||
|
|
7912fd81bd | ||
|
|
e2175e5b9d | ||
|
|
c0e119f8e0 | ||
|
|
fa77b2a08c | ||
|
|
a222c24796 | ||
|
|
08f4b877ce | ||
|
|
aa607d135c | ||
|
|
6d34cdb8a3 | ||
|
|
d47352253e | ||
|
|
61cbd470e1 | ||
|
|
b3ef4adfed | ||
|
|
856c471472 | ||
|
|
bec5600f16 | ||
|
|
5b485e7934 | ||
|
|
73a752d3ab | ||
|
|
1899a832b9 | ||
|
|
e6796c0954 | ||
|
|
4987289a98 | ||
|
|
18409922a5 | ||
|
|
3dca02b306 | ||
|
|
75f3275e15 | ||
|
|
b2e4a7046b | ||
|
|
60618c1da9 | ||
|
|
3b499b1344 | ||
|
|
8984ecba86 | ||
|
|
0953a9c0b2 | ||
|
|
fcff9dfdea | ||
|
|
d1cb4e1f71 | ||
|
|
68ef1297cb | ||
|
|
b79f01e051 | ||
|
|
095f3a126f | ||
|
|
ce94a9d620 | ||
|
|
2a73de34f0 | ||
|
|
8694501a7e | ||
|
|
4dfc3d766e | ||
|
|
0680d1ed1f | ||
|
|
493dee03c8 | ||
|
|
3992b1e666 | ||
|
|
c855884ebd | ||
|
|
19c457ae5f | ||
|
|
1a3751ab71 | ||
|
|
af8d62e5e2 | ||
|
|
cf03781bd7 | ||
|
|
fa2a28e877 | ||
|
|
7c0be1fa97 |
14
README.md
14
README.md
@@ -1,11 +1,11 @@
|
||||
<p align="center">
|
||||
<img alt="logo" src="https://oscimg.oschina.net/oscnet/up-b99b286755aef70355a7084753f89cdb7c9.png">
|
||||
</p>
|
||||
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">RuoYi v3.6.4</h1>
|
||||
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">RuoYi v3.6.5</h1>
|
||||
<h4 align="center">基于 Vue/Element UI 和 Spring Boot/Spring Cloud & Alibaba 前后端分离的分布式微服务架构</h4>
|
||||
<p align="center">
|
||||
<a href="https://gitee.com/y_project/RuoYi-Cloud/stargazers"><img src="https://gitee.com/y_project/RuoYi-Cloud/badge/star.svg?theme=dark"></a>
|
||||
<a href="https://gitee.com/y_project/RuoYi-Cloud"><img src="https://img.shields.io/badge/RuoYi-v3.6.4-brightgreen.svg"></a>
|
||||
<a href="https://gitee.com/y_project/RuoYi-Cloud"><img src="https://img.shields.io/badge/RuoYi-v3.6.5-brightgreen.svg"></a>
|
||||
<a href="https://gitee.com/y_project/RuoYi-Cloud/blob/master/LICENSE"><img src="https://img.shields.io/github/license/mashape/apistatus.svg"></a>
|
||||
</p>
|
||||
|
||||
@@ -17,12 +17,9 @@
|
||||
* 后端采用Spring Boot、Spring Cloud & Alibaba。
|
||||
* 注册中心、配置中心选型Nacos,权限认证使用Redis。
|
||||
* 流量控制框架选型Sentinel,分布式事务选型Seata。
|
||||
* 提供了技术栈([Vue3](https://v3.cn.vuejs.org) [Element Plus](https://element-plus.org/zh-CN) [Vite](https://cn.vitejs.dev))版本[RuoYi-Cloud-Vue3](https://github.com/yangzongzhuan/RuoYi-Cloud-Vue3),保持同步更新。
|
||||
* 提供了技术栈([Vue3](https://v3.cn.vuejs.org) [Element Plus](https://element-plus.org/zh-CN) [Vite](https://cn.vitejs.dev))版本[RuoYi-Cloud-Vue3](https://gitcode.com/yangzongzhuan/RuoYi-Cloud-Vue3),保持同步更新。
|
||||
* 如需不分离应用,请移步 [RuoYi](https://gitee.com/y_project/RuoYi),如需分离应用,请移步 [RuoYi-Vue](https://gitee.com/y_project/RuoYi-Vue)
|
||||
* 阿里云折扣场:[点我进入](http://aly.ruoyi.vip),腾讯云秒杀场:[点我进入](http://txy.ruoyi.vip)
|
||||
* 阿里云优惠券:[点我领取](https://www.aliyun.com/minisite/goods?userCode=brki8iof&share_source=copy_link),腾讯云优惠券:[点我领取](https://cloud.tencent.com/redirect.php?redirect=1025&cps_key=198c8df2ed259157187173bc7f4f32fd&from=console)
|
||||
|
||||
#### 友情链接 [若依/RuoYi-Cloud](https://gitee.com/zhangmrit/ruoyi-cloud) Ant Design版本。
|
||||
* 阿里云优惠券:[点我进入](http://aly.ruoyi.vip),腾讯云优惠券:[点我进入](http://txy.ruoyi.vip)
|
||||
|
||||
## 系统模块
|
||||
|
||||
@@ -41,6 +38,7 @@ com.ruoyi
|
||||
│ └── ruoyi-common-redis // 缓存服务
|
||||
│ └── ruoyi-common-seata // 分布式事务
|
||||
│ └── ruoyi-common-security // 安全模块
|
||||
│ └── ruoyi-common-sensitive // 数据脱敏
|
||||
│ └── ruoyi-common-swagger // 系统接口
|
||||
├── ruoyi-modules // 业务模块
|
||||
│ └── ruoyi-system // 系统模块 [9201]
|
||||
@@ -128,4 +126,4 @@ com.ruoyi
|
||||
|
||||
## 若依微服务交流群
|
||||
|
||||
QQ群: [](https://jq.qq.com/?_wv=1027&k=yqInfq0S) [](https://jq.qq.com/?_wv=1027&k=Oy1mb3p8) [](https://jq.qq.com/?_wv=1027&k=rvxkJtXK) [](https://jq.qq.com/?_wv=1027&k=0Ck3PvTe) [](https://jq.qq.com/?_wv=1027&k=FnHHP4TT) [](https://jq.qq.com/?_wv=1027&k=qdT1Ojpz) [](https://jq.qq.com/?_wv=1027&k=nw3OiyXs) [](https://jq.qq.com/?_wv=1027&k=kiU5WDls) [](https://jq.qq.com/?_wv=1027&k=MtBy6YfT) [](https://jq.qq.com/?_wv=1027&k=FqImHgH2) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=G4jZ4EtdT50PhnMBudTnEwgonxkXOscJ&authKey=FkGHYfoTKlGE6wHdKdjH9bVoOgQjtLP9WM%2Fj7pqGY1msoqw9uxDiBo39E2mLgzYg&noverify=0&group_code=128355254) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=irnwcXhbLOQEv1g-TwGifjNTA_f4wZiA&authKey=4bpzEwhcUY%2FvsPDHvzYn6xfoS%2FtOArvZ%2BGXzfr7O0%2FEqLfkKA%2BuCDXlzHIFg8t93&noverify=0&group_code=179219821) 点击按钮入群。
|
||||
QQ群: [](https://jq.qq.com/?_wv=1027&k=yqInfq0S) [](https://jq.qq.com/?_wv=1027&k=Oy1mb3p8) [](https://jq.qq.com/?_wv=1027&k=rvxkJtXK) [](https://jq.qq.com/?_wv=1027&k=0Ck3PvTe) [](https://jq.qq.com/?_wv=1027&k=FnHHP4TT) [](https://jq.qq.com/?_wv=1027&k=qdT1Ojpz) [](https://jq.qq.com/?_wv=1027&k=nw3OiyXs) [](https://jq.qq.com/?_wv=1027&k=kiU5WDls) [](https://jq.qq.com/?_wv=1027&k=MtBy6YfT) [](https://jq.qq.com/?_wv=1027&k=FqImHgH2) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=G4jZ4EtdT50PhnMBudTnEwgonxkXOscJ&authKey=FkGHYfoTKlGE6wHdKdjH9bVoOgQjtLP9WM%2Fj7pqGY1msoqw9uxDiBo39E2mLgzYg&noverify=0&group_code=128355254) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=irnwcXhbLOQEv1g-TwGifjNTA_f4wZiA&authKey=4bpzEwhcUY%2FvsPDHvzYn6xfoS%2FtOArvZ%2BGXzfr7O0%2FEqLfkKA%2BuCDXlzHIFg8t93&noverify=0&group_code=179219821) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=lx1uEdEDuxeM7rUvF3qmlFdqKqdJ5Z-R&authKey=rgyPW9yhhh4IIURKVFa6NgP3qiqH04WAzrJ0trsgkr3pjzm6sKIOGyA58oOjoj%2FJ&noverify=0&group_code=158753145) 点击按钮入群。
|
||||
@@ -9,8 +9,8 @@ usage() {
|
||||
|
||||
# copy sql
|
||||
echo "begin copy sql "
|
||||
cp ../sql/ry_20231130.sql ./mysql/db
|
||||
cp ../sql/ry_config_20231204.sql ./mysql/db
|
||||
cp ../sql/ry_20240629.sql ./mysql/db
|
||||
cp ../sql/ry_config_20250224.sql ./mysql/db
|
||||
|
||||
# copy html
|
||||
echo "begin copy html "
|
||||
|
||||
@@ -29,7 +29,7 @@ http {
|
||||
}
|
||||
|
||||
# 避免actuator暴露
|
||||
if ($request_uri ~ "/actuator") {
|
||||
if ($uri ~ "/actuator") {
|
||||
return 403;
|
||||
}
|
||||
|
||||
|
||||
85
pom.xml
85
pom.xml
@@ -6,41 +6,54 @@
|
||||
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<artifactId>ruoyi</artifactId>
|
||||
<version>3.6.4</version>
|
||||
<version>3.6.5</version>
|
||||
|
||||
<name>ruoyi</name>
|
||||
<url>http://www.ruoyi.vip</url>
|
||||
<description>若依微服务系统</description>
|
||||
|
||||
<properties>
|
||||
<ruoyi.version>3.6.4</ruoyi.version>
|
||||
<ruoyi.version>3.6.5</ruoyi.version>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
<java.version>1.8</java.version>
|
||||
<spring-boot.version>2.7.18</spring-boot.version>
|
||||
<spring-cloud.version>2021.0.8</spring-cloud.version>
|
||||
<spring-cloud-alibaba.version>2021.0.5.0</spring-cloud-alibaba.version>
|
||||
<spring-boot-admin.version>2.7.15</spring-boot-admin.version>
|
||||
<swagger.fox.version>3.0.0</swagger.fox.version>
|
||||
<swagger.core.version>1.6.2</swagger.core.version>
|
||||
<spring-cloud.version>2021.0.9</spring-cloud.version>
|
||||
<spring-cloud-alibaba.version>2021.0.6.1</spring-cloud-alibaba.version>
|
||||
<spring-boot-admin.version>2.7.16</spring-boot-admin.version>
|
||||
<tobato.version>1.27.2</tobato.version>
|
||||
<kaptcha.version>2.3.3</kaptcha.version>
|
||||
<pagehelper.boot.version>2.0.0</pagehelper.boot.version>
|
||||
<druid.version>1.2.20</druid.version>
|
||||
<dynamic-ds.version>4.2.0</dynamic-ds.version>
|
||||
<druid.version>1.2.23</druid.version>
|
||||
<dynamic-ds.version>4.3.1</dynamic-ds.version>
|
||||
<commons.io.version>2.13.0</commons.io.version>
|
||||
<velocity.version>2.3</velocity.version>
|
||||
<fastjson.version>2.0.43</fastjson.version>
|
||||
<fastjson.version>2.0.53</fastjson.version>
|
||||
<jjwt.version>0.9.1</jjwt.version>
|
||||
<minio.version>8.2.2</minio.version>
|
||||
<poi.version>4.1.2</poi.version>
|
||||
<springdoc.version>1.6.9</springdoc.version>
|
||||
<transmittable-thread-local.version>2.14.4</transmittable-thread-local.version>
|
||||
|
||||
<!-- override dependency version -->
|
||||
<tomcat.version>9.0.98</tomcat.version>
|
||||
<logback.version>1.2.13</logback.version>
|
||||
<spring-framework.version>5.3.39</spring-framework.version>
|
||||
</properties>
|
||||
|
||||
<!-- 依赖声明 -->
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
|
||||
<!-- 覆盖SpringFramework的依赖配置-->
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-framework-bom</artifactId>
|
||||
<version>${spring-framework.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- SpringCloud 微服务 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
@@ -68,6 +81,38 @@
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- 覆盖logback的依赖配置-->
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-core</artifactId>
|
||||
<version>${logback.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<version>${logback.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 覆盖tomcat的依赖配置-->
|
||||
<dependency>
|
||||
<groupId>org.apache.tomcat.embed</groupId>
|
||||
<artifactId>tomcat-embed-core</artifactId>
|
||||
<version>${tomcat.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.tomcat.embed</groupId>
|
||||
<artifactId>tomcat-embed-el</artifactId>
|
||||
<version>${tomcat.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.tomcat.embed</groupId>
|
||||
<artifactId>tomcat-embed-websocket</artifactId>
|
||||
<version>${tomcat.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- FastDFS 分布式文件系统 -->
|
||||
<dependency>
|
||||
<groupId>com.github.tobato</groupId>
|
||||
@@ -75,16 +120,11 @@
|
||||
<version>${tobato.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Swagger 依赖配置 -->
|
||||
<!-- Springdoc webmvc 依赖配置 -->
|
||||
<dependency>
|
||||
<groupId>io.swagger</groupId>
|
||||
<artifactId>swagger-models</artifactId>
|
||||
<version>${swagger.core.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.swagger</groupId>
|
||||
<artifactId>swagger-annotations</artifactId>
|
||||
<version>${swagger.core.version}</version>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-ui</artifactId>
|
||||
<version>${springdoc.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 验证码 -->
|
||||
@@ -164,6 +204,13 @@
|
||||
<version>${ruoyi.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 数据脱敏 -->
|
||||
<dependency>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<artifactId>ruoyi-common-sensitive</artifactId>
|
||||
<version>${ruoyi.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 权限范围 -->
|
||||
<dependency>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<artifactId>ruoyi</artifactId>
|
||||
<version>3.6.4</version>
|
||||
<version>3.6.5</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<artifactId>ruoyi-api</artifactId>
|
||||
<version>3.6.4</version>
|
||||
<version>3.6.5</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestHeader;
|
||||
import com.ruoyi.common.core.constant.SecurityConstants;
|
||||
@@ -40,4 +41,14 @@ public interface RemoteUserService
|
||||
*/
|
||||
@PostMapping("/user/register")
|
||||
public R<Boolean> registerUserInfo(@RequestBody SysUser sysUser, @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
|
||||
|
||||
/**
|
||||
* 记录用户登录IP地址和登录时间
|
||||
*
|
||||
* @param sysUser 用户信息
|
||||
* @param source 请求来源
|
||||
* @return 结果
|
||||
*/
|
||||
@PutMapping("/user/recordlogin")
|
||||
public R<Boolean> recordUserLogin(@RequestBody SysUser sysUser, @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
import com.ruoyi.common.core.annotation.Excel;
|
||||
import com.ruoyi.common.core.annotation.Excel.ColumnType;
|
||||
import com.ruoyi.common.core.annotation.Excel.Type;
|
||||
import com.ruoyi.common.core.constant.UserConstants;
|
||||
import com.ruoyi.common.core.annotation.Excels;
|
||||
import com.ruoyi.common.core.web.domain.BaseEntity;
|
||||
import com.ruoyi.common.core.xss.Xss;
|
||||
@@ -22,7 +23,7 @@ public class SysUser extends BaseEntity
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 用户ID */
|
||||
@Excel(name = "用户序号", cellType = ColumnType.NUMERIC, prompt = "用户编号")
|
||||
@Excel(name = "用户序号", type = Type.EXPORT, cellType = ColumnType.NUMERIC, prompt = "用户编号")
|
||||
private Long userId;
|
||||
|
||||
/** 部门ID */
|
||||
@@ -116,7 +117,7 @@ public class SysUser extends BaseEntity
|
||||
|
||||
public static boolean isAdmin(Long userId)
|
||||
{
|
||||
return userId != null && 1L == userId;
|
||||
return UserConstants.isAdmin(userId);
|
||||
}
|
||||
|
||||
public Long getDeptId()
|
||||
|
||||
@@ -36,6 +36,12 @@ public class RemoteUserFallbackFactory implements FallbackFactory<RemoteUserServ
|
||||
{
|
||||
return R.fail("注册用户失败:" + throwable.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public R<Boolean> recordUserLogin(SysUser sysUser, String source)
|
||||
{
|
||||
return R.fail("记录用户登录信息失败:" + throwable.getMessage());
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<artifactId>ruoyi</artifactId>
|
||||
<version>3.6.4</version>
|
||||
<version>3.6.5</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import com.ruoyi.common.core.domain.R;
|
||||
import com.ruoyi.common.core.enums.UserStatus;
|
||||
import com.ruoyi.common.core.exception.ServiceException;
|
||||
import com.ruoyi.common.core.text.Convert;
|
||||
import com.ruoyi.common.core.utils.DateUtils;
|
||||
import com.ruoyi.common.core.utils.StringUtils;
|
||||
import com.ruoyi.common.core.utils.ip.IpUtils;
|
||||
import com.ruoyi.common.redis.service.RedisService;
|
||||
@@ -73,17 +74,11 @@ public class SysLoginService
|
||||
// 查询用户信息
|
||||
R<LoginUser> userResult = remoteUserService.getUserInfo(username, SecurityConstants.INNER);
|
||||
|
||||
if (StringUtils.isNull(userResult) || StringUtils.isNull(userResult.getData()))
|
||||
{
|
||||
recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "登录用户不存在");
|
||||
throw new ServiceException("登录用户:" + username + " 不存在");
|
||||
}
|
||||
|
||||
if (R.FAIL == userResult.getCode())
|
||||
{
|
||||
throw new ServiceException(userResult.getMsg());
|
||||
}
|
||||
|
||||
|
||||
LoginUser userInfo = userResult.getData();
|
||||
SysUser user = userResult.getData().getSysUser();
|
||||
if (UserStatus.DELETED.getCode().equals(user.getDelFlag()))
|
||||
@@ -98,9 +93,26 @@ public class SysLoginService
|
||||
}
|
||||
passwordService.validate(user, password);
|
||||
recordLogService.recordLogininfor(username, Constants.LOGIN_SUCCESS, "登录成功");
|
||||
recordLoginInfo(user.getUserId());
|
||||
return userInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录登录信息
|
||||
*
|
||||
* @param userId 用户ID
|
||||
*/
|
||||
public void recordLoginInfo(Long userId)
|
||||
{
|
||||
SysUser sysUser = new SysUser();
|
||||
sysUser.setUserId(userId);
|
||||
// 更新用户登录IP
|
||||
sysUser.setLoginIp(IpUtils.getIpAddr());
|
||||
// 更新用户登录时间
|
||||
sysUser.setLoginDate(DateUtils.getNowDate());
|
||||
remoteUserService.recordUserLogin(sysUser, SecurityConstants.INNER);
|
||||
}
|
||||
|
||||
public void logout(String loginName)
|
||||
{
|
||||
recordLogService.recordLogininfor(loginName, Constants.LOGOUT, "退出成功");
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<artifactId>ruoyi</artifactId>
|
||||
<version>3.6.4</version>
|
||||
<version>3.6.5</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
<module>ruoyi-common-seata</module>
|
||||
<module>ruoyi-common-swagger</module>
|
||||
<module>ruoyi-common-security</module>
|
||||
<module>ruoyi-common-sensitive</module>
|
||||
<module>ruoyi-common-datascope</module>
|
||||
<module>ruoyi-common-datasource</module>
|
||||
</modules>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<artifactId>ruoyi-common</artifactId>
|
||||
<version>3.6.4</version>
|
||||
<version>3.6.5</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@@ -107,12 +107,6 @@
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Swagger -->
|
||||
<dependency>
|
||||
<groupId>io.swagger</groupId>
|
||||
<artifactId>swagger-annotations</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -78,6 +78,11 @@ public @interface Excel
|
||||
*/
|
||||
public String prompt() default "";
|
||||
|
||||
/**
|
||||
* 是否允许内容换行
|
||||
*/
|
||||
public boolean wrapText() default false;
|
||||
|
||||
/**
|
||||
* 设置只能选择不能输入的列内容.
|
||||
*/
|
||||
|
||||
@@ -20,7 +20,7 @@ public class SecurityConstants
|
||||
/**
|
||||
* 授权信息字段
|
||||
*/
|
||||
public static final String AUTHORIZATION_HEADER = "authorization";
|
||||
public static final String AUTHORIZATION_HEADER = "Authorization";
|
||||
|
||||
/**
|
||||
* 请求来源
|
||||
|
||||
@@ -7,11 +7,6 @@ package com.ruoyi.common.core.constant;
|
||||
*/
|
||||
public class TokenConstants
|
||||
{
|
||||
/**
|
||||
* 令牌自定义标识
|
||||
*/
|
||||
public static final String AUTHENTICATION = "Authorization";
|
||||
|
||||
/**
|
||||
* 令牌前缀
|
||||
*/
|
||||
|
||||
@@ -21,6 +21,9 @@ public class UserConstants
|
||||
/** 用户封禁状态 */
|
||||
public static final String USER_DISABLE = "1";
|
||||
|
||||
/** 角色正常状态 */
|
||||
public static final String ROLE_NORMAL = "0";
|
||||
|
||||
/** 角色封禁状态 */
|
||||
public static final String ROLE_DISABLE = "1";
|
||||
|
||||
@@ -77,4 +80,9 @@ public class UserConstants
|
||||
public static final int PASSWORD_MIN_LENGTH = 5;
|
||||
|
||||
public static final int PASSWORD_MAX_LENGTH = 20;
|
||||
|
||||
public static boolean isAdmin(Long userId)
|
||||
{
|
||||
return userId != null && 1L == userId;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -364,6 +364,10 @@ public class Convert
|
||||
*/
|
||||
public static String[] toStrArray(String str)
|
||||
{
|
||||
if (StringUtils.isEmpty(str))
|
||||
{
|
||||
return new String[] {};
|
||||
}
|
||||
return toStrArray(",", str);
|
||||
}
|
||||
|
||||
@@ -536,9 +540,9 @@ public class Convert
|
||||
|
||||
/**
|
||||
* 转换为boolean<br>
|
||||
* String支持的值为:true、false、yes、ok、no,1,0 如果给定的值为空,或者转换失败,返回默认值<br>
|
||||
* String支持的值为:true、false、yes、ok、no、1、0、是、否, 如果给定的值为空,或者转换失败,返回默认值<br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
*
|
||||
* @param value 被转换的值
|
||||
* @param defaultValue 转换错误时的默认值
|
||||
* @return 结果
|
||||
@@ -565,10 +569,12 @@ public class Convert
|
||||
case "yes":
|
||||
case "ok":
|
||||
case "1":
|
||||
case "是":
|
||||
return true;
|
||||
case "false":
|
||||
case "no":
|
||||
case "0":
|
||||
case "否":
|
||||
return false;
|
||||
default:
|
||||
return defaultValue;
|
||||
|
||||
@@ -26,7 +26,7 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils
|
||||
|
||||
public static String YYYYMMDDHHMMSS = "yyyyMMddHHmmss";
|
||||
|
||||
public static String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";
|
||||
public static final String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";
|
||||
|
||||
private static String[] parsePatterns = {
|
||||
"yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM",
|
||||
|
||||
@@ -20,6 +20,9 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils
|
||||
/** 下划线 */
|
||||
private static final char SEPARATOR = '_';
|
||||
|
||||
/** 星号 */
|
||||
private static final char ASTERISK = '*';
|
||||
|
||||
/**
|
||||
* 获取参数不为空值
|
||||
*
|
||||
@@ -160,6 +163,49 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils
|
||||
return (str == null ? "" : str.trim());
|
||||
}
|
||||
|
||||
/**
|
||||
* 替换指定字符串的指定区间内字符为"*"
|
||||
*
|
||||
* @param str 字符串
|
||||
* @param startInclude 开始位置(包含)
|
||||
* @param endExclude 结束位置(不包含)
|
||||
* @return 替换后的字符串
|
||||
*/
|
||||
public static String hide(CharSequence str, int startInclude, int endExclude)
|
||||
{
|
||||
if (isEmpty(str))
|
||||
{
|
||||
return NULLSTR;
|
||||
}
|
||||
final int strLength = str.length();
|
||||
if (startInclude > strLength)
|
||||
{
|
||||
return NULLSTR;
|
||||
}
|
||||
if (endExclude > strLength)
|
||||
{
|
||||
endExclude = strLength;
|
||||
}
|
||||
if (startInclude > endExclude)
|
||||
{
|
||||
// 如果起始位置大于结束位置,不替换
|
||||
return NULLSTR;
|
||||
}
|
||||
final char[] chars = new char[strLength];
|
||||
for (int i = 0; i < strLength; i++)
|
||||
{
|
||||
if (i >= startInclude && i < endExclude)
|
||||
{
|
||||
chars[i] = ASTERISK;
|
||||
}
|
||||
else
|
||||
{
|
||||
chars[i] = str.charAt(i);
|
||||
}
|
||||
}
|
||||
return new String(chars);
|
||||
}
|
||||
|
||||
/**
|
||||
* 截取字符串
|
||||
*
|
||||
@@ -237,6 +283,32 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils
|
||||
return str.substring(start, end);
|
||||
}
|
||||
|
||||
/**
|
||||
* 在字符串中查找第一个出现的 `open` 和最后一个出现的 `close` 之间的子字符串
|
||||
*
|
||||
* @param str 要截取的字符串
|
||||
* @param open 起始字符串
|
||||
* @param close 结束字符串
|
||||
* @return 截取结果
|
||||
*/
|
||||
public static String substringBetweenLast(final String str, final String open, final String close)
|
||||
{
|
||||
if (isEmpty(str) || isEmpty(open) || isEmpty(close))
|
||||
{
|
||||
return NULLSTR;
|
||||
}
|
||||
final int start = str.indexOf(open);
|
||||
if (start != INDEX_NOT_FOUND)
|
||||
{
|
||||
final int end = str.lastIndexOf(close);
|
||||
if (end != INDEX_NOT_FOUND)
|
||||
{
|
||||
return str.substring(start + open.length(), end);
|
||||
}
|
||||
}
|
||||
return NULLSTR;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否为空,并且不是空白字符
|
||||
*
|
||||
|
||||
@@ -167,6 +167,11 @@ public class ExcelUtil<T>
|
||||
*/
|
||||
public Class<T> clazz;
|
||||
|
||||
/**
|
||||
* 需要显示列属性
|
||||
*/
|
||||
public String[] includeFields;
|
||||
|
||||
/**
|
||||
* 需要排除列属性
|
||||
*/
|
||||
@@ -177,11 +182,20 @@ public class ExcelUtil<T>
|
||||
this.clazz = clazz;
|
||||
}
|
||||
|
||||
/**
|
||||
* 仅在Excel中显示列属性
|
||||
*
|
||||
* @param fields 列属性名 示例[单个"name"/多个"id","name"]
|
||||
*/
|
||||
public void showColumn(String... fields)
|
||||
{
|
||||
this.includeFields = fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* 隐藏Excel中列属性
|
||||
*
|
||||
* @param fields 列属性名 示例[单个"name"/多个"id","name"]
|
||||
* @throws Exception
|
||||
*/
|
||||
public void hideColumn(String... fields)
|
||||
{
|
||||
@@ -211,8 +225,6 @@ public class ExcelUtil<T>
|
||||
{
|
||||
if (StringUtils.isNotEmpty(title))
|
||||
{
|
||||
subMergedFirstRowNum++;
|
||||
subMergedLastRowNum++;
|
||||
int titleLastCol = this.fields.size() - 1;
|
||||
if (isSubList())
|
||||
{
|
||||
@@ -223,7 +235,7 @@ public class ExcelUtil<T>
|
||||
Cell titleCell = titleRow.createCell(0);
|
||||
titleCell.setCellStyle(styles.get("title"));
|
||||
titleCell.setCellValue(title);
|
||||
sheet.addMergedRegion(new CellRangeAddress(titleRow.getRowNum(), titleRow.getRowNum(), titleRow.getRowNum(), titleLastCol));
|
||||
sheet.addMergedRegion(new CellRangeAddress(titleRow.getRowNum(), titleRow.getRowNum(), 0, titleLastCol));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,23 +246,31 @@ public class ExcelUtil<T>
|
||||
{
|
||||
if (isSubList())
|
||||
{
|
||||
subMergedFirstRowNum++;
|
||||
subMergedLastRowNum++;
|
||||
Row subRow = sheet.createRow(rownum);
|
||||
int excelNum = 0;
|
||||
int column = 0;
|
||||
int subFieldSize = subFields != null ? subFields.size() : 0;
|
||||
for (Object[] objects : fields)
|
||||
{
|
||||
Field field = (Field) objects[0];
|
||||
Excel attr = (Excel) objects[1];
|
||||
Cell headCell1 = subRow.createCell(excelNum);
|
||||
headCell1.setCellValue(attr.name());
|
||||
headCell1.setCellStyle(styles.get(StringUtils.format("header_{}_{}", attr.headerColor(), attr.headerBackgroundColor())));
|
||||
excelNum++;
|
||||
}
|
||||
int headFirstRow = excelNum - 1;
|
||||
int headLastRow = headFirstRow + subFields.size() - 1;
|
||||
if (headLastRow > headFirstRow)
|
||||
{
|
||||
sheet.addMergedRegion(new CellRangeAddress(rownum, rownum, headFirstRow, headLastRow));
|
||||
if (Collection.class.isAssignableFrom(field.getType()))
|
||||
{
|
||||
Cell cell = subRow.createCell(column);
|
||||
cell.setCellValue(attr.name());
|
||||
cell.setCellStyle(styles.get(StringUtils.format("header_{}_{}", attr.headerColor(), attr.headerBackgroundColor())));
|
||||
if (subFieldSize > 1)
|
||||
{
|
||||
CellRangeAddress cellAddress = new CellRangeAddress(rownum, rownum, column, column + subFieldSize - 1);
|
||||
sheet.addMergedRegion(cellAddress);
|
||||
}
|
||||
column += subFieldSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
Cell cell = subRow.createCell(column++);
|
||||
cell.setCellValue(attr.name());
|
||||
cell.setCellStyle(styles.get(StringUtils.format("header_{}_{}", attr.headerColor(), attr.headerBackgroundColor())));
|
||||
}
|
||||
}
|
||||
rownum++;
|
||||
}
|
||||
@@ -263,11 +283,23 @@ public class ExcelUtil<T>
|
||||
* @return 转换后集合
|
||||
*/
|
||||
public List<T> importExcel(InputStream is)
|
||||
{
|
||||
return importExcel(is, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 对excel表单默认第一个索引名转换成list
|
||||
*
|
||||
* @param is 输入流
|
||||
* @param titleNum 标题占用行数
|
||||
* @return 转换后集合
|
||||
*/
|
||||
public List<T> importExcel(InputStream is, int titleNum)
|
||||
{
|
||||
List<T> list = null;
|
||||
try
|
||||
{
|
||||
list = importExcel(is, 0);
|
||||
list = importExcel(StringUtils.EMPTY, is, titleNum);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -281,18 +313,6 @@ public class ExcelUtil<T>
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 对excel表单默认第一个索引名转换成list
|
||||
*
|
||||
* @param is 输入流
|
||||
* @param titleNum 标题占用行数
|
||||
* @return 转换后集合
|
||||
*/
|
||||
public List<T> importExcel(InputStream is, int titleNum) throws Exception
|
||||
{
|
||||
return importExcel(StringUtils.EMPTY, is, titleNum);
|
||||
}
|
||||
|
||||
/**
|
||||
* 对excel表单指定表格索引名转换成list
|
||||
*
|
||||
@@ -575,64 +595,91 @@ public class ExcelUtil<T>
|
||||
{
|
||||
int startNo = index * sheetSize;
|
||||
int endNo = Math.min(startNo + sheetSize, list.size());
|
||||
int rowNo = (1 + rownum) - startNo;
|
||||
int currentRowNum = rownum + 1; // 从标题行后开始
|
||||
|
||||
for (int i = startNo; i < endNo; i++)
|
||||
{
|
||||
rowNo = isSubList() ? (i > 1 ? rowNo + 1 : rowNo + i) : i + 1 + rownum - startNo;
|
||||
row = sheet.createRow(rowNo);
|
||||
// 得到导出对象.
|
||||
row = sheet.createRow(currentRowNum);
|
||||
T vo = (T) list.get(i);
|
||||
Collection<?> subList = null;
|
||||
if (isSubList())
|
||||
{
|
||||
if (isSubListValue(vo))
|
||||
{
|
||||
subList = getListCellValue(vo);
|
||||
subMergedLastRowNum = subMergedLastRowNum + subList.size();
|
||||
}
|
||||
else
|
||||
{
|
||||
subMergedFirstRowNum++;
|
||||
subMergedLastRowNum++;
|
||||
}
|
||||
}
|
||||
int column = 0;
|
||||
int maxSubListSize = getCurrentMaxSubListSize(vo);
|
||||
for (Object[] os : fields)
|
||||
{
|
||||
Field field = (Field) os[0];
|
||||
Excel excel = (Excel) os[1];
|
||||
if (Collection.class.isAssignableFrom(field.getType()) && StringUtils.isNotNull(subList))
|
||||
if (Collection.class.isAssignableFrom(field.getType()))
|
||||
{
|
||||
boolean subFirst = false;
|
||||
for (Object obj : subList)
|
||||
try
|
||||
{
|
||||
if (subFirst)
|
||||
Collection<?> subList = (Collection<?>) getTargetValue(vo, field, excel);
|
||||
if (subList != null && !subList.isEmpty())
|
||||
{
|
||||
rowNo++;
|
||||
row = sheet.createRow(rowNo);
|
||||
}
|
||||
List<Field> subFields = FieldUtils.getFieldsListWithAnnotation(obj.getClass(), Excel.class);
|
||||
int subIndex = 0;
|
||||
for (Field subField : subFields)
|
||||
{
|
||||
if (subField.isAnnotationPresent(Excel.class))
|
||||
int subIndex = 0;
|
||||
for (Object subVo : subList)
|
||||
{
|
||||
subField.setAccessible(true);
|
||||
Excel attr = subField.getAnnotation(Excel.class);
|
||||
this.addCell(attr, row, (T) obj, subField, column + subIndex);
|
||||
Row subRow = sheet.getRow(currentRowNum + subIndex);
|
||||
if (subRow == null)
|
||||
{
|
||||
subRow = sheet.createRow(currentRowNum + subIndex);
|
||||
}
|
||||
|
||||
int subColumn = column;
|
||||
for (Field subField : subFields)
|
||||
{
|
||||
Excel subExcel = subField.getAnnotation(Excel.class);
|
||||
addCell(subExcel, subRow, (T) subVo, subField, subColumn++);
|
||||
}
|
||||
subIndex++;
|
||||
}
|
||||
subIndex++;
|
||||
column += subFields.size();
|
||||
}
|
||||
subFirst = true;
|
||||
}
|
||||
this.subMergedFirstRowNum = this.subMergedFirstRowNum + subList.size();
|
||||
catch (Exception e)
|
||||
{
|
||||
log.error("填充集合数据失败", e);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.addCell(excel, row, vo, field, column++);
|
||||
// 创建单元格并设置值
|
||||
addCell(excel, row, vo, field, column);
|
||||
if (maxSubListSize > 1 && excel.needMerge())
|
||||
{
|
||||
sheet.addMergedRegion(new CellRangeAddress(currentRowNum, currentRowNum + maxSubListSize - 1, column, column));
|
||||
}
|
||||
column++;
|
||||
}
|
||||
}
|
||||
currentRowNum += maxSubListSize;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取子列表最大数
|
||||
*/
|
||||
private int getCurrentMaxSubListSize(T vo)
|
||||
{
|
||||
int maxSubListSize = 1;
|
||||
for (Object[] os : fields)
|
||||
{
|
||||
Field field = (Field) os[0];
|
||||
if (Collection.class.isAssignableFrom(field.getType()))
|
||||
{
|
||||
try
|
||||
{
|
||||
Collection<?> subList = (Collection<?>) getTargetValue(vo, field, (Excel) os[1]);
|
||||
if (subList != null && !subList.isEmpty())
|
||||
{
|
||||
maxSubListSize = Math.max(maxSubListSize, subList.size());
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
log.error("获取集合大小失败", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return maxSubListSize;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -767,7 +814,7 @@ public class ExcelUtil<T>
|
||||
*/
|
||||
public void annotationDataStyles(Map<String, CellStyle> styles, Field field, Excel excel)
|
||||
{
|
||||
String key = StringUtils.format("data_{}_{}_{}_{}", excel.align(), excel.color(), excel.backgroundColor(), excel.cellType());
|
||||
String key = StringUtils.format("data_{}_{}_{}_{}_{}", excel.align(), excel.color(), excel.backgroundColor(), excel.cellType(), excel.wrapText());
|
||||
if (!styles.containsKey(key))
|
||||
{
|
||||
CellStyle style = wb.createCellStyle();
|
||||
@@ -783,6 +830,7 @@ public class ExcelUtil<T>
|
||||
style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
|
||||
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
|
||||
style.setFillForegroundColor(excel.backgroundColor().getIndex());
|
||||
style.setWrapText(excel.wrapText());
|
||||
Font dataFont = wb.createFont();
|
||||
dataFont.setFontName("Arial");
|
||||
dataFont.setFontHeightInPoints((short) 10);
|
||||
@@ -811,7 +859,7 @@ public class ExcelUtil<T>
|
||||
if (isSubList())
|
||||
{
|
||||
// 填充默认样式,防止合并单元格样式失效
|
||||
sheet.setDefaultColumnStyle(column, styles.get(StringUtils.format("data_{}_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor(), attr.cellType())));
|
||||
sheet.setDefaultColumnStyle(column, styles.get(StringUtils.format("data_{}_{}_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor(), attr.cellType(), attr.wrapText())));
|
||||
if (attr.needMerge())
|
||||
{
|
||||
sheet.addMergedRegion(new CellRangeAddress(rownum - 1, rownum, column, column));
|
||||
@@ -938,10 +986,12 @@ public class ExcelUtil<T>
|
||||
cell = row.createCell(column);
|
||||
if (isSubListValue(vo) && getListCellValue(vo).size() > 1 && attr.needMerge())
|
||||
{
|
||||
CellRangeAddress cellAddress = new CellRangeAddress(subMergedFirstRowNum, subMergedLastRowNum, column, column);
|
||||
sheet.addMergedRegion(cellAddress);
|
||||
if (subMergedLastRowNum >= subMergedFirstRowNum)
|
||||
{
|
||||
sheet.addMergedRegion(new CellRangeAddress(subMergedFirstRowNum, subMergedLastRowNum, column, column));
|
||||
}
|
||||
}
|
||||
cell.setCellStyle(styles.get(StringUtils.format("data_{}_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor(), attr.cellType())));
|
||||
cell.setCellStyle(styles.get(StringUtils.format("data_{}_{}_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor(), attr.cellType(), attr.wrapText())));
|
||||
|
||||
// 用于读取对象中的属性
|
||||
Object value = getTargetValue(vo, field, attr);
|
||||
@@ -950,6 +1000,7 @@ public class ExcelUtil<T>
|
||||
String separator = attr.separator();
|
||||
if (StringUtils.isNotEmpty(dateFormat) && StringUtils.isNotNull(value))
|
||||
{
|
||||
cell.getCellStyle().setDataFormat(this.wb.getCreationHelper().createDataFormat().getFormat(dateFormat));
|
||||
cell.setCellValue(parseDateToStr(dateFormat, value));
|
||||
}
|
||||
else if (StringUtils.isNotEmpty(readConverterExp) && StringUtils.isNotNull(value))
|
||||
@@ -1221,6 +1272,7 @@ public class ExcelUtil<T>
|
||||
*/
|
||||
private Object getTargetValue(T vo, Field field, Excel excel) throws Exception
|
||||
{
|
||||
field.setAccessible(true);
|
||||
Object o = field.get(vo);
|
||||
if (StringUtils.isNotEmpty(excel.targetAttr()))
|
||||
{
|
||||
@@ -1280,46 +1332,83 @@ public class ExcelUtil<T>
|
||||
List<Field> tempFields = new ArrayList<>();
|
||||
tempFields.addAll(Arrays.asList(clazz.getSuperclass().getDeclaredFields()));
|
||||
tempFields.addAll(Arrays.asList(clazz.getDeclaredFields()));
|
||||
for (Field field : tempFields)
|
||||
if (StringUtils.isNotEmpty(includeFields))
|
||||
{
|
||||
if (!ArrayUtils.contains(this.excludeFields, field.getName()))
|
||||
for (Field field : tempFields)
|
||||
{
|
||||
// 单注解
|
||||
if (field.isAnnotationPresent(Excel.class))
|
||||
if (ArrayUtils.contains(this.includeFields, field.getName()) || field.isAnnotationPresent(Excels.class))
|
||||
{
|
||||
Excel attr = field.getAnnotation(Excel.class);
|
||||
if (attr != null && (attr.type() == Type.ALL || attr.type() == type))
|
||||
addField(fields, field);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (StringUtils.isNotEmpty(excludeFields))
|
||||
{
|
||||
for (Field field : tempFields)
|
||||
{
|
||||
if (!ArrayUtils.contains(this.excludeFields, field.getName()))
|
||||
{
|
||||
addField(fields, field);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (Field field : tempFields)
|
||||
{
|
||||
addField(fields, field);
|
||||
}
|
||||
}
|
||||
return fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加字段信息
|
||||
*/
|
||||
public void addField(List<Object[]> fields, Field field)
|
||||
{
|
||||
// 单注解
|
||||
if (field.isAnnotationPresent(Excel.class))
|
||||
{
|
||||
Excel attr = field.getAnnotation(Excel.class);
|
||||
if (attr != null && (attr.type() == Type.ALL || attr.type() == type))
|
||||
{
|
||||
fields.add(new Object[] { field, attr });
|
||||
}
|
||||
if (Collection.class.isAssignableFrom(field.getType()))
|
||||
{
|
||||
subMethod = getSubMethod(field.getName(), clazz);
|
||||
ParameterizedType pt = (ParameterizedType) field.getGenericType();
|
||||
Class<?> subClass = (Class<?>) pt.getActualTypeArguments()[0];
|
||||
this.subFields = FieldUtils.getFieldsListWithAnnotation(subClass, Excel.class);
|
||||
}
|
||||
}
|
||||
|
||||
// 多注解
|
||||
if (field.isAnnotationPresent(Excels.class))
|
||||
{
|
||||
Excels attrs = field.getAnnotation(Excels.class);
|
||||
Excel[] excels = attrs.value();
|
||||
for (Excel attr : excels)
|
||||
{
|
||||
if (StringUtils.isNotEmpty(includeFields))
|
||||
{
|
||||
if (ArrayUtils.contains(this.includeFields, field.getName() + "." + attr.targetAttr())
|
||||
&& (attr != null && (attr.type() == Type.ALL || attr.type() == type)))
|
||||
{
|
||||
field.setAccessible(true);
|
||||
fields.add(new Object[] { field, attr });
|
||||
}
|
||||
if (Collection.class.isAssignableFrom(field.getType()))
|
||||
{
|
||||
subMethod = getSubMethod(field.getName(), clazz);
|
||||
ParameterizedType pt = (ParameterizedType) field.getGenericType();
|
||||
Class<?> subClass = (Class<?>) pt.getActualTypeArguments()[0];
|
||||
this.subFields = FieldUtils.getFieldsListWithAnnotation(subClass, Excel.class);
|
||||
}
|
||||
}
|
||||
|
||||
// 多注解
|
||||
if (field.isAnnotationPresent(Excels.class))
|
||||
else
|
||||
{
|
||||
Excels attrs = field.getAnnotation(Excels.class);
|
||||
Excel[] excels = attrs.value();
|
||||
for (Excel attr : excels)
|
||||
if (!ArrayUtils.contains(this.excludeFields, field.getName() + "." + attr.targetAttr())
|
||||
&& (attr != null && (attr.type() == Type.ALL || attr.type() == type)))
|
||||
{
|
||||
if (!ArrayUtils.contains(this.excludeFields, field.getName() + "." + attr.targetAttr())
|
||||
&& (attr != null && (attr.type() == Type.ALL || attr.type() == type)))
|
||||
{
|
||||
field.setAccessible(true);
|
||||
fields.add(new Object[] { field, attr });
|
||||
}
|
||||
fields.add(new Object[] { field, attr });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return fields;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -13,7 +13,7 @@ public class SqlUtil
|
||||
/**
|
||||
* 定义常用的 sql关键字
|
||||
*/
|
||||
public static String SQL_REGEX = "and |extractvalue|updatexml|exec |insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |or |+|user()";
|
||||
public static String SQL_REGEX = "\u000B|and |extractvalue|updatexml|sleep|exec |insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |or |union |like |+|/*|user()";
|
||||
|
||||
/**
|
||||
* 仅支持字母、数字、下划线、空格、逗号、小数点(支持多个字段排序)
|
||||
|
||||
@@ -37,7 +37,7 @@ public class TableDataInfo implements Serializable
|
||||
* @param list 列表数据
|
||||
* @param total 总记录数
|
||||
*/
|
||||
public TableDataInfo(List<?> list, int total)
|
||||
public TableDataInfo(List<?> list, long total)
|
||||
{
|
||||
this.rows = list;
|
||||
this.total = total;
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<artifactId>ruoyi-common</artifactId>
|
||||
<version>3.6.4</version>
|
||||
<version>3.6.5</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import org.aspectj.lang.JoinPoint;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Before;
|
||||
import org.springframework.stereotype.Component;
|
||||
import com.ruoyi.common.core.constant.UserConstants;
|
||||
import com.ruoyi.common.core.context.SecurityContextHolder;
|
||||
import com.ruoyi.common.core.text.Convert;
|
||||
import com.ruoyi.common.core.utils.StringUtils;
|
||||
@@ -73,8 +74,7 @@ public class DataScopeAspect
|
||||
if (StringUtils.isNotNull(currentUser) && !currentUser.isAdmin())
|
||||
{
|
||||
String permission = StringUtils.defaultIfEmpty(controllerDataScope.permission(), SecurityContextHolder.getPermission());
|
||||
dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptAlias(),
|
||||
controllerDataScope.userAlias(), permission);
|
||||
dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptAlias(), controllerDataScope.userAlias(), permission);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -92,16 +92,22 @@ public class DataScopeAspect
|
||||
{
|
||||
StringBuilder sqlString = new StringBuilder();
|
||||
List<String> conditions = new ArrayList<String>();
|
||||
List<String> scopeCustomIds = new ArrayList<String>();
|
||||
user.getRoles().forEach(role -> {
|
||||
if (DATA_SCOPE_CUSTOM.equals(role.getDataScope()) && StringUtils.equals(role.getStatus(), UserConstants.ROLE_NORMAL) && StringUtils.containsAny(role.getPermissions(), Convert.toStrArray(permission)))
|
||||
{
|
||||
scopeCustomIds.add(Convert.toStr(role.getRoleId()));
|
||||
}
|
||||
});
|
||||
|
||||
for (SysRole role : user.getRoles())
|
||||
{
|
||||
String dataScope = role.getDataScope();
|
||||
if (!DATA_SCOPE_CUSTOM.equals(dataScope) && conditions.contains(dataScope))
|
||||
if (conditions.contains(dataScope) || StringUtils.equals(role.getStatus(), UserConstants.ROLE_DISABLE))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (StringUtils.isNotEmpty(permission) && StringUtils.isNotEmpty(role.getPermissions())
|
||||
&& !StringUtils.containsAny(role.getPermissions(), Convert.toStrArray(permission)))
|
||||
if (!StringUtils.containsAny(role.getPermissions(), Convert.toStrArray(permission)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -113,9 +119,15 @@ public class DataScopeAspect
|
||||
}
|
||||
else if (DATA_SCOPE_CUSTOM.equals(dataScope))
|
||||
{
|
||||
sqlString.append(StringUtils.format(
|
||||
" OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ", deptAlias,
|
||||
role.getRoleId()));
|
||||
if (scopeCustomIds.size() > 1)
|
||||
{
|
||||
// 多个自定数据权限使用in查询,避免多次拼接。
|
||||
sqlString.append(StringUtils.format(" OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id in ({}) ) ", deptAlias, String.join(",", scopeCustomIds)));
|
||||
}
|
||||
else
|
||||
{
|
||||
sqlString.append(StringUtils.format(" OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ", deptAlias, role.getRoleId()));
|
||||
}
|
||||
}
|
||||
else if (DATA_SCOPE_DEPT.equals(dataScope))
|
||||
{
|
||||
@@ -123,9 +135,7 @@ public class DataScopeAspect
|
||||
}
|
||||
else if (DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope))
|
||||
{
|
||||
sqlString.append(StringUtils.format(
|
||||
" OR {}.dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} or find_in_set( {} , ancestors ) )",
|
||||
deptAlias, user.getDeptId(), user.getDeptId()));
|
||||
sqlString.append(StringUtils.format(" OR {}.dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} or find_in_set( {} , ancestors ) )", deptAlias, user.getDeptId(), user.getDeptId()));
|
||||
}
|
||||
else if (DATA_SCOPE_SELF.equals(dataScope))
|
||||
{
|
||||
@@ -142,7 +152,7 @@ public class DataScopeAspect
|
||||
conditions.add(dataScope);
|
||||
}
|
||||
|
||||
// 多角色情况下,所有角色都不包含传递过来的权限字符,这个时候sqlString也会为空,所以要限制一下,不查询任何数据
|
||||
// 角色都不包含传递过来的权限字符,这个时候sqlString也会为空,所以要限制一下,不查询任何数据
|
||||
if (StringUtils.isEmpty(conditions))
|
||||
{
|
||||
sqlString.append(StringUtils.format(" OR {}.dept_id = 0 ", deptAlias));
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<artifactId>ruoyi-common</artifactId>
|
||||
<version>3.6.4</version>
|
||||
<version>3.6.5</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<artifactId>ruoyi-common</artifactId>
|
||||
<version>3.6.4</version>
|
||||
<version>3.6.5</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -19,6 +19,8 @@ import org.springframework.stereotype.Component;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.ruoyi.common.core.text.Convert;
|
||||
import com.ruoyi.common.core.utils.ExceptionUtil;
|
||||
import com.ruoyi.common.core.utils.ServletUtils;
|
||||
import com.ruoyi.common.core.utils.StringUtils;
|
||||
import com.ruoyi.common.core.utils.ip.IpUtils;
|
||||
@@ -53,7 +55,7 @@ public class LogAspect
|
||||
* 处理请求前执行
|
||||
*/
|
||||
@Before(value = "@annotation(controllerLog)")
|
||||
public void boBefore(JoinPoint joinPoint, Log controllerLog)
|
||||
public void doBefore(JoinPoint joinPoint, Log controllerLog)
|
||||
{
|
||||
TIME_THREADLOCAL.set(System.currentTimeMillis());
|
||||
}
|
||||
@@ -101,7 +103,7 @@ public class LogAspect
|
||||
if (e != null)
|
||||
{
|
||||
operLog.setStatus(BusinessStatus.FAIL.ordinal());
|
||||
operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000));
|
||||
operLog.setErrorMsg(StringUtils.substring(Convert.toStr(e.getMessage(), ExceptionUtil.getExceptionMessage(e)), 0, 2000));
|
||||
}
|
||||
// 设置方法名称
|
||||
String className = joinPoint.getTarget().getClass().getName();
|
||||
@@ -164,10 +166,9 @@ public class LogAspect
|
||||
*/
|
||||
private void setRequestValue(JoinPoint joinPoint, SysOperLog operLog, String[] excludeParamNames) throws Exception
|
||||
{
|
||||
String requestMethod = operLog.getRequestMethod();
|
||||
Map<?, ?> paramsMap = ServletUtils.getParamMap(ServletUtils.getRequest());
|
||||
if (StringUtils.isEmpty(paramsMap)
|
||||
&& (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)))
|
||||
String requestMethod = operLog.getRequestMethod();
|
||||
if (StringUtils.isEmpty(paramsMap) && StringUtils.equalsAny(requestMethod, HttpMethod.PUT.name(), HttpMethod.POST.name(), HttpMethod.DELETE.name()))
|
||||
{
|
||||
String params = argsArrayToString(joinPoint.getArgs(), excludeParamNames);
|
||||
operLog.setOperParam(StringUtils.substring(params, 0, 2000));
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<artifactId>ruoyi-common</artifactId>
|
||||
<version>3.6.4</version>
|
||||
<version>3.6.5</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<artifactId>ruoyi-common</artifactId>
|
||||
<version>3.6.4</version>
|
||||
<version>3.6.5</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<artifactId>ruoyi-common</artifactId>
|
||||
<version>3.6.4</version>
|
||||
<version>3.6.5</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -1,13 +1,6 @@
|
||||
package com.ruoyi.common.security.handler;
|
||||
|
||||
import com.ruoyi.common.core.constant.HttpStatus;
|
||||
import com.ruoyi.common.core.exception.DemoModeException;
|
||||
import com.ruoyi.common.core.exception.InnerAuthException;
|
||||
import com.ruoyi.common.core.exception.ServiceException;
|
||||
import com.ruoyi.common.core.exception.auth.NotPermissionException;
|
||||
import com.ruoyi.common.core.exception.auth.NotRoleException;
|
||||
import com.ruoyi.common.core.utils.StringUtils;
|
||||
import com.ruoyi.common.core.web.domain.AjaxResult;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.validation.BindException;
|
||||
@@ -17,7 +10,16 @@ import org.springframework.web.bind.MissingPathVariableException;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import com.ruoyi.common.core.constant.HttpStatus;
|
||||
import com.ruoyi.common.core.exception.DemoModeException;
|
||||
import com.ruoyi.common.core.exception.InnerAuthException;
|
||||
import com.ruoyi.common.core.exception.ServiceException;
|
||||
import com.ruoyi.common.core.exception.auth.NotPermissionException;
|
||||
import com.ruoyi.common.core.exception.auth.NotRoleException;
|
||||
import com.ruoyi.common.core.text.Convert;
|
||||
import com.ruoyi.common.core.utils.StringUtils;
|
||||
import com.ruoyi.common.core.utils.html.EscapeUtil;
|
||||
import com.ruoyi.common.core.web.domain.AjaxResult;
|
||||
|
||||
/**
|
||||
* 全局异常处理器
|
||||
@@ -91,8 +93,13 @@ public class GlobalExceptionHandler
|
||||
public AjaxResult handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e, HttpServletRequest request)
|
||||
{
|
||||
String requestURI = request.getRequestURI();
|
||||
String value = Convert.toStr(e.getValue());
|
||||
if (StringUtils.isNotEmpty(value))
|
||||
{
|
||||
value = EscapeUtil.clean(value);
|
||||
}
|
||||
log.error("请求参数类型不匹配'{}',发生系统异常.", requestURI, e);
|
||||
return AjaxResult.error(String.format("请求参数类型不匹配,参数[%s]要求类型为:'%s',但输入值为:'%s'", e.getName(), e.getRequiredType().getName(), e.getValue()));
|
||||
return AjaxResult.error(String.format("请求参数类型不匹配,参数[%s]要求类型为:'%s',但输入值为:'%s'", e.getName(), e.getRequiredType().getName(), value));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -36,11 +36,11 @@ public class TokenService
|
||||
|
||||
protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND;
|
||||
|
||||
private final static long expireTime = CacheConstants.EXPIRATION;
|
||||
private final static long TOKEN_EXPIRE_TIME = CacheConstants.EXPIRATION;
|
||||
|
||||
private final static String ACCESS_TOKEN = CacheConstants.LOGIN_TOKEN_KEY;
|
||||
|
||||
private final static Long MILLIS_MINUTE_TEN = CacheConstants.REFRESH_TIME * MILLIS_MINUTE;
|
||||
private final static Long TOKEN_REFRESH_THRESHOLD_MINUTES = CacheConstants.REFRESH_TIME * MILLIS_MINUTE;
|
||||
|
||||
/**
|
||||
* 创建令牌
|
||||
@@ -65,7 +65,7 @@ public class TokenService
|
||||
// 接口返回信息
|
||||
Map<String, Object> rspMap = new HashMap<String, Object>();
|
||||
rspMap.put("access_token", JwtUtils.createToken(claimsMap));
|
||||
rspMap.put("expires_in", expireTime);
|
||||
rspMap.put("expires_in", TOKEN_EXPIRE_TIME);
|
||||
return rspMap;
|
||||
}
|
||||
|
||||
@@ -147,7 +147,7 @@ public class TokenService
|
||||
{
|
||||
long expireTime = loginUser.getExpireTime();
|
||||
long currentTime = System.currentTimeMillis();
|
||||
if (expireTime - currentTime <= MILLIS_MINUTE_TEN)
|
||||
if (expireTime - currentTime <= TOKEN_REFRESH_THRESHOLD_MINUTES)
|
||||
{
|
||||
refreshToken(loginUser);
|
||||
}
|
||||
@@ -161,10 +161,10 @@ public class TokenService
|
||||
public void refreshToken(LoginUser loginUser)
|
||||
{
|
||||
loginUser.setLoginTime(System.currentTimeMillis());
|
||||
loginUser.setExpireTime(loginUser.getLoginTime() + expireTime * MILLIS_MINUTE);
|
||||
loginUser.setExpireTime(loginUser.getLoginTime() + TOKEN_EXPIRE_TIME * MILLIS_MINUTE);
|
||||
// 根据uuid将loginUser缓存
|
||||
String userKey = getTokenKey(loginUser.getToken());
|
||||
redisService.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES);
|
||||
redisService.setCacheObject(userKey, loginUser, TOKEN_EXPIRE_TIME, TimeUnit.MINUTES);
|
||||
}
|
||||
|
||||
private String getTokenKey(String token)
|
||||
|
||||
@@ -62,7 +62,7 @@ public class SecurityUtils
|
||||
public static String getToken(HttpServletRequest request)
|
||||
{
|
||||
// 从header获取token标识
|
||||
String token = request.getHeader(TokenConstants.AUTHENTICATION);
|
||||
String token = request.getHeader(SecurityConstants.AUTHORIZATION_HEADER);
|
||||
return replaceTokenPrefix(token);
|
||||
}
|
||||
|
||||
|
||||
27
ruoyi-common/ruoyi-common-sensitive/pom.xml
Normal file
27
ruoyi-common/ruoyi-common-sensitive/pom.xml
Normal file
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<artifactId>ruoyi-common</artifactId>
|
||||
<version>3.6.5</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>ruoyi-common-sensitive</artifactId>
|
||||
|
||||
<description>
|
||||
ruoyi-common-sensitive数据脱敏
|
||||
</description>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- RuoYi Common Core -->
|
||||
<dependency>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<artifactId>ruoyi-common-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.ruoyi.common.sensitive.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import com.ruoyi.common.sensitive.config.SensitiveJsonSerializer;
|
||||
import com.ruoyi.common.sensitive.enums.DesensitizedType;
|
||||
|
||||
/**
|
||||
* 数据脱敏注解
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
@JacksonAnnotationsInside
|
||||
@JsonSerialize(using = SensitiveJsonSerializer.class)
|
||||
public @interface Sensitive
|
||||
{
|
||||
DesensitizedType desensitizedType();
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.ruoyi.common.sensitive.config;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.databind.BeanProperty;
|
||||
import com.fasterxml.jackson.databind.JsonMappingException;
|
||||
import com.fasterxml.jackson.databind.JsonSerializer;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
|
||||
import com.ruoyi.common.core.constant.UserConstants;
|
||||
import com.ruoyi.common.core.context.SecurityContextHolder;
|
||||
import com.ruoyi.common.sensitive.annotation.Sensitive;
|
||||
import com.ruoyi.common.sensitive.enums.DesensitizedType;
|
||||
|
||||
/**
|
||||
* 数据脱敏序列化过滤
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class SensitiveJsonSerializer extends JsonSerializer<String> implements ContextualSerializer
|
||||
{
|
||||
private DesensitizedType desensitizedType;
|
||||
|
||||
@Override
|
||||
public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException
|
||||
{
|
||||
if (desensitization())
|
||||
{
|
||||
gen.writeString(desensitizedType.desensitizer().apply(value));
|
||||
}
|
||||
else
|
||||
{
|
||||
gen.writeString(value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property)
|
||||
throws JsonMappingException
|
||||
{
|
||||
Sensitive annotation = property.getAnnotation(Sensitive.class);
|
||||
if (Objects.nonNull(annotation) && Objects.equals(String.class, property.getType().getRawClass()))
|
||||
{
|
||||
this.desensitizedType = annotation.desensitizedType();
|
||||
return this;
|
||||
}
|
||||
return prov.findValueSerializer(property.getType(), property);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否需要脱敏处理
|
||||
*/
|
||||
private boolean desensitization()
|
||||
{
|
||||
try
|
||||
{
|
||||
Long userId = SecurityContextHolder.getUserId();
|
||||
// 管理员不脱敏
|
||||
return !UserConstants.isAdmin(userId);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.ruoyi.common.sensitive.enums;
|
||||
|
||||
import java.util.function.Function;
|
||||
import com.ruoyi.common.sensitive.utils.DesensitizedUtil;
|
||||
|
||||
/**
|
||||
* 脱敏类型
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public enum DesensitizedType
|
||||
{
|
||||
/**
|
||||
* 姓名,第2位星号替换
|
||||
*/
|
||||
USERNAME(s -> s.replaceAll("(\\S)\\S(\\S*)", "$1*$2")),
|
||||
|
||||
/**
|
||||
* 密码,全部字符都用*代替
|
||||
*/
|
||||
PASSWORD(DesensitizedUtil::password),
|
||||
|
||||
/**
|
||||
* 身份证,中间10位星号替换
|
||||
*/
|
||||
ID_CARD(s -> s.replaceAll("(\\d{4})\\d{10}(\\d{3}[Xx]|\\d{4})", "$1** **** ****$2")),
|
||||
|
||||
/**
|
||||
* 手机号,中间4位星号替换
|
||||
*/
|
||||
PHONE(s -> s.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2")),
|
||||
|
||||
/**
|
||||
* 电子邮箱,仅显示第一个字母和@后面的地址显示,其他星号替换
|
||||
*/
|
||||
EMAIL(s -> s.replaceAll("(^.)[^@]*(@.*$)", "$1****$2")),
|
||||
|
||||
/**
|
||||
* 银行卡号,保留最后4位,其他星号替换
|
||||
*/
|
||||
BANK_CARD(s -> s.replaceAll("\\d{15}(\\d{3})", "**** **** **** **** $1")),
|
||||
|
||||
/**
|
||||
* 车牌号码,包含普通车辆、新能源车辆
|
||||
*/
|
||||
CAR_LICENSE(DesensitizedUtil::carLicense);
|
||||
|
||||
private final Function<String, String> desensitizer;
|
||||
|
||||
DesensitizedType(Function<String, String> desensitizer)
|
||||
{
|
||||
this.desensitizer = desensitizer;
|
||||
}
|
||||
|
||||
public Function<String, String> desensitizer()
|
||||
{
|
||||
return desensitizer;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.ruoyi.common.sensitive.utils;
|
||||
|
||||
import com.ruoyi.common.core.utils.StringUtils;
|
||||
|
||||
/**
|
||||
* 脱敏工具类
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class DesensitizedUtil
|
||||
{
|
||||
/**
|
||||
* 密码的全部字符都用*代替,比如:******
|
||||
*
|
||||
* @param password 密码
|
||||
* @return 脱敏后的密码
|
||||
*/
|
||||
public static String password(String password)
|
||||
{
|
||||
if (StringUtils.isBlank(password))
|
||||
{
|
||||
return StringUtils.EMPTY;
|
||||
}
|
||||
return StringUtils.repeat('*', password.length());
|
||||
}
|
||||
|
||||
/**
|
||||
* 车牌中间用*代替,如果是错误的车牌,不处理
|
||||
*
|
||||
* @param carLicense 完整的车牌号
|
||||
* @return 脱敏后的车牌
|
||||
*/
|
||||
public static String carLicense(String carLicense)
|
||||
{
|
||||
if (StringUtils.isBlank(carLicense))
|
||||
{
|
||||
return StringUtils.EMPTY;
|
||||
}
|
||||
// 普通车牌
|
||||
if (carLicense.length() == 7)
|
||||
{
|
||||
carLicense = StringUtils.hide(carLicense, 3, 6);
|
||||
}
|
||||
else if (carLicense.length() == 8)
|
||||
{
|
||||
// 新能源车牌
|
||||
carLicense = StringUtils.hide(carLicense, 3, 7);
|
||||
}
|
||||
return carLicense;
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<artifactId>ruoyi-common</artifactId>
|
||||
<version>3.6.4</version>
|
||||
<version>3.6.5</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@@ -23,11 +23,10 @@
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Swagger -->
|
||||
<!-- SpringDoc webmvc -->
|
||||
<dependency>
|
||||
<groupId>io.springfox</groupId>
|
||||
<artifactId>springfox-swagger2</artifactId>
|
||||
<version>${swagger.fox.version}</version>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-ui</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
package com.ruoyi.common.swagger.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import com.ruoyi.common.swagger.config.SwaggerAutoConfiguration;
|
||||
|
||||
@Target({ ElementType.TYPE })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Inherited
|
||||
@Import({ SwaggerAutoConfiguration.class })
|
||||
public @interface EnableCustomSwagger2
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.ruoyi.common.swagger.config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import com.ruoyi.common.swagger.config.properties.SpringDocProperties;
|
||||
import io.swagger.v3.oas.models.Components;
|
||||
import io.swagger.v3.oas.models.OpenAPI;
|
||||
import io.swagger.v3.oas.models.info.Info;
|
||||
import io.swagger.v3.oas.models.security.SecurityRequirement;
|
||||
import io.swagger.v3.oas.models.security.SecurityScheme;
|
||||
import io.swagger.v3.oas.models.servers.Server;
|
||||
|
||||
/**
|
||||
* Swagger 文档配置
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@EnableConfigurationProperties(SpringDocProperties.class)
|
||||
@ConditionalOnProperty(name = "springdoc.api-docs.enabled", havingValue = "true", matchIfMissing = true)
|
||||
public class SpringDocAutoConfiguration
|
||||
{
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(OpenAPI.class)
|
||||
public OpenAPI openApi(SpringDocProperties properties)
|
||||
{
|
||||
return new OpenAPI().components(new Components()
|
||||
// 设置认证的请求头
|
||||
.addSecuritySchemes("apikey", securityScheme()))
|
||||
.addSecurityItem(new SecurityRequirement().addList("apikey"))
|
||||
.info(convertInfo(properties.getInfo()))
|
||||
.servers(servers(properties.getGatewayUrl()));
|
||||
}
|
||||
|
||||
public SecurityScheme securityScheme()
|
||||
{
|
||||
return new SecurityScheme().type(SecurityScheme.Type.APIKEY)
|
||||
.name("Authorization")
|
||||
.in(SecurityScheme.In.HEADER)
|
||||
.scheme("Bearer");
|
||||
}
|
||||
|
||||
private Info convertInfo(SpringDocProperties.InfoProperties infoProperties)
|
||||
{
|
||||
Info info = new Info();
|
||||
info.setTitle(infoProperties.getTitle());
|
||||
info.setDescription(infoProperties.getDescription());
|
||||
info.setContact(infoProperties.getContact());
|
||||
info.setLicense(infoProperties.getLicense());
|
||||
info.setVersion(infoProperties.getVersion());
|
||||
return info;
|
||||
}
|
||||
|
||||
public List<Server> servers(String gatewayUrl)
|
||||
{
|
||||
List<Server> serverList = new ArrayList<>();
|
||||
serverList.add(new Server().url(gatewayUrl));
|
||||
return serverList;
|
||||
}
|
||||
}
|
||||
@@ -1,123 +0,0 @@
|
||||
package com.ruoyi.common.swagger.config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import springfox.documentation.builders.ApiInfoBuilder;
|
||||
import springfox.documentation.builders.PathSelectors;
|
||||
import springfox.documentation.builders.RequestHandlerSelectors;
|
||||
import springfox.documentation.service.ApiInfo;
|
||||
import springfox.documentation.service.ApiKey;
|
||||
import springfox.documentation.service.AuthorizationScope;
|
||||
import springfox.documentation.service.Contact;
|
||||
import springfox.documentation.service.SecurityReference;
|
||||
import springfox.documentation.service.SecurityScheme;
|
||||
import springfox.documentation.spi.DocumentationType;
|
||||
import springfox.documentation.spi.service.contexts.SecurityContext;
|
||||
import springfox.documentation.spring.web.plugins.ApiSelectorBuilder;
|
||||
import springfox.documentation.spring.web.plugins.Docket;
|
||||
import springfox.documentation.swagger2.annotations.EnableSwagger2;
|
||||
|
||||
@Configuration
|
||||
@EnableSwagger2
|
||||
@EnableConfigurationProperties(SwaggerProperties.class)
|
||||
@ConditionalOnProperty(name = "swagger.enabled", matchIfMissing = true)
|
||||
@Import({SwaggerBeanPostProcessor.class, SwaggerWebConfiguration.class})
|
||||
public class SwaggerAutoConfiguration
|
||||
{
|
||||
/**
|
||||
* 默认的排除路径,排除Spring Boot默认的错误处理路径和端点
|
||||
*/
|
||||
private static final List<String> DEFAULT_EXCLUDE_PATH = Arrays.asList("/error", "/actuator/**");
|
||||
|
||||
private static final String BASE_PATH = "/**";
|
||||
|
||||
@Bean
|
||||
public Docket api(SwaggerProperties swaggerProperties)
|
||||
{
|
||||
// base-path处理
|
||||
if (swaggerProperties.getBasePath().isEmpty())
|
||||
{
|
||||
swaggerProperties.getBasePath().add(BASE_PATH);
|
||||
}
|
||||
// noinspection unchecked
|
||||
List<Predicate<String>> basePath = new ArrayList<Predicate<String>>();
|
||||
swaggerProperties.getBasePath().forEach(path -> basePath.add(PathSelectors.ant(path)));
|
||||
|
||||
// exclude-path处理
|
||||
if (swaggerProperties.getExcludePath().isEmpty())
|
||||
{
|
||||
swaggerProperties.getExcludePath().addAll(DEFAULT_EXCLUDE_PATH);
|
||||
}
|
||||
|
||||
List<Predicate<String>> excludePath = new ArrayList<>();
|
||||
swaggerProperties.getExcludePath().forEach(path -> excludePath.add(PathSelectors.ant(path)));
|
||||
|
||||
ApiSelectorBuilder builder = new Docket(DocumentationType.SWAGGER_2).host(swaggerProperties.getHost())
|
||||
.apiInfo(apiInfo(swaggerProperties)).select()
|
||||
.apis(RequestHandlerSelectors.basePackage(swaggerProperties.getBasePackage()));
|
||||
|
||||
swaggerProperties.getBasePath().forEach(p -> builder.paths(PathSelectors.ant(p)));
|
||||
swaggerProperties.getExcludePath().forEach(p -> builder.paths(PathSelectors.ant(p).negate()));
|
||||
|
||||
return builder.build().securitySchemes(securitySchemes()).securityContexts(securityContexts()).pathMapping("/");
|
||||
}
|
||||
|
||||
/**
|
||||
* 安全模式,这里指定token通过Authorization头请求头传递
|
||||
*/
|
||||
private List<SecurityScheme> securitySchemes()
|
||||
{
|
||||
List<SecurityScheme> apiKeyList = new ArrayList<SecurityScheme>();
|
||||
apiKeyList.add(new ApiKey("Authorization", "Authorization", "header"));
|
||||
return apiKeyList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 安全上下文
|
||||
*/
|
||||
private List<SecurityContext> securityContexts()
|
||||
{
|
||||
List<SecurityContext> securityContexts = new ArrayList<>();
|
||||
securityContexts.add(
|
||||
SecurityContext.builder()
|
||||
.securityReferences(defaultAuth())
|
||||
.operationSelector(o -> o.requestMappingPattern().matches("/.*"))
|
||||
.build());
|
||||
return securityContexts;
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认的全局鉴权策略
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private List<SecurityReference> defaultAuth()
|
||||
{
|
||||
AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
|
||||
AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
|
||||
authorizationScopes[0] = authorizationScope;
|
||||
List<SecurityReference> securityReferences = new ArrayList<>();
|
||||
securityReferences.add(new SecurityReference("Authorization", authorizationScopes));
|
||||
return securityReferences;
|
||||
}
|
||||
|
||||
private ApiInfo apiInfo(SwaggerProperties swaggerProperties)
|
||||
{
|
||||
return new ApiInfoBuilder()
|
||||
.title(swaggerProperties.getTitle())
|
||||
.description(swaggerProperties.getDescription())
|
||||
.license(swaggerProperties.getLicense())
|
||||
.licenseUrl(swaggerProperties.getLicenseUrl())
|
||||
.termsOfServiceUrl(swaggerProperties.getTermsOfServiceUrl())
|
||||
.contact(new Contact(swaggerProperties.getContact().getName(), swaggerProperties.getContact().getUrl(), swaggerProperties.getContact().getEmail()))
|
||||
.version(swaggerProperties.getVersion())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
package com.ruoyi.common.swagger.config;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping;
|
||||
import springfox.documentation.spring.web.plugins.WebFluxRequestHandlerProvider;
|
||||
import springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* swagger 在 springboot 2.6.x 不兼容问题的处理
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class SwaggerBeanPostProcessor implements BeanPostProcessor
|
||||
{
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException
|
||||
{
|
||||
if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider)
|
||||
{
|
||||
customizeSpringfoxHandlerMappings(getHandlerMappings(bean));
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
private <T extends RequestMappingInfoHandlerMapping> void customizeSpringfoxHandlerMappings(List<T> mappings)
|
||||
{
|
||||
List<T> copy = mappings.stream().filter(mapping -> mapping.getPatternParser() == null)
|
||||
.collect(Collectors.toList());
|
||||
mappings.clear();
|
||||
mappings.addAll(copy);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private List<RequestMappingInfoHandlerMapping> getHandlerMappings(Object bean)
|
||||
{
|
||||
try
|
||||
{
|
||||
Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings");
|
||||
field.setAccessible(true);
|
||||
return (List<RequestMappingInfoHandlerMapping>) field.get(bean);
|
||||
}
|
||||
catch (IllegalArgumentException | IllegalAccessException e)
|
||||
{
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,343 +0,0 @@
|
||||
package com.ruoyi.common.swagger.config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
@ConfigurationProperties("swagger")
|
||||
public class SwaggerProperties
|
||||
{
|
||||
/**
|
||||
* 是否开启swagger
|
||||
*/
|
||||
private Boolean enabled;
|
||||
|
||||
/**
|
||||
* swagger会解析的包路径
|
||||
**/
|
||||
private String basePackage = "";
|
||||
|
||||
/**
|
||||
* swagger会解析的url规则
|
||||
**/
|
||||
private List<String> basePath = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 在basePath基础上需要排除的url规则
|
||||
**/
|
||||
private List<String> excludePath = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 标题
|
||||
**/
|
||||
private String title = "";
|
||||
|
||||
/**
|
||||
* 描述
|
||||
**/
|
||||
private String description = "";
|
||||
|
||||
/**
|
||||
* 版本
|
||||
**/
|
||||
private String version = "";
|
||||
|
||||
/**
|
||||
* 许可证
|
||||
**/
|
||||
private String license = "";
|
||||
|
||||
/**
|
||||
* 许可证URL
|
||||
**/
|
||||
private String licenseUrl = "";
|
||||
|
||||
/**
|
||||
* 服务条款URL
|
||||
**/
|
||||
private String termsOfServiceUrl = "";
|
||||
|
||||
/**
|
||||
* host信息
|
||||
**/
|
||||
private String host = "";
|
||||
|
||||
/**
|
||||
* 联系人信息
|
||||
*/
|
||||
private Contact contact = new Contact();
|
||||
|
||||
/**
|
||||
* 全局统一鉴权配置
|
||||
**/
|
||||
private Authorization authorization = new Authorization();
|
||||
|
||||
public Boolean getEnabled()
|
||||
{
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(Boolean enabled)
|
||||
{
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public String getBasePackage()
|
||||
{
|
||||
return basePackage;
|
||||
}
|
||||
|
||||
public void setBasePackage(String basePackage)
|
||||
{
|
||||
this.basePackage = basePackage;
|
||||
}
|
||||
|
||||
public List<String> getBasePath()
|
||||
{
|
||||
return basePath;
|
||||
}
|
||||
|
||||
public void setBasePath(List<String> basePath)
|
||||
{
|
||||
this.basePath = basePath;
|
||||
}
|
||||
|
||||
public List<String> getExcludePath()
|
||||
{
|
||||
return excludePath;
|
||||
}
|
||||
|
||||
public void setExcludePath(List<String> excludePath)
|
||||
{
|
||||
this.excludePath = excludePath;
|
||||
}
|
||||
|
||||
public String getTitle()
|
||||
{
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title)
|
||||
{
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public String getDescription()
|
||||
{
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description)
|
||||
{
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getVersion()
|
||||
{
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(String version)
|
||||
{
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public String getLicense()
|
||||
{
|
||||
return license;
|
||||
}
|
||||
|
||||
public void setLicense(String license)
|
||||
{
|
||||
this.license = license;
|
||||
}
|
||||
|
||||
public String getLicenseUrl()
|
||||
{
|
||||
return licenseUrl;
|
||||
}
|
||||
|
||||
public void setLicenseUrl(String licenseUrl)
|
||||
{
|
||||
this.licenseUrl = licenseUrl;
|
||||
}
|
||||
|
||||
public String getTermsOfServiceUrl()
|
||||
{
|
||||
return termsOfServiceUrl;
|
||||
}
|
||||
|
||||
public void setTermsOfServiceUrl(String termsOfServiceUrl)
|
||||
{
|
||||
this.termsOfServiceUrl = termsOfServiceUrl;
|
||||
}
|
||||
|
||||
public String getHost()
|
||||
{
|
||||
return host;
|
||||
}
|
||||
|
||||
public void setHost(String host)
|
||||
{
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
public Contact getContact()
|
||||
{
|
||||
return contact;
|
||||
}
|
||||
|
||||
public void setContact(Contact contact)
|
||||
{
|
||||
this.contact = contact;
|
||||
}
|
||||
|
||||
public Authorization getAuthorization()
|
||||
{
|
||||
return authorization;
|
||||
}
|
||||
|
||||
public void setAuthorization(Authorization authorization)
|
||||
{
|
||||
this.authorization = authorization;
|
||||
}
|
||||
|
||||
public static class Contact
|
||||
{
|
||||
/**
|
||||
* 联系人
|
||||
**/
|
||||
private String name = "";
|
||||
/**
|
||||
* 联系人url
|
||||
**/
|
||||
private String url = "";
|
||||
/**
|
||||
* 联系人email
|
||||
**/
|
||||
private String email = "";
|
||||
|
||||
public String getName()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name)
|
||||
{
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getUrl()
|
||||
{
|
||||
return url;
|
||||
}
|
||||
|
||||
public void setUrl(String url)
|
||||
{
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public String getEmail()
|
||||
{
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email)
|
||||
{
|
||||
this.email = email;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Authorization
|
||||
{
|
||||
/**
|
||||
* 鉴权策略ID,需要和SecurityReferences ID保持一致
|
||||
*/
|
||||
private String name = "";
|
||||
|
||||
/**
|
||||
* 需要开启鉴权URL的正则
|
||||
*/
|
||||
private String authRegex = "^.*$";
|
||||
|
||||
/**
|
||||
* 鉴权作用域列表
|
||||
*/
|
||||
private List<AuthorizationScope> authorizationScopeList = new ArrayList<>();
|
||||
|
||||
private List<String> tokenUrlList = new ArrayList<>();
|
||||
|
||||
public String getName()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name)
|
||||
{
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getAuthRegex()
|
||||
{
|
||||
return authRegex;
|
||||
}
|
||||
|
||||
public void setAuthRegex(String authRegex)
|
||||
{
|
||||
this.authRegex = authRegex;
|
||||
}
|
||||
|
||||
public List<AuthorizationScope> getAuthorizationScopeList()
|
||||
{
|
||||
return authorizationScopeList;
|
||||
}
|
||||
|
||||
public void setAuthorizationScopeList(List<AuthorizationScope> authorizationScopeList)
|
||||
{
|
||||
this.authorizationScopeList = authorizationScopeList;
|
||||
}
|
||||
|
||||
public List<String> getTokenUrlList()
|
||||
{
|
||||
return tokenUrlList;
|
||||
}
|
||||
|
||||
public void setTokenUrlList(List<String> tokenUrlList)
|
||||
{
|
||||
this.tokenUrlList = tokenUrlList;
|
||||
}
|
||||
}
|
||||
|
||||
public static class AuthorizationScope
|
||||
{
|
||||
/**
|
||||
* 作用域名称
|
||||
*/
|
||||
private String scope = "";
|
||||
|
||||
/**
|
||||
* 作用域描述
|
||||
*/
|
||||
private String description = "";
|
||||
|
||||
public String getScope()
|
||||
{
|
||||
return scope;
|
||||
}
|
||||
|
||||
public void setScope(String scope)
|
||||
{
|
||||
this.scope = scope;
|
||||
}
|
||||
|
||||
public String getDescription()
|
||||
{
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description)
|
||||
{
|
||||
this.description = description;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package com.ruoyi.common.swagger.config;
|
||||
|
||||
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
/**
|
||||
* swagger 资源映射路径
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class SwaggerWebConfiguration implements WebMvcConfigurer
|
||||
{
|
||||
@Override
|
||||
public void addResourceHandlers(ResourceHandlerRegistry registry)
|
||||
{
|
||||
/** swagger-ui 地址 */
|
||||
registry.addResourceHandler("/swagger-ui/**")
|
||||
.addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
package com.ruoyi.common.swagger.config.properties;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.NestedConfigurationProperty;
|
||||
import io.swagger.v3.oas.models.info.Contact;
|
||||
import io.swagger.v3.oas.models.info.License;
|
||||
|
||||
/**
|
||||
* Swagger 配置属性
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "springdoc")
|
||||
public class SpringDocProperties
|
||||
{
|
||||
/**
|
||||
* 网关
|
||||
*/
|
||||
private String gatewayUrl;
|
||||
|
||||
/**
|
||||
* 文档基本信息
|
||||
*/
|
||||
@NestedConfigurationProperty
|
||||
private InfoProperties info = new InfoProperties();
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 文档的基础属性信息
|
||||
* </p>
|
||||
*
|
||||
* @see io.swagger.v3.oas.models.info.Info
|
||||
*
|
||||
* 为了 springboot 自动生产配置提示信息,所以这里复制一个类出来
|
||||
*/
|
||||
public static class InfoProperties
|
||||
{
|
||||
/**
|
||||
* 标题
|
||||
*/
|
||||
private String title = null;
|
||||
|
||||
/**
|
||||
* 描述
|
||||
*/
|
||||
private String description = null;
|
||||
|
||||
/**
|
||||
* 联系人信息
|
||||
*/
|
||||
@NestedConfigurationProperty
|
||||
private Contact contact = null;
|
||||
|
||||
/**
|
||||
* 许可证
|
||||
*/
|
||||
@NestedConfigurationProperty
|
||||
private License license = null;
|
||||
|
||||
/**
|
||||
* 版本
|
||||
*/
|
||||
private String version = null;
|
||||
|
||||
public String getTitle()
|
||||
{
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title)
|
||||
{
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public String getDescription()
|
||||
{
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description)
|
||||
{
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public Contact getContact()
|
||||
{
|
||||
return contact;
|
||||
}
|
||||
|
||||
public void setContact(Contact contact)
|
||||
{
|
||||
this.contact = contact;
|
||||
}
|
||||
|
||||
public License getLicense()
|
||||
{
|
||||
return license;
|
||||
}
|
||||
|
||||
public void setLicense(License license)
|
||||
{
|
||||
this.license = license;
|
||||
}
|
||||
|
||||
public String getVersion()
|
||||
{
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(String version)
|
||||
{
|
||||
this.version = version;
|
||||
}
|
||||
}
|
||||
|
||||
public String getGatewayUrl()
|
||||
{
|
||||
return gatewayUrl;
|
||||
}
|
||||
|
||||
public void setGatewayUrl(String gatewayUrl)
|
||||
{
|
||||
this.gatewayUrl = gatewayUrl;
|
||||
}
|
||||
|
||||
public InfoProperties getInfo()
|
||||
{
|
||||
return info;
|
||||
}
|
||||
|
||||
public void setInfo(InfoProperties info)
|
||||
{
|
||||
this.info = info;
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1 @@
|
||||
# com.ruoyi.common.swagger.config.SwaggerAutoConfiguration
|
||||
# com.ruoyi.common.swagger.config.SwaggerWebConfiguration
|
||||
# com.ruoyi.common.swagger.config.SwaggerBeanPostProcessor
|
||||
com.ruoyi.common.swagger.config.SpringDocAutoConfiguration
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<artifactId>ruoyi</artifactId>
|
||||
<version>3.6.4</version>
|
||||
<version>3.6.5</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@@ -76,16 +76,11 @@
|
||||
<artifactId>ruoyi-common-redis</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Swagger -->
|
||||
<!-- Springdoc -->
|
||||
<dependency>
|
||||
<groupId>io.springfox</groupId>
|
||||
<artifactId>springfox-swagger-ui</artifactId>
|
||||
<version>${swagger.fox.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.springfox</groupId>
|
||||
<artifactId>springfox-swagger2</artifactId>
|
||||
<version>${swagger.fox.version}</version>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-webflux-ui</artifactId>
|
||||
<version>${springdoc.version}</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
package com.ruoyi.gateway.config;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import org.springdoc.core.AbstractSwaggerUiConfigProperties;
|
||||
import org.springdoc.core.SwaggerUiConfigProperties;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.cloud.client.discovery.DiscoveryClient;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import com.alibaba.nacos.client.naming.event.InstancesChangeEvent;
|
||||
import com.alibaba.nacos.common.notify.Event;
|
||||
import com.alibaba.nacos.common.notify.NotifyCenter;
|
||||
import com.alibaba.nacos.common.notify.listener.Subscriber;
|
||||
import com.ruoyi.common.core.utils.StringUtils;
|
||||
|
||||
/**
|
||||
* SpringDoc配置类
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnProperty(value = "springdoc.api-docs.enabled", matchIfMissing = true)
|
||||
public class SpringDocConfig implements InitializingBean
|
||||
{
|
||||
@Autowired
|
||||
private SwaggerUiConfigProperties swaggerUiConfigProperties;
|
||||
|
||||
@Autowired
|
||||
private DiscoveryClient discoveryClient;
|
||||
|
||||
/**
|
||||
* 在初始化后调用的方法
|
||||
*/
|
||||
@Override
|
||||
public void afterPropertiesSet()
|
||||
{
|
||||
NotifyCenter.registerSubscriber(new SwaggerDocRegister(swaggerUiConfigProperties, discoveryClient));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Swagger文档注册器
|
||||
*/
|
||||
class SwaggerDocRegister extends Subscriber<InstancesChangeEvent>
|
||||
{
|
||||
@Autowired
|
||||
private SwaggerUiConfigProperties swaggerUiConfigProperties;
|
||||
|
||||
@Autowired
|
||||
private DiscoveryClient discoveryClient;
|
||||
|
||||
private final static String[] EXCLUDE_ROUTES = new String[] { "ruoyi-gateway", "ruoyi-auth", "ruoyi-file", "ruoyi-monitor" };
|
||||
|
||||
public SwaggerDocRegister(SwaggerUiConfigProperties swaggerUiConfigProperties, DiscoveryClient discoveryClient)
|
||||
{
|
||||
this.swaggerUiConfigProperties = swaggerUiConfigProperties;
|
||||
this.discoveryClient = discoveryClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* 事件回调方法,处理InstancesChangeEvent事件
|
||||
* @param event 事件对象
|
||||
*/
|
||||
@Override
|
||||
public void onEvent(InstancesChangeEvent event)
|
||||
{
|
||||
Set<AbstractSwaggerUiConfigProperties.SwaggerUrl> swaggerUrlSet = discoveryClient.getServices()
|
||||
.stream()
|
||||
.flatMap(serviceId -> discoveryClient.getInstances(serviceId).stream())
|
||||
.filter(instance -> !StringUtils.equalsAnyIgnoreCase(instance.getServiceId(), EXCLUDE_ROUTES))
|
||||
.map(instance -> {
|
||||
AbstractSwaggerUiConfigProperties.SwaggerUrl swaggerUrl = new AbstractSwaggerUiConfigProperties.SwaggerUrl();
|
||||
swaggerUrl.setName(instance.getServiceId());
|
||||
swaggerUrl.setUrl(String.format("/%s/v3/api-docs", instance.getServiceId()));
|
||||
return swaggerUrl;
|
||||
})
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
swaggerUiConfigProperties.setUrls(swaggerUrlSet);
|
||||
}
|
||||
|
||||
/**
|
||||
* 订阅类型方法,返回订阅的事件类型
|
||||
* @return 订阅的事件类型
|
||||
*/
|
||||
@Override
|
||||
public Class<? extends Event> subscribeType()
|
||||
{
|
||||
return InstancesChangeEvent.class;
|
||||
}
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
package com.ruoyi.gateway.config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cloud.gateway.config.GatewayProperties;
|
||||
import org.springframework.cloud.gateway.route.RouteLocator;
|
||||
import org.springframework.cloud.gateway.support.NameUtils;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.reactive.config.ResourceHandlerRegistry;
|
||||
import org.springframework.web.reactive.config.WebFluxConfigurer;
|
||||
import springfox.documentation.swagger.web.SwaggerResource;
|
||||
import springfox.documentation.swagger.web.SwaggerResourcesProvider;
|
||||
|
||||
/**
|
||||
* 聚合系统接口
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Component
|
||||
public class SwaggerProvider implements SwaggerResourcesProvider, WebFluxConfigurer
|
||||
{
|
||||
/**
|
||||
* Swagger2默认的url后缀
|
||||
*/
|
||||
public static final String SWAGGER2URL = "/v2/api-docs";
|
||||
|
||||
/**
|
||||
* 网关路由
|
||||
*/
|
||||
@Lazy
|
||||
@Autowired
|
||||
private RouteLocator routeLocator;
|
||||
|
||||
@Autowired
|
||||
private GatewayProperties gatewayProperties;
|
||||
|
||||
/**
|
||||
* 聚合其他服务接口
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public List<SwaggerResource> get()
|
||||
{
|
||||
List<SwaggerResource> resourceList = new ArrayList<>();
|
||||
List<String> routes = new ArrayList<>();
|
||||
// 获取网关中配置的route
|
||||
routeLocator.getRoutes().subscribe(route -> routes.add(route.getId()));
|
||||
gatewayProperties.getRoutes().stream()
|
||||
.filter(routeDefinition -> routes
|
||||
.contains(routeDefinition.getId()))
|
||||
.forEach(routeDefinition -> routeDefinition.getPredicates().stream()
|
||||
.filter(predicateDefinition -> "Path".equalsIgnoreCase(predicateDefinition.getName()))
|
||||
.filter(predicateDefinition -> !"ruoyi-auth".equalsIgnoreCase(routeDefinition.getId()))
|
||||
.forEach(predicateDefinition -> resourceList
|
||||
.add(swaggerResource(routeDefinition.getId(), predicateDefinition.getArgs()
|
||||
.get(NameUtils.GENERATED_NAME_PREFIX + "0").replace("/**", SWAGGER2URL)))));
|
||||
return resourceList;
|
||||
}
|
||||
|
||||
private SwaggerResource swaggerResource(String name, String location)
|
||||
{
|
||||
SwaggerResource swaggerResource = new SwaggerResource();
|
||||
swaggerResource.setName(name);
|
||||
swaggerResource.setLocation(location);
|
||||
swaggerResource.setSwaggerVersion("2.0");
|
||||
return swaggerResource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addResourceHandlers(ResourceHandlerRegistry registry)
|
||||
{
|
||||
/** swagger-ui 地址 */
|
||||
registry.addResourceHandler("/swagger-ui/**")
|
||||
.addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/");
|
||||
}
|
||||
}
|
||||
@@ -101,7 +101,7 @@ public class AuthFilter implements GlobalFilter, Ordered
|
||||
|
||||
private Mono<Void> unauthorizedResponse(ServerWebExchange exchange, String msg)
|
||||
{
|
||||
log.error("[鉴权异常处理]请求路径:{}", exchange.getRequest().getPath());
|
||||
log.error("[鉴权异常处理]请求路径:{},错误信息:{}", exchange.getRequest().getPath(), msg);
|
||||
return ServletUtils.webFluxResponseWriter(exchange.getResponse(), msg, HttpStatus.UNAUTHORIZED);
|
||||
}
|
||||
|
||||
@@ -118,7 +118,7 @@ public class AuthFilter implements GlobalFilter, Ordered
|
||||
*/
|
||||
private String getToken(ServerHttpRequest request)
|
||||
{
|
||||
String token = request.getHeaders().getFirst(TokenConstants.AUTHENTICATION);
|
||||
String token = request.getHeaders().getFirst(SecurityConstants.AUTHORIZATION_HEADER);
|
||||
// 如果前端设置了令牌前缀,则裁剪掉前缀
|
||||
if (StringUtils.isNotEmpty(token) && token.startsWith(TokenConstants.PREFIX))
|
||||
{
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
package com.ruoyi.gateway.handler;
|
||||
|
||||
import java.util.Optional;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import reactor.core.publisher.Mono;
|
||||
import springfox.documentation.swagger.web.SecurityConfiguration;
|
||||
import springfox.documentation.swagger.web.SecurityConfigurationBuilder;
|
||||
import springfox.documentation.swagger.web.SwaggerResourcesProvider;
|
||||
import springfox.documentation.swagger.web.UiConfiguration;
|
||||
import springfox.documentation.swagger.web.UiConfigurationBuilder;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/swagger-resources")
|
||||
public class SwaggerHandler
|
||||
{
|
||||
@Autowired(required = false)
|
||||
private SecurityConfiguration securityConfiguration;
|
||||
|
||||
@Autowired(required = false)
|
||||
private UiConfiguration uiConfiguration;
|
||||
|
||||
private final SwaggerResourcesProvider swaggerResources;
|
||||
|
||||
@Autowired
|
||||
public SwaggerHandler(SwaggerResourcesProvider swaggerResources)
|
||||
{
|
||||
this.swaggerResources = swaggerResources;
|
||||
}
|
||||
|
||||
@GetMapping("/configuration/security")
|
||||
public Mono<ResponseEntity<SecurityConfiguration>> securityConfiguration()
|
||||
{
|
||||
return Mono.just(new ResponseEntity<>(
|
||||
Optional.ofNullable(securityConfiguration).orElse(SecurityConfigurationBuilder.builder().build()),
|
||||
HttpStatus.OK));
|
||||
}
|
||||
|
||||
@GetMapping("/configuration/ui")
|
||||
public Mono<ResponseEntity<UiConfiguration>> uiConfiguration()
|
||||
{
|
||||
return Mono.just(new ResponseEntity<>(
|
||||
Optional.ofNullable(uiConfiguration).orElse(UiConfigurationBuilder.builder().build()), HttpStatus.OK));
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
@GetMapping("")
|
||||
public Mono<ResponseEntity> swaggerResources()
|
||||
{
|
||||
return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK)));
|
||||
}
|
||||
}
|
||||
@@ -103,14 +103,13 @@ public class ValidateCodeServiceImpl implements ValidateCodeService
|
||||
{
|
||||
throw new CaptchaException("验证码不能为空");
|
||||
}
|
||||
if (StringUtils.isEmpty(uuid))
|
||||
String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StringUtils.nvl(uuid, "");
|
||||
String captcha = redisService.getCacheObject(verifyKey);
|
||||
if (captcha == null)
|
||||
{
|
||||
throw new CaptchaException("验证码已失效");
|
||||
}
|
||||
String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + uuid;
|
||||
String captcha = redisService.getCacheObject(verifyKey);
|
||||
redisService.deleteObject(verifyKey);
|
||||
|
||||
if (!code.equalsIgnoreCase(captcha))
|
||||
{
|
||||
throw new CaptchaException("验证码错误");
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<artifactId>ruoyi</artifactId>
|
||||
<version>3.6.4</version>
|
||||
<version>3.6.5</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<artifactId>ruoyi-modules</artifactId>
|
||||
<version>3.6.4</version>
|
||||
<version>3.6.5</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@@ -40,6 +40,12 @@
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- SpringBoot Web -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- FastDFS -->
|
||||
<dependency>
|
||||
@@ -60,12 +66,6 @@
|
||||
<artifactId>ruoyi-api-system</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- RuoYi Common Swagger -->
|
||||
<dependency>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<artifactId>ruoyi-common-swagger</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
||||
@@ -3,14 +3,12 @@ package com.ruoyi.file;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
||||
import com.ruoyi.common.swagger.annotation.EnableCustomSwagger2;
|
||||
|
||||
/**
|
||||
* 文件服务
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@EnableCustomSwagger2
|
||||
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class })
|
||||
public class RuoYiFileApplication
|
||||
{
|
||||
|
||||
@@ -37,10 +37,20 @@ public class FastDfsSysFileServiceImpl implements ISysFileService
|
||||
@Override
|
||||
public String uploadFile(MultipartFile file) throws Exception
|
||||
{
|
||||
InputStream inputStream = file.getInputStream();
|
||||
StorePath storePath = storageClient.uploadFile(inputStream, file.getSize(),
|
||||
FileTypeUtils.getExtension(file), null);
|
||||
IoUtils.closeQuietly(inputStream);
|
||||
return domain + "/" + storePath.getFullPath();
|
||||
InputStream inputStream = null;
|
||||
try
|
||||
{
|
||||
inputStream = file.getInputStream();
|
||||
StorePath storePath = storageClient.uploadFile(inputStream, file.getSize(), FileTypeUtils.getExtension(file), null);
|
||||
return domain + "/" + storePath.getFullPath();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new RuntimeException("FastDfs Failed to upload file", e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IoUtils.closeQuietly(inputStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,16 +34,27 @@ public class MinioSysFileServiceImpl implements ISysFileService
|
||||
@Override
|
||||
public String uploadFile(MultipartFile file) throws Exception
|
||||
{
|
||||
String fileName = FileUploadUtils.extractFilename(file);
|
||||
InputStream inputStream = file.getInputStream();
|
||||
PutObjectArgs args = PutObjectArgs.builder()
|
||||
.bucket(minioConfig.getBucketName())
|
||||
.object(fileName)
|
||||
.stream(inputStream, file.getSize(), -1)
|
||||
.contentType(file.getContentType())
|
||||
.build();
|
||||
client.putObject(args);
|
||||
IoUtils.closeQuietly(inputStream);
|
||||
return minioConfig.getUrl() + "/" + minioConfig.getBucketName() + "/" + fileName;
|
||||
InputStream inputStream = null;
|
||||
try
|
||||
{
|
||||
String fileName = FileUploadUtils.extractFilename(file);
|
||||
inputStream = file.getInputStream();
|
||||
PutObjectArgs args = PutObjectArgs.builder()
|
||||
.bucket(minioConfig.getBucketName())
|
||||
.object(fileName)
|
||||
.stream(inputStream, file.getSize(), -1)
|
||||
.contentType(file.getContentType())
|
||||
.build();
|
||||
client.putObject(args);
|
||||
return minioConfig.getUrl() + "/" + minioConfig.getBucketName() + "/" + fileName;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new RuntimeException("Minio Failed to upload file", e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IoUtils.closeQuietly(inputStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ public class FileUploadUtils
|
||||
/**
|
||||
* 默认大小 50M
|
||||
*/
|
||||
public static final long DEFAULT_MAX_SIZE = 50 * 1024 * 1024;
|
||||
public static final long DEFAULT_MAX_SIZE = 50 * 1024 * 1024L;
|
||||
|
||||
/**
|
||||
* 默认的文件名最大长度 100
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<artifactId>ruoyi-modules</artifactId>
|
||||
<version>3.6.4</version>
|
||||
<version>3.6.5</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@@ -40,13 +40,6 @@
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Swagger UI -->
|
||||
<dependency>
|
||||
<groupId>io.springfox</groupId>
|
||||
<artifactId>springfox-swagger-ui</artifactId>
|
||||
<version>${swagger.fox.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Apache Velocity -->
|
||||
<dependency>
|
||||
|
||||
@@ -4,7 +4,6 @@ import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import com.ruoyi.common.security.annotation.EnableCustomConfig;
|
||||
import com.ruoyi.common.security.annotation.EnableRyFeignClients;
|
||||
import com.ruoyi.common.swagger.annotation.EnableCustomSwagger2;
|
||||
|
||||
/**
|
||||
* 代码生成
|
||||
@@ -12,7 +11,6 @@ import com.ruoyi.common.swagger.annotation.EnableCustomSwagger2;
|
||||
* @author ruoyi
|
||||
*/
|
||||
@EnableCustomConfig
|
||||
@EnableCustomSwagger2
|
||||
@EnableRyFeignClients
|
||||
@SpringBootApplication
|
||||
public class RuoYiGenApplication
|
||||
|
||||
@@ -18,12 +18,15 @@ public class GenConfig
|
||||
/** 生成包路径 */
|
||||
public static String packageName;
|
||||
|
||||
/** 自动去除表前缀,默认是false */
|
||||
/** 自动去除表前缀 */
|
||||
public static boolean autoRemovePre;
|
||||
|
||||
/** 表前缀(类名不会包含表前缀) */
|
||||
/** 表前缀 */
|
||||
public static String tablePrefix;
|
||||
|
||||
/** 是否允许生成文件覆盖到本地(自定义路径) */
|
||||
public static boolean allowOverwrite;
|
||||
|
||||
public static String getAuthor()
|
||||
{
|
||||
return author;
|
||||
@@ -63,4 +66,14 @@ public class GenConfig
|
||||
{
|
||||
GenConfig.tablePrefix = tablePrefix;
|
||||
}
|
||||
|
||||
public static boolean isAllowOverwrite()
|
||||
{
|
||||
return allowOverwrite;
|
||||
}
|
||||
|
||||
public void setAllowOverwrite(boolean allowOverwrite)
|
||||
{
|
||||
GenConfig.allowOverwrite = allowOverwrite;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import com.ruoyi.common.core.web.page.TableDataInfo;
|
||||
import com.ruoyi.common.log.annotation.Log;
|
||||
import com.ruoyi.common.log.enums.BusinessType;
|
||||
import com.ruoyi.common.security.annotation.RequiresPermissions;
|
||||
import com.ruoyi.gen.config.GenConfig;
|
||||
import com.ruoyi.gen.domain.GenTable;
|
||||
import com.ruoyi.gen.domain.GenTableColumn;
|
||||
import com.ruoyi.gen.service.IGenTableColumnService;
|
||||
@@ -168,6 +169,10 @@ public class GenController extends BaseController
|
||||
@GetMapping("/genCode/{tableName}")
|
||||
public AjaxResult genCode(@PathVariable("tableName") String tableName)
|
||||
{
|
||||
if (!GenConfig.isAllowOverwrite())
|
||||
{
|
||||
return AjaxResult.error("【系统预设】不允许生成文件覆盖到本地");
|
||||
}
|
||||
genTableService.generatorCode(tableName);
|
||||
return success();
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ public class GenTable extends BaseEntity
|
||||
private String treeName;
|
||||
|
||||
/** 上级菜单ID字段 */
|
||||
private String parentMenuId;
|
||||
private Long parentMenuId;
|
||||
|
||||
/** 上级菜单名称字段 */
|
||||
private String parentMenuName;
|
||||
@@ -317,12 +317,12 @@ public class GenTable extends BaseEntity
|
||||
this.treeName = treeName;
|
||||
}
|
||||
|
||||
public String getParentMenuId()
|
||||
public Long getParentMenuId()
|
||||
{
|
||||
return parentMenuId;
|
||||
}
|
||||
|
||||
public void setParentMenuId(String parentMenuId)
|
||||
public void setParentMenuId(Long parentMenuId)
|
||||
{
|
||||
this.parentMenuId = parentMenuId;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.ruoyi.gen.domain;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
import com.ruoyi.common.core.utils.StringUtils;
|
||||
import com.ruoyi.common.core.web.domain.BaseEntity;
|
||||
|
||||
|
||||
@@ -129,9 +129,9 @@ public class GenTableServiceImpl implements IGenTableService
|
||||
int row = genTableMapper.updateGenTable(genTable);
|
||||
if (row > 0)
|
||||
{
|
||||
for (GenTableColumn cenTableColumn : genTable.getColumns())
|
||||
for (GenTableColumn genTableColumn : genTable.getColumns())
|
||||
{
|
||||
genTableColumnMapper.updateGenTableColumn(cenTableColumn);
|
||||
genTableColumnMapper.updateGenTableColumn(genTableColumn);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -414,16 +414,16 @@ public class GenTableServiceImpl implements IGenTableService
|
||||
{
|
||||
throw new ServiceException("树名称字段不能为空");
|
||||
}
|
||||
else if (GenConstants.TPL_SUB.equals(genTable.getTplCategory()))
|
||||
}
|
||||
else if (GenConstants.TPL_SUB.equals(genTable.getTplCategory()))
|
||||
{
|
||||
if (StringUtils.isEmpty(genTable.getSubTableName()))
|
||||
{
|
||||
if (StringUtils.isEmpty(genTable.getSubTableName()))
|
||||
{
|
||||
throw new ServiceException("关联子表的表名不能为空");
|
||||
}
|
||||
else if (StringUtils.isEmpty(genTable.getSubTableFkName()))
|
||||
{
|
||||
throw new ServiceException("子表关联的外键名不能为空");
|
||||
}
|
||||
throw new ServiceException("关联子表的表名不能为空");
|
||||
}
|
||||
else if (StringUtils.isEmpty(genTable.getSubTableFkName()))
|
||||
{
|
||||
throw new ServiceException("子表关联的外键名不能为空");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -491,7 +491,7 @@ public class GenTableServiceImpl implements IGenTableService
|
||||
String treeCode = paramsObj.getString(GenConstants.TREE_CODE);
|
||||
String treeParentCode = paramsObj.getString(GenConstants.TREE_PARENT_CODE);
|
||||
String treeName = paramsObj.getString(GenConstants.TREE_NAME);
|
||||
String parentMenuId = paramsObj.getString(GenConstants.PARENT_MENU_ID);
|
||||
Long parentMenuId = paramsObj.getLongValue(GenConstants.PARENT_MENU_ID);
|
||||
String parentMenuName = paramsObj.getString(GenConstants.PARENT_MENU_NAME);
|
||||
|
||||
genTable.setTreeCode(treeCode);
|
||||
|
||||
@@ -26,7 +26,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<result property="updateBy" column="update_by" />
|
||||
<result property="updateTime" column="update_time" />
|
||||
<result property="remark" column="remark" />
|
||||
<collection property="columns" javaType="java.util.List" resultMap="GenTableColumnResult" />
|
||||
<collection property="columns" javaType="java.util.List" resultMap="GenTableColumnResult" />
|
||||
</resultMap>
|
||||
|
||||
<resultMap type="GenTableColumn" id="GenTableColumnResult">
|
||||
@@ -68,10 +68,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
AND lower(table_comment) like lower(concat('%', #{tableComment}, '%'))
|
||||
</if>
|
||||
<if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 -->
|
||||
AND date_format(create_time,'%y%m%d') >= date_format(#{params.beginTime},'%y%m%d')
|
||||
AND date_format(create_time,'%Y%m%d') >= date_format(#{params.beginTime},'%Y%m%d')
|
||||
</if>
|
||||
<if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 -->
|
||||
AND date_format(create_time,'%y%m%d') <= date_format(#{params.endTime},'%y%m%d')
|
||||
AND date_format(create_time,'%Y%m%d') <= date_format(#{params.endTime},'%Y%m%d')
|
||||
</if>
|
||||
</where>
|
||||
</select>
|
||||
@@ -79,7 +79,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 'qrtz\_%' AND table_name NOT LIKE 'gen\_%'
|
||||
AND table_name NOT IN (select table_name from gen_table)
|
||||
<if test="tableName != null and tableName != ''">
|
||||
AND lower(table_name) like lower(concat('%', #{tableName}, '%'))
|
||||
@@ -88,17 +88,17 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
AND lower(table_comment) like lower(concat('%', #{tableComment}, '%'))
|
||||
</if>
|
||||
<if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 -->
|
||||
AND date_format(create_time,'%y%m%d') >= date_format(#{params.beginTime},'%y%m%d')
|
||||
AND date_format(create_time,'%Y%m%d') >= date_format(#{params.beginTime},'%Y%m%d')
|
||||
</if>
|
||||
<if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 -->
|
||||
AND date_format(create_time,'%y%m%d') <= date_format(#{params.endTime},'%y%m%d')
|
||||
AND date_format(create_time,'%Y%m%d') <= date_format(#{params.endTime},'%Y%m%d')
|
||||
</if>
|
||||
order by create_time desc
|
||||
</select>
|
||||
|
||||
<select id="selectDbTableListByNames" resultMap="GenTableResult">
|
||||
select table_name, table_comment, create_time, update_time from information_schema.tables
|
||||
where table_name NOT LIKE 'qrtz_%' and table_name NOT LIKE 'gen_%' and table_schema = (select database())
|
||||
where table_name NOT LIKE 'qrtz\_%' and table_name NOT LIKE 'gen\_%' and table_schema = (select database())
|
||||
and table_name in
|
||||
<foreach collection="array" item="name" open="(" separator="," close=")">
|
||||
#{name}
|
||||
|
||||
@@ -71,9 +71,9 @@ public class ${ClassName} extends ${Entity}
|
||||
{
|
||||
return $column.javaField;
|
||||
}
|
||||
#end
|
||||
#end
|
||||
|
||||
#end
|
||||
#end
|
||||
#if($table.sub)
|
||||
public List<${subClassName}> get${subClassName}List()
|
||||
{
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
icon="el-icon-plus"
|
||||
size="mini"
|
||||
@click="handleAdd"
|
||||
v-hasPermi="['${moduleName}:${businessName}:add']"
|
||||
v-hasPermi="['${permissionPrefix}:add']"
|
||||
>新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
@@ -144,21 +144,21 @@
|
||||
type="text"
|
||||
icon="el-icon-edit"
|
||||
@click="handleUpdate(scope.row)"
|
||||
v-hasPermi="['${moduleName}:${businessName}:edit']"
|
||||
v-hasPermi="['${permissionPrefix}:edit']"
|
||||
>修改</el-button>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-plus"
|
||||
@click="handleAdd(scope.row)"
|
||||
v-hasPermi="['${moduleName}:${businessName}:add']"
|
||||
v-hasPermi="['${permissionPrefix}:add']"
|
||||
>新增</el-button>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-delete"
|
||||
@click="handleDelete(scope.row)"
|
||||
v-hasPermi="['${moduleName}:${businessName}:remove']"
|
||||
v-hasPermi="['${permissionPrefix}:remove']"
|
||||
>删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -453,7 +453,7 @@ export default {
|
||||
this.reset();
|
||||
this.getTreeselect();
|
||||
if (row != null) {
|
||||
this.form.${treeParentCode} = row.${treeCode};
|
||||
this.form.${treeParentCode} = row.${treeParentCode};
|
||||
}
|
||||
get${BusinessName}(row.${pkColumn.javaField}).then(response => {
|
||||
this.form = response.data;
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
icon="el-icon-plus"
|
||||
size="mini"
|
||||
@click="handleAdd"
|
||||
v-hasPermi="['${moduleName}:${businessName}:add']"
|
||||
v-hasPermi="['${permissionPrefix}:add']"
|
||||
>新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
@@ -86,7 +86,7 @@
|
||||
size="mini"
|
||||
:disabled="single"
|
||||
@click="handleUpdate"
|
||||
v-hasPermi="['${moduleName}:${businessName}:edit']"
|
||||
v-hasPermi="['${permissionPrefix}:edit']"
|
||||
>修改</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
@@ -97,7 +97,7 @@
|
||||
size="mini"
|
||||
:disabled="multiple"
|
||||
@click="handleDelete"
|
||||
v-hasPermi="['${moduleName}:${businessName}:remove']"
|
||||
v-hasPermi="['${permissionPrefix}:remove']"
|
||||
>删除</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
@@ -107,7 +107,7 @@
|
||||
icon="el-icon-download"
|
||||
size="mini"
|
||||
@click="handleExport"
|
||||
v-hasPermi="['${moduleName}:${businessName}:export']"
|
||||
v-hasPermi="['${permissionPrefix}:export']"
|
||||
>导出</el-button>
|
||||
</el-col>
|
||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||
@@ -158,14 +158,14 @@
|
||||
type="text"
|
||||
icon="el-icon-edit"
|
||||
@click="handleUpdate(scope.row)"
|
||||
v-hasPermi="['${moduleName}:${businessName}:edit']"
|
||||
v-hasPermi="['${permissionPrefix}:edit']"
|
||||
>修改</el-button>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
icon="el-icon-delete"
|
||||
@click="handleDelete(scope.row)"
|
||||
v-hasPermi="['${moduleName}:${businessName}:remove']"
|
||||
v-hasPermi="['${permissionPrefix}:remove']"
|
||||
>删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
@@ -73,7 +73,7 @@
|
||||
plain
|
||||
icon="Plus"
|
||||
@click="handleAdd"
|
||||
v-hasPermi="['${moduleName}:${businessName}:add']"
|
||||
v-hasPermi="['${permissionPrefix}:add']"
|
||||
>新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
@@ -136,9 +136,9 @@
|
||||
#end
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['${moduleName}:${businessName}:edit']">修改</el-button>
|
||||
<el-button link type="primary" icon="Plus" @click="handleAdd(scope.row)" v-hasPermi="['${moduleName}:${businessName}:add']">新增</el-button>
|
||||
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['${moduleName}:${businessName}:remove']">删除</el-button>
|
||||
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['${permissionPrefix}:edit']">修改</el-button>
|
||||
<el-button link type="primary" icon="Plus" @click="handleAdd(scope.row)" v-hasPermi="['${permissionPrefix}:add']">新增</el-button>
|
||||
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['${permissionPrefix}:remove']">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@@ -420,7 +420,7 @@ async function handleUpdate(row) {
|
||||
reset();
|
||||
await getTreeselect();
|
||||
if (row != null) {
|
||||
form.value.${treeParentCode} = row.${treeCode};
|
||||
form.value.${treeParentCode} = row.${treeParentCode};
|
||||
}
|
||||
get${BusinessName}(row.${pkColumn.javaField}).then(response => {
|
||||
form.value = response.data;
|
||||
|
||||
@@ -73,7 +73,7 @@
|
||||
plain
|
||||
icon="Plus"
|
||||
@click="handleAdd"
|
||||
v-hasPermi="['${moduleName}:${businessName}:add']"
|
||||
v-hasPermi="['${permissionPrefix}:add']"
|
||||
>新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
@@ -83,7 +83,7 @@
|
||||
icon="Edit"
|
||||
:disabled="single"
|
||||
@click="handleUpdate"
|
||||
v-hasPermi="['${moduleName}:${businessName}:edit']"
|
||||
v-hasPermi="['${permissionPrefix}:edit']"
|
||||
>修改</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
@@ -93,7 +93,7 @@
|
||||
icon="Delete"
|
||||
:disabled="multiple"
|
||||
@click="handleDelete"
|
||||
v-hasPermi="['${moduleName}:${businessName}:remove']"
|
||||
v-hasPermi="['${permissionPrefix}:remove']"
|
||||
>删除</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
@@ -102,7 +102,7 @@
|
||||
plain
|
||||
icon="Download"
|
||||
@click="handleExport"
|
||||
v-hasPermi="['${moduleName}:${businessName}:export']"
|
||||
v-hasPermi="['${permissionPrefix}:export']"
|
||||
>导出</el-button>
|
||||
</el-col>
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||
@@ -148,8 +148,8 @@
|
||||
#end
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['${moduleName}:${businessName}:edit']">修改</el-button>
|
||||
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['${moduleName}:${businessName}:remove']">删除</el-button>
|
||||
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['${permissionPrefix}:edit']">修改</el-button>
|
||||
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['${permissionPrefix}:remove']">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
@@ -12,12 +12,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
#if($table.sub)
|
||||
|
||||
<resultMap id="${ClassName}${subClassName}Result" type="${ClassName}" extends="${ClassName}Result">
|
||||
<collection property="${subclassName}List" notNullColumn="sub_${subTable.pkColumn.columnName}" javaType="java.util.List" resultMap="${subClassName}Result" />
|
||||
<collection property="${subclassName}List" ofType="${subClassName}" column="${pkColumn.columnName}" select="select${subClassName}List" />
|
||||
</resultMap>
|
||||
|
||||
<resultMap type="${subClassName}" id="${subClassName}Result">
|
||||
#foreach ($column in $subTable.columns)
|
||||
<result property="${column.javaField}" column="sub_${column.columnName}" />
|
||||
<result property="${column.javaField}" column="${column.columnName}" />
|
||||
#end
|
||||
</resultMap>
|
||||
#end
|
||||
@@ -63,15 +63,20 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<include refid="select${ClassName}Vo"/>
|
||||
where ${pkColumn.columnName} = #{${pkColumn.javaField}}
|
||||
#elseif($table.sub)
|
||||
select#foreach($column in $columns) a.$column.columnName#if($foreach.count != $columns.size()),#end#end,
|
||||
#foreach($column in $subTable.columns) b.$column.columnName as sub_$column.columnName#if($foreach.count != $subTable.columns.size()),#end#end
|
||||
|
||||
from ${tableName} a
|
||||
left join ${subTableName} b on b.${subTableFkName} = a.${pkColumn.columnName}
|
||||
where a.${pkColumn.columnName} = #{${pkColumn.javaField}}
|
||||
select#foreach($column in $columns) $column.columnName#if($foreach.count != $columns.size()),#end#end
|
||||
from ${tableName}
|
||||
where ${pkColumn.columnName} = #{${pkColumn.javaField}}
|
||||
#end
|
||||
</select>
|
||||
|
||||
#if($table.sub)
|
||||
|
||||
<select id="select${subClassName}List" resultMap="${subClassName}Result">
|
||||
select#foreach ($column in $subTable.columns) $column.columnName#if($foreach.count != $subTable.columns.size()),#end#end
|
||||
from ${subTableName}
|
||||
where ${subTableFkName} = #{${subTableFkName}}
|
||||
</select>
|
||||
#end
|
||||
|
||||
<insert id="insert${ClassName}" parameterType="${ClassName}"#if($pkColumn.increment) useGeneratedKeys="true" keyProperty="$pkColumn.javaField"#end>
|
||||
insert into ${tableName}
|
||||
<trim prefix="(" suffix=")" suffixOverrides=",">
|
||||
@@ -127,7 +132,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
|
||||
<insert id="batch${subClassName}">
|
||||
insert into ${subTableName}(#foreach($column in $subTable.columns) $column.columnName#if($foreach.count != $subTable.columns.size()),#end#end) values
|
||||
<foreach item="item" index="index" collection="list" separator=",">
|
||||
<foreach item="item" index="index" collection="list" separator=",">
|
||||
(#foreach($column in $subTable.columns) #{item.$column.javaField}#if($foreach.count != $subTable.columns.size()),#end#end)
|
||||
</foreach>
|
||||
</insert>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<artifactId>ruoyi-modules</artifactId>
|
||||
<version>3.6.4</version>
|
||||
<version>3.6.5</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@@ -40,14 +40,7 @@
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Swagger UI -->
|
||||
<dependency>
|
||||
<groupId>io.springfox</groupId>
|
||||
<artifactId>springfox-swagger-ui</artifactId>
|
||||
<version>${swagger.fox.version}</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- Quartz -->
|
||||
<dependency>
|
||||
<groupId>org.quartz-scheduler</groupId>
|
||||
|
||||
@@ -4,7 +4,6 @@ import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import com.ruoyi.common.security.annotation.EnableCustomConfig;
|
||||
import com.ruoyi.common.security.annotation.EnableRyFeignClients;
|
||||
import com.ruoyi.common.swagger.annotation.EnableCustomSwagger2;
|
||||
|
||||
/**
|
||||
* 定时任务
|
||||
@@ -12,7 +11,6 @@ import com.ruoyi.common.swagger.annotation.EnableCustomSwagger2;
|
||||
* @author ruoyi
|
||||
*/
|
||||
@EnableCustomConfig
|
||||
@EnableCustomSwagger2
|
||||
@EnableRyFeignClients
|
||||
@SpringBootApplication
|
||||
public class RuoYiJobApplication
|
||||
|
||||
@@ -105,7 +105,7 @@ public class JobInvokeUtil
|
||||
*/
|
||||
public static List<Object[]> getMethodParams(String invokeTarget)
|
||||
{
|
||||
String methodStr = StringUtils.substringBetween(invokeTarget, "(", ")");
|
||||
String methodStr = StringUtils.substringBetweenLast(invokeTarget, "(", ")");
|
||||
if (StringUtils.isEmpty(methodStr))
|
||||
{
|
||||
return null;
|
||||
|
||||
@@ -36,10 +36,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
AND invoke_target like concat('%', #{invokeTarget}, '%')
|
||||
</if>
|
||||
<if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 -->
|
||||
and date_format(create_time,'%y%m%d') >= date_format(#{params.beginTime},'%y%m%d')
|
||||
and date_format(create_time,'%Y%m%d') >= date_format(#{params.beginTime},'%Y%m%d')
|
||||
</if>
|
||||
<if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 -->
|
||||
and date_format(create_time,'%y%m%d') <= date_format(#{params.endTime},'%y%m%d')
|
||||
and date_format(create_time,'%Y%m%d') <= date_format(#{params.endTime},'%Y%m%d')
|
||||
</if>
|
||||
</where>
|
||||
order by create_time desc
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<artifactId>ruoyi-modules</artifactId>
|
||||
<version>3.6.4</version>
|
||||
<version>3.6.5</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@@ -40,14 +40,7 @@
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Swagger UI -->
|
||||
<dependency>
|
||||
<groupId>io.springfox</groupId>
|
||||
<artifactId>springfox-swagger-ui</artifactId>
|
||||
<version>${swagger.fox.version}</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- Mysql Connector -->
|
||||
<dependency>
|
||||
<groupId>com.mysql</groupId>
|
||||
|
||||
@@ -4,7 +4,6 @@ import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import com.ruoyi.common.security.annotation.EnableCustomConfig;
|
||||
import com.ruoyi.common.security.annotation.EnableRyFeignClients;
|
||||
import com.ruoyi.common.swagger.annotation.EnableCustomSwagger2;
|
||||
|
||||
/**
|
||||
* 系统模块
|
||||
@@ -12,7 +11,6 @@ import com.ruoyi.common.swagger.annotation.EnableCustomSwagger2;
|
||||
* @author ruoyi
|
||||
*/
|
||||
@EnableCustomConfig
|
||||
@EnableCustomSwagger2
|
||||
@EnableRyFeignClients
|
||||
@SpringBootApplication
|
||||
public class RuoYiSystemApplication
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.ruoyi.system.controller;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
@@ -79,7 +80,7 @@ public class SysProfileController extends BaseController
|
||||
{
|
||||
return error("修改用户'" + loginUser.getUsername() + "'失败,邮箱账号已存在");
|
||||
}
|
||||
if (userService.updateUserProfile(currentUser) > 0)
|
||||
if (userService.updateUserProfile(currentUser))
|
||||
{
|
||||
// 更新缓存用户信息
|
||||
tokenService.setLoginUser(loginUser);
|
||||
@@ -93,11 +94,13 @@ public class SysProfileController extends BaseController
|
||||
*/
|
||||
@Log(title = "个人信息", businessType = BusinessType.UPDATE)
|
||||
@PutMapping("/updatePwd")
|
||||
public AjaxResult updatePwd(String oldPassword, String newPassword)
|
||||
public AjaxResult updatePwd(@RequestBody Map<String, String> params)
|
||||
{
|
||||
String username = SecurityUtils.getUsername();
|
||||
SysUser user = userService.selectUserByUserName(username);
|
||||
String password = user.getPassword();
|
||||
String oldPassword = params.get("oldPassword");
|
||||
String newPassword = params.get("newPassword");
|
||||
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||
String userName = loginUser.getUsername();
|
||||
String password = loginUser.getSysUser().getPassword();
|
||||
if (!SecurityUtils.matchesPassword(oldPassword, password))
|
||||
{
|
||||
return error("修改密码失败,旧密码错误");
|
||||
@@ -107,10 +110,9 @@ public class SysProfileController extends BaseController
|
||||
return error("新密码不能与旧密码相同");
|
||||
}
|
||||
newPassword = SecurityUtils.encryptPassword(newPassword);
|
||||
if (userService.resetUserPwd(username, newPassword) > 0)
|
||||
if (userService.resetUserPwd(userName, newPassword) > 0)
|
||||
{
|
||||
// 更新缓存用户密码
|
||||
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||
loginUser.getSysUser().setPassword(newPassword);
|
||||
tokenService.setLoginUser(loginUser);
|
||||
return success();
|
||||
|
||||
@@ -27,6 +27,7 @@ import com.ruoyi.common.log.annotation.Log;
|
||||
import com.ruoyi.common.log.enums.BusinessType;
|
||||
import com.ruoyi.common.security.annotation.InnerAuth;
|
||||
import com.ruoyi.common.security.annotation.RequiresPermissions;
|
||||
import com.ruoyi.common.security.service.TokenService;
|
||||
import com.ruoyi.common.security.utils.SecurityUtils;
|
||||
import com.ruoyi.system.api.domain.SysDept;
|
||||
import com.ruoyi.system.api.domain.SysRole;
|
||||
@@ -66,6 +67,9 @@ public class SysUserController extends BaseController
|
||||
@Autowired
|
||||
private ISysConfigService configService;
|
||||
|
||||
@Autowired
|
||||
private TokenService tokenService;
|
||||
|
||||
/**
|
||||
* 获取用户列表
|
||||
*/
|
||||
@@ -149,6 +153,16 @@ public class SysUserController extends BaseController
|
||||
return R.ok(userService.registerUser(sysUser));
|
||||
}
|
||||
|
||||
/**
|
||||
*记录用户登录IP地址和登录时间
|
||||
*/
|
||||
@InnerAuth
|
||||
@PutMapping("/recordlogin")
|
||||
public R<Boolean> recordlogin(@RequestBody SysUser sysUser)
|
||||
{
|
||||
return R.ok(userService.updateUserProfile(sysUser));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户信息
|
||||
*
|
||||
@@ -157,11 +171,17 @@ public class SysUserController extends BaseController
|
||||
@GetMapping("getInfo")
|
||||
public AjaxResult getInfo()
|
||||
{
|
||||
SysUser user = userService.selectUserById(SecurityUtils.getUserId());
|
||||
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||
SysUser user = loginUser.getSysUser();
|
||||
// 角色集合
|
||||
Set<String> roles = permissionService.getRolePermission(user);
|
||||
// 权限集合
|
||||
Set<String> permissions = permissionService.getMenuPermission(user);
|
||||
if (!loginUser.getPermissions().equals(permissions))
|
||||
{
|
||||
loginUser.setPermissions(permissions);
|
||||
tokenService.refreshToken(loginUser);
|
||||
}
|
||||
AjaxResult ajax = AjaxResult.success();
|
||||
ajax.put("user", user);
|
||||
ajax.put("roles", roles);
|
||||
@@ -176,18 +196,18 @@ public class SysUserController extends BaseController
|
||||
@GetMapping(value = { "/", "/{userId}" })
|
||||
public AjaxResult getInfo(@PathVariable(value = "userId", required = false) Long userId)
|
||||
{
|
||||
userService.checkUserDataScope(userId);
|
||||
AjaxResult ajax = AjaxResult.success();
|
||||
List<SysRole> roles = roleService.selectRoleAll();
|
||||
ajax.put("roles", SysUser.isAdmin(userId) ? roles : roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList()));
|
||||
ajax.put("posts", postService.selectPostAll());
|
||||
if (StringUtils.isNotNull(userId))
|
||||
{
|
||||
userService.checkUserDataScope(userId);
|
||||
SysUser sysUser = userService.selectUserById(userId);
|
||||
ajax.put(AjaxResult.DATA_TAG, sysUser);
|
||||
ajax.put("postIds", postService.selectPostListByUserId(userId));
|
||||
ajax.put("roleIds", sysUser.getRoles().stream().map(SysRole::getRoleId).collect(Collectors.toList()));
|
||||
}
|
||||
List<SysRole> roles = roleService.selectRoleAll();
|
||||
ajax.put("roles", SysUser.isAdmin(userId) ? roles : roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList()));
|
||||
ajax.put("posts", postService.selectPostAll());
|
||||
return ajax;
|
||||
}
|
||||
|
||||
@@ -199,6 +219,8 @@ public class SysUserController extends BaseController
|
||||
@PostMapping
|
||||
public AjaxResult add(@Validated @RequestBody SysUser user)
|
||||
{
|
||||
deptService.checkDeptDataScope(user.getDeptId());
|
||||
roleService.checkRoleDataScope(user.getRoleIds());
|
||||
if (!userService.checkUserNameUnique(user))
|
||||
{
|
||||
return error("新增用户'" + user.getUserName() + "'失败,登录账号已存在");
|
||||
@@ -226,6 +248,8 @@ public class SysUserController extends BaseController
|
||||
{
|
||||
userService.checkUserAllowed(user);
|
||||
userService.checkUserDataScope(user.getUserId());
|
||||
deptService.checkDeptDataScope(user.getDeptId());
|
||||
roleService.checkRoleDataScope(user.getRoleIds());
|
||||
if (!userService.checkUserNameUnique(user))
|
||||
{
|
||||
return error("修改用户'" + user.getUserName() + "'失败,登录账号已存在");
|
||||
@@ -310,6 +334,7 @@ public class SysUserController extends BaseController
|
||||
public AjaxResult insertAuthRole(Long userId, Long[] roleIds)
|
||||
{
|
||||
userService.checkUserDataScope(userId);
|
||||
roleService.checkRoleDataScope(roleIds);
|
||||
userService.insertUserAuth(userId, roleIds);
|
||||
return success();
|
||||
}
|
||||
|
||||
@@ -42,6 +42,9 @@ public class SysMenu extends BaseEntity
|
||||
/** 路由参数 */
|
||||
private String query;
|
||||
|
||||
/** 路由名称,默认和路由地址相同的驼峰格式(注意:因为vue3版本的router会删除名称相同路由,为避免名字的冲突,特殊情况可以自定义) */
|
||||
private String routeName;
|
||||
|
||||
/** 是否为外链(0是 1否) */
|
||||
private String isFrame;
|
||||
|
||||
@@ -151,6 +154,16 @@ public class SysMenu extends BaseEntity
|
||||
this.query = query;
|
||||
}
|
||||
|
||||
public String getRouteName()
|
||||
{
|
||||
return routeName;
|
||||
}
|
||||
|
||||
public void setRouteName(String routeName)
|
||||
{
|
||||
this.routeName = routeName;
|
||||
}
|
||||
|
||||
public String getIsFrame()
|
||||
{
|
||||
return isFrame;
|
||||
@@ -232,7 +245,7 @@ public class SysMenu extends BaseEntity
|
||||
{
|
||||
this.children = children;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
|
||||
@@ -242,6 +255,8 @@ public class SysMenu extends BaseEntity
|
||||
.append("orderNum", getOrderNum())
|
||||
.append("path", getPath())
|
||||
.append("component", getComponent())
|
||||
.append("query", getQuery())
|
||||
.append("routeName", getRouteName())
|
||||
.append("isFrame", getIsFrame())
|
||||
.append("IsCache", getIsCache())
|
||||
.append("menuType", getMenuType())
|
||||
|
||||
@@ -4,6 +4,8 @@ import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.ruoyi.common.core.constant.UserConstants;
|
||||
import com.ruoyi.common.core.utils.StringUtils;
|
||||
import com.ruoyi.system.api.domain.SysDept;
|
||||
import com.ruoyi.system.domain.SysMenu;
|
||||
|
||||
@@ -22,6 +24,9 @@ public class TreeSelect implements Serializable
|
||||
/** 节点名称 */
|
||||
private String label;
|
||||
|
||||
/** 节点禁用 */
|
||||
private boolean disabled = false;
|
||||
|
||||
/** 子节点 */
|
||||
@JsonInclude(JsonInclude.Include.NON_EMPTY)
|
||||
private List<TreeSelect> children;
|
||||
@@ -35,6 +40,7 @@ public class TreeSelect implements Serializable
|
||||
{
|
||||
this.id = dept.getDeptId();
|
||||
this.label = dept.getDeptName();
|
||||
this.disabled = StringUtils.equals(UserConstants.DEPT_DISABLE, dept.getStatus());
|
||||
this.children = dept.getChildren().stream().map(TreeSelect::new).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@@ -65,6 +71,16 @@ public class TreeSelect implements Serializable
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
public boolean isDisabled()
|
||||
{
|
||||
return disabled;
|
||||
}
|
||||
|
||||
public void setDisabled(boolean disabled)
|
||||
{
|
||||
this.disabled = disabled;
|
||||
}
|
||||
|
||||
public List<TreeSelect> getChildren()
|
||||
{
|
||||
return children;
|
||||
|
||||
@@ -85,9 +85,9 @@ public interface ISysRoleService
|
||||
/**
|
||||
* 校验角色是否有数据权限
|
||||
*
|
||||
* @param roleId 角色id
|
||||
* @param roleIds 角色id
|
||||
*/
|
||||
public void checkRoleDataScope(Long roleId);
|
||||
public void checkRoleDataScope(Long... roleIds);
|
||||
|
||||
/**
|
||||
* 通过角色ID查询角色使用数量
|
||||
|
||||
@@ -150,7 +150,7 @@ public interface ISysUserService
|
||||
* @param user 用户信息
|
||||
* @return 结果
|
||||
*/
|
||||
public int updateUserProfile(SysUser user);
|
||||
public boolean updateUserProfile(SysUser user);
|
||||
|
||||
/**
|
||||
* 修改用户头像
|
||||
|
||||
@@ -190,7 +190,7 @@ public class SysDeptServiceImpl implements ISysDeptService
|
||||
@Override
|
||||
public void checkDeptDataScope(Long deptId)
|
||||
{
|
||||
if (!SysUser.isAdmin(SecurityUtils.getUserId()))
|
||||
if (!SysUser.isAdmin(SecurityUtils.getUserId()) && StringUtils.isNotNull(deptId))
|
||||
{
|
||||
SysDept dept = new SysDept();
|
||||
dept.setDeptId(deptId);
|
||||
|
||||
@@ -188,7 +188,7 @@ public class SysMenuServiceImpl implements ISysMenuService
|
||||
RouterVo children = new RouterVo();
|
||||
children.setPath(menu.getPath());
|
||||
children.setComponent(menu.getComponent());
|
||||
children.setName(StringUtils.capitalize(menu.getPath()));
|
||||
children.setName(getRouteName(menu.getRouteName(), menu.getPath()));
|
||||
children.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), StringUtils.equals("1", menu.getIsCache()), menu.getPath()));
|
||||
children.setQuery(menu.getQuery());
|
||||
childrenList.add(children);
|
||||
@@ -203,7 +203,7 @@ public class SysMenuServiceImpl implements ISysMenuService
|
||||
String routerPath = innerLinkReplaceEach(menu.getPath());
|
||||
children.setPath(routerPath);
|
||||
children.setComponent(UserConstants.INNER_LINK);
|
||||
children.setName(StringUtils.capitalize(routerPath));
|
||||
children.setName(getRouteName(menu.getRouteName(), routerPath));
|
||||
children.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), menu.getPath()));
|
||||
childrenList.add(children);
|
||||
router.setChildren(childrenList);
|
||||
@@ -354,13 +354,25 @@ public class SysMenuServiceImpl implements ISysMenuService
|
||||
*/
|
||||
public String getRouteName(SysMenu menu)
|
||||
{
|
||||
String routerName = StringUtils.capitalize(menu.getPath());
|
||||
// 非外链并且是一级目录(类型为目录)
|
||||
if (isMenuFrame(menu))
|
||||
{
|
||||
routerName = StringUtils.EMPTY;
|
||||
return StringUtils.EMPTY;
|
||||
}
|
||||
return routerName;
|
||||
return getRouteName(menu.getRouteName(), menu.getPath());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取路由名称,如没有配置路由名称则取路由地址
|
||||
*
|
||||
* @param name 路由名称
|
||||
* @param path 路由地址
|
||||
* @return 路由名称(驼峰格式)
|
||||
*/
|
||||
public String getRouteName(String name, String path)
|
||||
{
|
||||
String routerName = StringUtils.isNotEmpty(name) ? name : path;
|
||||
return StringUtils.capitalize(routerName);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,6 +6,8 @@ import java.util.Set;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import com.ruoyi.common.core.constant.UserConstants;
|
||||
import com.ruoyi.common.core.utils.StringUtils;
|
||||
import com.ruoyi.system.api.domain.SysRole;
|
||||
import com.ruoyi.system.api.domain.SysUser;
|
||||
import com.ruoyi.system.service.ISysMenuService;
|
||||
@@ -71,9 +73,12 @@ public class SysPermissionServiceImpl implements ISysPermissionService
|
||||
// 多角色设置permissions属性,以便数据权限匹配权限
|
||||
for (SysRole role : roles)
|
||||
{
|
||||
Set<String> rolePerms = menuService.selectMenuPermsByRoleId(role.getRoleId());
|
||||
role.setPermissions(rolePerms);
|
||||
perms.addAll(rolePerms);
|
||||
if (StringUtils.equals(role.getStatus(), UserConstants.ROLE_NORMAL) && !role.isAdmin())
|
||||
{
|
||||
Set<String> rolePerms = menuService.selectMenuPermsByRoleId(role.getRoleId());
|
||||
role.setPermissions(rolePerms);
|
||||
perms.addAll(rolePerms);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -192,19 +192,22 @@ public class SysRoleServiceImpl implements ISysRoleService
|
||||
/**
|
||||
* 校验角色是否有数据权限
|
||||
*
|
||||
* @param roleId 角色id
|
||||
* @param roleIds 角色id
|
||||
*/
|
||||
@Override
|
||||
public void checkRoleDataScope(Long roleId)
|
||||
public void checkRoleDataScope(Long... roleIds)
|
||||
{
|
||||
if (!SysUser.isAdmin(SecurityUtils.getUserId()))
|
||||
{
|
||||
SysRole role = new SysRole();
|
||||
role.setRoleId(roleId);
|
||||
List<SysRole> roles = SpringUtils.getAopProxy(this).selectRoleList(role);
|
||||
if (StringUtils.isEmpty(roles))
|
||||
for (Long roleId : roleIds)
|
||||
{
|
||||
throw new ServiceException("没有权限访问角色数据!");
|
||||
SysRole role = new SysRole();
|
||||
role.setRoleId(roleId);
|
||||
List<SysRole> roles = SpringUtils.getAopProxy(this).selectRoleList(role);
|
||||
if (StringUtils.isEmpty(roles))
|
||||
{
|
||||
throw new ServiceException("没有权限访问角色数据!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import com.ruoyi.system.mapper.SysUserMapper;
|
||||
import com.ruoyi.system.mapper.SysUserPostMapper;
|
||||
import com.ruoyi.system.mapper.SysUserRoleMapper;
|
||||
import com.ruoyi.system.service.ISysConfigService;
|
||||
import com.ruoyi.system.service.ISysDeptService;
|
||||
import com.ruoyi.system.service.ISysUserService;
|
||||
|
||||
/**
|
||||
@@ -58,6 +59,9 @@ public class SysUserServiceImpl implements ISysUserService
|
||||
@Autowired
|
||||
private ISysConfigService configService;
|
||||
|
||||
@Autowired
|
||||
private ISysDeptService deptService;
|
||||
|
||||
@Autowired
|
||||
protected Validator validator;
|
||||
|
||||
@@ -332,9 +336,9 @@ public class SysUserServiceImpl implements ISysUserService
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public int updateUserProfile(SysUser user)
|
||||
public boolean updateUserProfile(SysUser user)
|
||||
{
|
||||
return userMapper.updateUser(user);
|
||||
return userMapper.updateUser(user) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -489,7 +493,6 @@ public class SysUserServiceImpl implements ISysUserService
|
||||
int failureNum = 0;
|
||||
StringBuilder successMsg = new StringBuilder();
|
||||
StringBuilder failureMsg = new StringBuilder();
|
||||
String password = configService.selectConfigByKey("sys.user.initPassword");
|
||||
for (SysUser user : userList)
|
||||
{
|
||||
try
|
||||
@@ -499,6 +502,8 @@ public class SysUserServiceImpl implements ISysUserService
|
||||
if (StringUtils.isNull(u))
|
||||
{
|
||||
BeanValidators.validateWithException(validator, user);
|
||||
deptService.checkDeptDataScope(user.getDeptId());
|
||||
String password = configService.selectConfigByKey("sys.user.initPassword");
|
||||
user.setPassword(SecurityUtils.encryptPassword(password));
|
||||
user.setCreateBy(operName);
|
||||
userMapper.insertUser(user);
|
||||
@@ -510,6 +515,7 @@ public class SysUserServiceImpl implements ISysUserService
|
||||
BeanValidators.validateWithException(validator, user);
|
||||
checkUserAllowed(u);
|
||||
checkUserDataScope(u.getUserId());
|
||||
deptService.checkDeptDataScope(user.getDeptId());
|
||||
user.setUserId(u.getUserId());
|
||||
user.setUpdateBy(operName);
|
||||
userMapper.updateUser(user);
|
||||
|
||||
@@ -51,10 +51,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
AND config_key like concat('%', #{configKey}, '%')
|
||||
</if>
|
||||
<if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 -->
|
||||
and date_format(create_time,'%y%m%d') >= date_format(#{params.beginTime},'%y%m%d')
|
||||
and date_format(create_time,'%Y%m%d') >= date_format(#{params.beginTime},'%Y%m%d')
|
||||
</if>
|
||||
<if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 -->
|
||||
and date_format(create_time,'%y%m%d') <= date_format(#{params.endTime},'%y%m%d')
|
||||
and date_format(create_time,'%Y%m%d') <= date_format(#{params.endTime},'%Y%m%d')
|
||||
</if>
|
||||
</where>
|
||||
</select>
|
||||
|
||||
@@ -41,7 +41,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
order by dict_sort asc
|
||||
</select>
|
||||
|
||||
<select id="selectDictDataByType" parameterType="SysDictData" resultMap="SysDictDataResult">
|
||||
<select id="selectDictDataByType" parameterType="String" resultMap="SysDictDataResult">
|
||||
<include refid="selectDictDataVo"/>
|
||||
where status = '0' and dict_type = #{dictType} order by dict_sort asc
|
||||
</select>
|
||||
|
||||
@@ -33,10 +33,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
AND dict_type like concat('%', #{dictType}, '%')
|
||||
</if>
|
||||
<if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 -->
|
||||
and date_format(create_time,'%y%m%d') >= date_format(#{params.beginTime},'%y%m%d')
|
||||
and date_format(create_time,'%Y%m%d') >= date_format(#{params.beginTime},'%Y%m%d')
|
||||
</if>
|
||||
<if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 -->
|
||||
and date_format(create_time,'%y%m%d') <= date_format(#{params.endTime},'%y%m%d')
|
||||
and date_format(create_time,'%Y%m%d') <= date_format(#{params.endTime},'%Y%m%d')
|
||||
</if>
|
||||
</where>
|
||||
</select>
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
<result property="path" column="path" />
|
||||
<result property="component" column="component" />
|
||||
<result property="query" column="query" />
|
||||
<result property="routeName" column="route_name" />
|
||||
<result property="isFrame" column="is_frame" />
|
||||
<result property="isCache" column="is_cache" />
|
||||
<result property="menuType" column="menu_type" />
|
||||
@@ -28,7 +29,7 @@
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectMenuVo">
|
||||
select menu_id, menu_name, parent_id, order_num, path, component, `query`, is_frame, is_cache, menu_type, visible, status, ifnull(perms,'') as perms, icon, create_time
|
||||
select menu_id, menu_name, parent_id, order_num, path, component, `query`, route_name, is_frame, is_cache, menu_type, visible, status, ifnull(perms,'') as perms, icon, create_time
|
||||
from sys_menu
|
||||
</sql>
|
||||
|
||||
@@ -49,13 +50,13 @@
|
||||
</select>
|
||||
|
||||
<select id="selectMenuTreeAll" resultMap="SysMenuResult">
|
||||
select distinct m.menu_id, m.parent_id, m.menu_name, m.path, m.component, m.`query`, m.visible, m.status, ifnull(m.perms,'') as perms, m.is_frame, m.is_cache, m.menu_type, m.icon, m.order_num, m.create_time
|
||||
select distinct m.menu_id, m.parent_id, m.menu_name, m.path, m.component, m.`query`, m.route_name, m.visible, m.status, ifnull(m.perms,'') as perms, m.is_frame, m.is_cache, m.menu_type, m.icon, m.order_num, m.create_time
|
||||
from sys_menu m where m.menu_type in ('M', 'C') and m.status = 0
|
||||
order by m.parent_id, m.order_num
|
||||
</select>
|
||||
|
||||
<select id="selectMenuListByUserId" parameterType="SysMenu" resultMap="SysMenuResult">
|
||||
select distinct m.menu_id, m.parent_id, m.menu_name, m.path, m.component, m.`query`, m.visible, m.status, ifnull(m.perms,'') as perms, m.is_frame, m.is_cache, m.menu_type, m.icon, m.order_num, m.create_time
|
||||
select distinct m.menu_id, m.parent_id, m.menu_name, m.path, m.component, m.`query`, m.route_name, m.visible, m.status, ifnull(m.perms,'') as perms, m.is_frame, m.is_cache, m.menu_type, m.icon, m.order_num, m.create_time
|
||||
from sys_menu m
|
||||
left join sys_role_menu rm on m.menu_id = rm.menu_id
|
||||
left join sys_user_role ur on rm.role_id = ur.role_id
|
||||
@@ -74,7 +75,7 @@
|
||||
</select>
|
||||
|
||||
<select id="selectMenuTreeByUserId" parameterType="Long" resultMap="SysMenuResult">
|
||||
select distinct m.menu_id, m.parent_id, m.menu_name, m.path, m.component, m.`query`, m.visible, m.status, ifnull(m.perms,'') as perms, m.is_frame, m.is_cache, m.menu_type, m.icon, m.order_num, m.create_time
|
||||
select distinct m.menu_id, m.parent_id, m.menu_name, m.path, m.component, m.`query`, m.route_name, m.visible, m.status, ifnull(m.perms,'') as perms, m.is_frame, m.is_cache, m.menu_type, m.icon, m.order_num, m.create_time
|
||||
from sys_menu m
|
||||
left join sys_role_menu rm on m.menu_id = rm.menu_id
|
||||
left join sys_user_role ur on rm.role_id = ur.role_id
|
||||
@@ -141,6 +142,7 @@
|
||||
<if test="path != null and path != ''">path = #{path},</if>
|
||||
<if test="component != null">component = #{component},</if>
|
||||
<if test="query != null">`query` = #{query},</if>
|
||||
<if test="routeName != null">route_name = #{routeName},</if>
|
||||
<if test="isFrame != null and isFrame != ''">is_frame = #{isFrame},</if>
|
||||
<if test="isCache != null and isCache != ''">is_cache = #{isCache},</if>
|
||||
<if test="menuType != null and menuType != ''">menu_type = #{menuType},</if>
|
||||
@@ -164,6 +166,7 @@
|
||||
<if test="path != null and path != ''">path,</if>
|
||||
<if test="component != null and component != ''">component,</if>
|
||||
<if test="query != null and query != ''">`query`,</if>
|
||||
<if test="routeName != null">route_name,</if>
|
||||
<if test="isFrame != null and isFrame != ''">is_frame,</if>
|
||||
<if test="isCache != null and isCache != ''">is_cache,</if>
|
||||
<if test="menuType != null and menuType != ''">menu_type,</if>
|
||||
@@ -182,6 +185,7 @@
|
||||
<if test="path != null and path != ''">#{path},</if>
|
||||
<if test="component != null and component != ''">#{component},</if>
|
||||
<if test="query != null and query != ''">#{query},</if>
|
||||
<if test="routeName != null">#{routeName},</if>
|
||||
<if test="isFrame != null and isFrame != ''">#{isFrame},</if>
|
||||
<if test="isCache != null and isCache != ''">#{isCache},</if>
|
||||
<if test="menuType != null and menuType != ''">#{menuType},</if>
|
||||
|
||||
@@ -46,10 +46,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
AND r.role_key like concat('%', #{roleKey}, '%')
|
||||
</if>
|
||||
<if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 -->
|
||||
and date_format(r.create_time,'%y%m%d') >= date_format(#{params.beginTime},'%y%m%d')
|
||||
and date_format(r.create_time,'%Y%m%d') >= date_format(#{params.beginTime},'%Y%m%d')
|
||||
</if>
|
||||
<if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 -->
|
||||
and date_format(r.create_time,'%y%m%d') <= date_format(#{params.endTime},'%y%m%d')
|
||||
and date_format(r.create_time,'%Y%m%d') <= date_format(#{params.endTime},'%Y%m%d')
|
||||
</if>
|
||||
<!-- 数据范围过滤 -->
|
||||
${params.dataScope}
|
||||
|
||||
@@ -73,10 +73,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
AND u.phonenumber like concat('%', #{phonenumber}, '%')
|
||||
</if>
|
||||
<if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 -->
|
||||
AND date_format(u.create_time,'%y%m%d') >= date_format(#{params.beginTime},'%y%m%d')
|
||||
AND date_format(u.create_time,'%Y%m%d') >= date_format(#{params.beginTime},'%Y%m%d')
|
||||
</if>
|
||||
<if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 -->
|
||||
AND date_format(u.create_time,'%y%m%d') <= date_format(#{params.endTime},'%y%m%d')
|
||||
AND date_format(u.create_time,'%Y%m%d') <= date_format(#{params.endTime},'%Y%m%d')
|
||||
</if>
|
||||
<if test="deptId != null and deptId != 0">
|
||||
AND (u.dept_id = #{deptId} OR u.dept_id IN ( SELECT t.dept_id FROM sys_dept t WHERE find_in_set(#{deptId}, ancestors) ))
|
||||
@@ -146,7 +146,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
insert into sys_user(
|
||||
<if test="userId != null and userId != 0">user_id,</if>
|
||||
<if test="deptId != null and deptId != 0">dept_id,</if>
|
||||
<if test="userName != null and userName != ''">user_name,</if>
|
||||
<if test="nickName != null and nickName != ''">nick_name,</if>
|
||||
<if test="email != null and email != ''">email,</if>
|
||||
<if test="avatar != null and avatar != ''">avatar,</if>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
# 页面标题
|
||||
VUE_APP_TITLE = 若依管理系统
|
||||
|
||||
BABEL_ENV = production
|
||||
|
||||
NODE_ENV = production
|
||||
|
||||
# 测试环境配置
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ruoyi",
|
||||
"version": "3.6.4",
|
||||
"version": "3.6.5",
|
||||
"description": "若依管理系统",
|
||||
"author": "若依",
|
||||
"license": "MIT",
|
||||
@@ -37,9 +37,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@riophae/vue-treeselect": "0.4.0",
|
||||
"axios": "0.24.0",
|
||||
"axios": "0.28.1",
|
||||
"clipboard": "2.0.8",
|
||||
"core-js": "3.25.3",
|
||||
"core-js": "3.37.1",
|
||||
"echarts": "5.4.0",
|
||||
"element-ui": "2.15.14",
|
||||
"file-saver": "2.0.5",
|
||||
@@ -49,9 +49,10 @@
|
||||
"js-cookie": "3.0.1",
|
||||
"jsencrypt": "3.0.0-rc.1",
|
||||
"nprogress": "0.2.0",
|
||||
"quill": "1.3.7",
|
||||
"quill": "2.0.2",
|
||||
"screenfull": "5.0.2",
|
||||
"sortablejs": "1.10.2",
|
||||
"splitpanes": "2.4.1",
|
||||
"vue": "2.6.12",
|
||||
"vue-count-to": "1.0.13",
|
||||
"vue-cropper": "0.5.5",
|
||||
|
||||
1
ruoyi-ui/public/styles/theme-chalk/index.css
Normal file
1
ruoyi-ui/public/styles/theme-chalk/index.css
Normal file
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user