diff --git a/README.md b/README.md
index 555b89cfbd7cdd0479c83800fd1c4364a3bbe62e..998475c999aafca9a74c4429eca3d789d10fe11f 100644
--- a/README.md
+++ b/README.md
@@ -1,11 +1,11 @@
-RuoYi v3.8.7
+RuoYi v3.9.0
基于SpringBoot+Vue前后端分离的Java快速开发框架
-
+
@@ -18,11 +18,10 @@
* 权限认证使用Jwt,支持多终端认证系统。
* 支持加载动态权限菜单,多方式轻松权限控制。
* 高效率开发,使用代码生成器可以一键生成前后端代码。
-* 提供了技术栈([Vue3](https://v3.cn.vuejs.org) [Element Plus](https://element-plus.org/zh-CN) [Vite](https://cn.vitejs.dev))版本[RuoYi-Vue3](https://github.com/yangzongzhuan/RuoYi-Vue3),保持同步更新。
-* 提供了单应用版本[RuoYi-Vue-fast](https://github.com/yangzongzhuan/RuoYi-Vue-fast),Oracle版本[RuoYi-Vue-Oracle](https://github.com/yangzongzhuan/RuoYi-Vue-Oracle),保持同步更新。
+* 提供了技术栈([Vue3](https://v3.cn.vuejs.org) [Element Plus](https://element-plus.org/zh-CN) [Vite](https://cn.vitejs.dev))版本[RuoYi-Vue3](https://gitcode.com/yangzongzhuan/RuoYi-Vue3),保持同步更新。
+* 提供了单应用版本[RuoYi-Vue-fast](https://gitcode.com/yangzongzhuan/RuoYi-Vue-fast),Oracle版本[RuoYi-Vue-Oracle](https://gitcode.com/yangzongzhuan/RuoYi-Vue-Oracle),保持同步更新。
* 不分离版本,请移步[RuoYi](https://gitee.com/y_project/RuoYi),微服务版本,请移步[RuoYi-Cloud](https://gitee.com/y_project/RuoYi-Cloud)
* 阿里云折扣场:[点我进入](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)
## 内置功能
@@ -93,4 +92,4 @@
## 若依前后端分离交流群
-QQ群: [](https://jq.qq.com/?_wv=1027&k=5bVB1og) [](https://jq.qq.com/?_wv=1027&k=5eiA4DH) [](https://jq.qq.com/?_wv=1027&k=5AxMKlC) [](https://jq.qq.com/?_wv=1027&k=51G72yr) [](https://jq.qq.com/?_wv=1027&k=VvjN2nvu) [](https://jq.qq.com/?_wv=1027&k=5vYAqA05) [](https://jq.qq.com/?_wv=1027&k=kOIINEb5) [](https://jq.qq.com/?_wv=1027&k=UKtX5jhs) [](https://jq.qq.com/?_wv=1027&k=EI9an8lJ) [](https://jq.qq.com/?_wv=1027&k=SWCtLnMz) [](https://jq.qq.com/?_wv=1027&k=96Dkdq0k) [](https://jq.qq.com/?_wv=1027&k=0fsNiYZt) [](https://jq.qq.com/?_wv=1027&k=7xw4xUG1) [](https://jq.qq.com/?_wv=1027&k=eCx8eyoJ) [](https://jq.qq.com/?_wv=1027&k=SpyH2875) [](https://jq.qq.com/?_wv=1027&k=tKEt51dz) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=0vBbSb0ztbBgVtn3kJS-Q4HUNYwip89G&authKey=8irq5PhutrZmWIvsUsklBxhj57l%2F1nOZqjzigkXZVoZE451GG4JHPOqW7AW6cf0T&noverify=0&group_code=143961921) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=ZFAPAbp09S2ltvwrJzp7wGlbopsc0rwi&authKey=HB2cxpxP2yspk%2Bo3WKTBfktRCccVkU26cgi5B16u0KcAYrVu7sBaE7XSEqmMdFQp&noverify=0&group_code=174951577) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=Fn2aF5IHpwsy8j6VlalNJK6qbwFLFHat&authKey=uyIT%2B97x2AXj3odyXpsSpVaPMC%2Bidw0LxG5MAtEqlrcBcWJUA%2FeS43rsF1Tg7IRJ&noverify=0&group_code=161281055) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=XIzkm_mV2xTsUtFxo63bmicYoDBA6Ifm&authKey=dDW%2F4qsmw3x9govoZY9w%2FoWAoC4wbHqGal%2BbqLzoS6VBarU8EBptIgPKN%2FviyC8j&noverify=0&group_code=138988063) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=DkugnCg68PevlycJSKSwjhFqfIgrWWwR&authKey=pR1Pa5lPIeGF%2FFtIk6d%2FGB5qFi0EdvyErtpQXULzo03zbhopBHLWcuqdpwY241R%2F&noverify=0&group_code=151450850) 点击按钮入群。
\ No newline at end of file
+QQ群: [](https://jq.qq.com/?_wv=1027&k=5bVB1og) [](https://jq.qq.com/?_wv=1027&k=5eiA4DH) [](https://jq.qq.com/?_wv=1027&k=5AxMKlC) [](https://jq.qq.com/?_wv=1027&k=51G72yr) [](https://jq.qq.com/?_wv=1027&k=VvjN2nvu) [](https://jq.qq.com/?_wv=1027&k=5vYAqA05) [](https://jq.qq.com/?_wv=1027&k=kOIINEb5) [](https://jq.qq.com/?_wv=1027&k=UKtX5jhs) [](https://jq.qq.com/?_wv=1027&k=EI9an8lJ) [](https://jq.qq.com/?_wv=1027&k=SWCtLnMz) [](https://jq.qq.com/?_wv=1027&k=96Dkdq0k) [](https://jq.qq.com/?_wv=1027&k=0fsNiYZt) [](https://jq.qq.com/?_wv=1027&k=7xw4xUG1) [](https://jq.qq.com/?_wv=1027&k=eCx8eyoJ) [](https://jq.qq.com/?_wv=1027&k=SpyH2875) [](https://jq.qq.com/?_wv=1027&k=tKEt51dz) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=0vBbSb0ztbBgVtn3kJS-Q4HUNYwip89G&authKey=8irq5PhutrZmWIvsUsklBxhj57l%2F1nOZqjzigkXZVoZE451GG4JHPOqW7AW6cf0T&noverify=0&group_code=143961921) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=ZFAPAbp09S2ltvwrJzp7wGlbopsc0rwi&authKey=HB2cxpxP2yspk%2Bo3WKTBfktRCccVkU26cgi5B16u0KcAYrVu7sBaE7XSEqmMdFQp&noverify=0&group_code=174951577) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=Fn2aF5IHpwsy8j6VlalNJK6qbwFLFHat&authKey=uyIT%2B97x2AXj3odyXpsSpVaPMC%2Bidw0LxG5MAtEqlrcBcWJUA%2FeS43rsF1Tg7IRJ&noverify=0&group_code=161281055) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=XIzkm_mV2xTsUtFxo63bmicYoDBA6Ifm&authKey=dDW%2F4qsmw3x9govoZY9w%2FoWAoC4wbHqGal%2BbqLzoS6VBarU8EBptIgPKN%2FviyC8j&noverify=0&group_code=138988063) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=DkugnCg68PevlycJSKSwjhFqfIgrWWwR&authKey=pR1Pa5lPIeGF%2FFtIk6d%2FGB5qFi0EdvyErtpQXULzo03zbhopBHLWcuqdpwY241R%2F&noverify=0&group_code=151450850) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=F58bgRa-Dp-rsQJThiJqIYv8t4-lWfXh&authKey=UmUs4CVG5OPA1whvsa4uSespOvyd8%2FAr9olEGaWAfdLmfKQk%2FVBp2YU3u2xXXt76&noverify=0&group_code=224622315) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=Nxb2EQ5qozWa218Wbs7zgBnjLSNk_tVT&authKey=obBKXj6SBKgrFTJZx0AqQnIYbNOvBB2kmgwWvGhzxR67RoRr84%2Bus5OadzMcdJl5&noverify=0&group_code=287842588) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=numtK1M_I4eVd2Gvg8qtbuL8JgX42qNh&authKey=giV9XWMaFZTY%2FqPlmWbkB9g3fi0Ev5CwEtT9Tgei0oUlFFCQLDp4ozWRiVIzubIm&noverify=0&group_code=187944233) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=G6r5KGCaa3pqdbUSXNIgYloyb8e0_L0D&authKey=4w8tF1eGW7%2FedWn%2FHAypQksdrML%2BDHolQSx7094Agm7Luakj9EbfPnSTxSi2T1LQ&noverify=0&group_code=228578329) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=GsOo-OLz53J8y_9TPoO6XXSGNRTgbFxA&authKey=R7Uy%2Feq%2BZsoKNqHvRKhiXpypW7DAogoWapOawUGHokJSBIBIre2%2FoiAZeZBSLuBc&noverify=0&group_code=191164766) 点击按钮入群。
\ No newline at end of file
diff --git "a/doc/\350\213\245\344\276\235\347\216\257\345\242\203\344\275\277\347\224\250\346\211\213\345\206\214.docx" "b/doc/\350\213\245\344\276\235\347\216\257\345\242\203\344\275\277\347\224\250\346\211\213\345\206\214.docx"
index 9e4daef4d9be2e445419109a02eaf321cd4d537e..19d2fc3da32eee76d8539c2e72b76f7ef4723944 100644
Binary files "a/doc/\350\213\245\344\276\235\347\216\257\345\242\203\344\275\277\347\224\250\346\211\213\345\206\214.docx" and "b/doc/\350\213\245\344\276\235\347\216\257\345\242\203\344\275\277\347\224\250\346\211\213\345\206\214.docx" differ
diff --git a/pom.xml b/pom.xml
index de52c2ce1bc564b9f5c0db71fcd4ffd4acec224b..6025da2ede7e2b6620a8509af2aa5245e10ab53d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,60 +6,45 @@
com.ruoyi
ruoyi
- 3.8.7
+ 3.9.0
ruoyi
http://www.ruoyi.vip
若依管理系统
- 3.8.7
+ 3.9.0
UTF-8
UTF-8
- 1.8
+ 17
3.1.1
- 5.3.33
- 5.7.12
+ 3.0.4
1.2.23
1.21
3.0.0
2.3.3
- 1.4.7
- 2.0.43
- 6.6.1
- 2.13.0
+ 2.1.1
+ 2.0.58
+ 6.8.3
+ 2.19.0
4.1.2
2.3
0.9.1
+ 8.2.0
+ 2.3.1
+ 6.0.0
+ 2.8.9
-
-
- org.springframework
- spring-framework-bom
- ${spring-framework.version}
- pom
- import
-
-
-
-
- org.springframework.security
- spring-security-bom
- ${spring-security.version}
- pom
- import
-
-
org.springframework.boot
spring-boot-dependencies
- 2.5.15
+ 3.5.4
pom
import
@@ -67,7 +52,7 @@
com.alibaba
- druid-spring-boot-starter
+ druid-spring-boot-3-starter
${druid.version}
@@ -85,6 +70,30 @@
${pagehelper.boot.version}
+
+ org.mybatis.spring.boot
+ mybatis-spring-boot-starter
+ ${mybatis-spring-boot.version}
+
+
+
+ com.mysql
+ mysql-connector-j
+ ${mysql.version}
+
+
+
+ javax.xml.bind
+ jaxb-api
+ ${jaxb-api.version}
+
+
+
+ jakarta.servlet
+ jakarta.servlet-api
+ ${jakarta.version}
+
+
com.github.oshi
@@ -92,17 +101,11 @@
${oshi.version}
-
+
- io.springfox
- springfox-boot-starter
- ${swagger.version}
-
-
- io.swagger
- swagger-models
-
-
+ org.springdoc
+ springdoc-openapi-starter-webmvc-ui
+ ${springdoc.version}
@@ -200,13 +203,19 @@
org.apache.maven.plugins
maven-compiler-plugin
- 3.1
+ 3.13.0
+ true
${java.version}
${java.version}
${project.build.sourceEncoding}
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+ 3.3.0
+
diff --git a/ruoyi-admin/pom.xml b/ruoyi-admin/pom.xml
index e6c67123146abe02f993241d51cb7fbb3e1f21a9..991fee7e0a4ca953e4991667034bd32683c3c63b 100644
--- a/ruoyi-admin/pom.xml
+++ b/ruoyi-admin/pom.xml
@@ -5,7 +5,7 @@
ruoyi
com.ruoyi
- 3.8.7
+ 3.9.0
4.0.0
jar
@@ -24,23 +24,16 @@
true
-
+
- io.springfox
- springfox-boot-starter
-
-
-
-
- io.swagger
- swagger-models
- 1.6.2
+ org.springdoc
+ springdoc-openapi-starter-webmvc-ui
- mysql
- mysql-connector-java
+ com.mysql
+ mysql-connector-j
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CaptchaController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CaptchaController.java
index e1a397e66f78cd98343a7888cebe0489c34ee3f8..474a9122a2d7e6d73d862873c98f800e54ab5f0d 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CaptchaController.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CaptchaController.java
@@ -3,9 +3,9 @@ package com.ruoyi.web.controller.common;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
-import javax.annotation.Resource;
+import jakarta.annotation.Resource;
import javax.imageio.ImageIO;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.FastByteArrayOutputStream;
import org.springframework.web.bind.annotation.GetMapping;
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CommonController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CommonController.java
index d51d61d00238ef031a326c6f49106e67bdbc215c..74feabdea5bfff70c702433946b1b0529421aad2 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CommonController.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CommonController.java
@@ -2,8 +2,8 @@ package com.ruoyi.web.controller.common;
import java.util.ArrayList;
import java.util.List;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -14,7 +14,6 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import com.ruoyi.common.config.RuoYiConfig;
-import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.file.FileUploadUtils;
@@ -148,7 +147,7 @@ public class CommonController
// 本地资源路径
String localPath = RuoYiConfig.getProfile();
// 数据库资源地址
- String downloadPath = localPath + StringUtils.substringAfter(resource, Constants.RESOURCE_PREFIX);
+ String downloadPath = localPath + FileUtils.stripPrefix(resource);
// 下载名称
String downloadName = StringUtils.substringAfterLast(downloadPath, "/");
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/CacheController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/CacheController.java
index 504c0fde6b5c0301c56a3ac5b1086aa0f1a804ed..f265b37be80b2a02cf817103827f4c04c35f199b 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/CacheController.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/CacheController.java
@@ -45,6 +45,7 @@ public class CacheController
caches.add(new SysCache(CacheConstants.PWD_ERR_CNT_KEY, "密码错误次数"));
}
+ @SuppressWarnings("deprecation")
@PreAuthorize("@ss.hasPermi('monitor:cache:list')")
@GetMapping()
public AjaxResult getInfo() throws Exception
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysLogininforController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysLogininforController.java
index f9f262e0066505165c7e054f704a539885688ad7..e9f4b7dcef3f3199c62d6e29d001294928ed475c 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysLogininforController.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysLogininforController.java
@@ -1,7 +1,7 @@
package com.ruoyi.web.controller.monitor;
import java.util.List;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping;
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysOperlogController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysOperlogController.java
index 5378424a8dfbae1a42c04b5477fb173fc235ffad..2ada49cb8d9cea0a105dd61709b4320de9354eff 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysOperlogController.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysOperlogController.java
@@ -1,7 +1,7 @@
package com.ruoyi.web.controller.monitor;
import java.util.List;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping;
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysConfigController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysConfigController.java
index 95bcaecebb60377d150d90032be4f44d8e5dea26..6303471c19eebfe2159abd803c1663613d36c570 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysConfigController.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysConfigController.java
@@ -1,7 +1,7 @@
package com.ruoyi.web.controller.system;
import java.util.List;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictDataController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictDataController.java
index 1914b998cee5748cdf3f8f4fc590517167c44598..adc9e9d804bb5c101241ecda4f83b765d8489b3a 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictDataController.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictDataController.java
@@ -2,7 +2,7 @@ package com.ruoyi.web.controller.system;
import java.util.ArrayList;
import java.util.List;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictTypeController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictTypeController.java
index 980884246f51716ede5d66decccf55ab60a1118b..79be9e71a5670818e79894920e743b9bb3eafb65 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictTypeController.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictTypeController.java
@@ -1,7 +1,7 @@
package com.ruoyi.web.controller.system;
import java.util.List;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java
index dab2bf8792394a2aff3a9f7df74ba20a947719f2..caebb39727ccae9ef435d46857213edbe1e3bfb4 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java
@@ -1,5 +1,6 @@
package com.ruoyi.web.controller.system;
+import java.util.Date;
import java.util.List;
import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired;
@@ -12,9 +13,15 @@ import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.SysMenu;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.LoginBody;
+import com.ruoyi.common.core.domain.model.LoginUser;
+import com.ruoyi.common.core.text.Convert;
+import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.framework.web.service.SysLoginService;
import com.ruoyi.framework.web.service.SysPermissionService;
+import com.ruoyi.framework.web.service.TokenService;
+import com.ruoyi.system.service.ISysConfigService;
import com.ruoyi.system.service.ISysMenuService;
/**
@@ -34,6 +41,12 @@ public class SysLoginController
@Autowired
private SysPermissionService permissionService;
+ @Autowired
+ private TokenService tokenService;
+
+ @Autowired
+ private ISysConfigService configService;
+
/**
* 登录方法
*
@@ -59,15 +72,23 @@ public class SysLoginController
@GetMapping("getInfo")
public AjaxResult getInfo()
{
- SysUser user = SecurityUtils.getLoginUser().getUser();
+ LoginUser loginUser = SecurityUtils.getLoginUser();
+ SysUser user = loginUser.getUser();
// 角色集合
Set roles = permissionService.getRolePermission(user);
// 权限集合
Set 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);
ajax.put("permissions", permissions);
+ ajax.put("isDefaultModifyPwd", initPasswordIsModify(user.getPwdUpdateDate()));
+ ajax.put("isPasswordExpired", passwordIsExpiration(user.getPwdUpdateDate()));
return ajax;
}
@@ -83,4 +104,28 @@ public class SysLoginController
List menus = menuService.selectMenuTreeByUserId(userId);
return AjaxResult.success(menuService.buildMenus(menus));
}
+
+ // 检查初始密码是否提醒修改
+ public boolean initPasswordIsModify(Date pwdUpdateDate)
+ {
+ Integer initPasswordModify = Convert.toInt(configService.selectConfigByKey("sys.account.initPasswordModify"));
+ return initPasswordModify != null && initPasswordModify == 1 && pwdUpdateDate == null;
+ }
+
+ // 检查密码是否过期
+ public boolean passwordIsExpiration(Date pwdUpdateDate)
+ {
+ Integer passwordValidateDays = Convert.toInt(configService.selectConfigByKey("sys.account.passwordValidateDays"));
+ if (passwordValidateDays != null && passwordValidateDays > 0)
+ {
+ if (StringUtils.isNull(pwdUpdateDate))
+ {
+ // 如果从未修改过初始密码,直接提醒过期
+ return true;
+ }
+ Date nowDate = DateUtils.getNowDate();
+ return DateUtils.differentDaysByMillisecond(nowDate, pwdUpdateDate) > passwordValidateDays;
+ }
+ return false;
+ }
}
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysPostController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysPostController.java
index a5b96c8765baabff14ed852ab0671a7440216c0c..a4236292d8d7aa68b17666c4c8a909d1ee20204e 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysPostController.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysPostController.java
@@ -1,7 +1,7 @@
package com.ruoyi.web.controller.system;
import java.util.List;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java
index cd8abd6f50e2b013155f0d0e07617399627b54ae..5b02a70ab2c256840a9e82dbbcf1bcc32dd0f250 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java
@@ -1,5 +1,6 @@
package com.ruoyi.web.controller.system;
+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;
@@ -16,9 +17,11 @@ import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.enums.BusinessType;
+import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.file.FileUploadUtils;
+import com.ruoyi.common.utils.file.FileUtils;
import com.ruoyi.common.utils.file.MimeTypeUtils;
import com.ruoyi.framework.web.service.TokenService;
import com.ruoyi.system.service.ISysUserService;
@@ -87,10 +90,12 @@ public class SysProfileController extends BaseController
*/
@Log(title = "个人信息", businessType = BusinessType.UPDATE)
@PutMapping("/updatePwd")
- public AjaxResult updatePwd(String oldPassword, String newPassword)
+ public AjaxResult updatePwd(@RequestBody Map params)
{
+ String oldPassword = params.get("oldPassword");
+ String newPassword = params.get("newPassword");
LoginUser loginUser = getLoginUser();
- String userName = loginUser.getUsername();
+ Long userId = loginUser.getUserId();
String password = loginUser.getPassword();
if (!SecurityUtils.matchesPassword(oldPassword, password))
{
@@ -101,9 +106,10 @@ public class SysProfileController extends BaseController
return error("新密码不能与旧密码相同");
}
newPassword = SecurityUtils.encryptPassword(newPassword);
- if (userService.resetUserPwd(userName, newPassword) > 0)
+ if (userService.resetUserPwd(userId, newPassword) > 0)
{
- // 更新缓存用户密码
+ // 更新缓存用户密码&密码最后更新时间
+ loginUser.getUser().setPwdUpdateDate(DateUtils.getNowDate());
loginUser.getUser().setPassword(newPassword);
tokenService.setLoginUser(loginUser);
return success();
@@ -121,9 +127,14 @@ public class SysProfileController extends BaseController
if (!file.isEmpty())
{
LoginUser loginUser = getLoginUser();
- String avatar = FileUploadUtils.upload(RuoYiConfig.getAvatarPath(), file, MimeTypeUtils.IMAGE_EXTENSION);
- if (userService.updateUserAvatar(loginUser.getUsername(), avatar))
+ String avatar = FileUploadUtils.upload(RuoYiConfig.getAvatarPath(), file, MimeTypeUtils.IMAGE_EXTENSION, true);
+ if (userService.updateUserAvatar(loginUser.getUserId(), avatar))
{
+ String oldAvatar = loginUser.getUser().getAvatar();
+ if (StringUtils.isNotEmpty(oldAvatar))
+ {
+ FileUtils.deleteFile(RuoYiConfig.getProfile() + FileUtils.stripPrefix(oldAvatar));
+ }
AjaxResult ajax = AjaxResult.success();
ajax.put("imgUrl", avatar);
// 更新缓存用户头像
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRoleController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRoleController.java
index ebe7460e19d1fc2bf33ad56b2ce0782a0abce13c..a794936d4ae03147fe089663117f08ed6756752a 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRoleController.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRoleController.java
@@ -1,7 +1,7 @@
package com.ruoyi.web.controller.system;
import java.util.List;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
@@ -132,8 +132,8 @@ public class SysRoleController extends BaseController
LoginUser loginUser = getLoginUser();
if (StringUtils.isNotNull(loginUser.getUser()) && !loginUser.getUser().isAdmin())
{
- loginUser.setPermissions(permissionService.getMenuPermission(loginUser.getUser()));
loginUser.setUser(userService.selectUserByUserName(loginUser.getUser().getUserName()));
+ loginUser.setPermissions(permissionService.getMenuPermission(loginUser.getUser()));
tokenService.setLoginUser(loginUser);
}
return success();
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java
index 24aafae73faf33e7af79996d3c999f9688bf86e1..b1bef02ecfef5dc20baf16f79d4cbddc7679a04f 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java
@@ -2,7 +2,7 @@ package com.ruoyi.web.controller.system;
import java.util.List;
import java.util.stream.Collectors;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
@@ -101,18 +101,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 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 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;
}
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/TestController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/TestController.java
index c55f97496a7747a8672c28d9fc3aae4d13318b0f..b56a09766d00530e48b2fb156c201824d699ee64 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/TestController.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/TestController.java
@@ -15,19 +15,16 @@ import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.utils.StringUtils;
-import io.swagger.annotations.Api;
-import io.swagger.annotations.ApiImplicitParam;
-import io.swagger.annotations.ApiImplicitParams;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
-import io.swagger.annotations.ApiOperation;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.tags.Tag;
/**
* swagger 用户测试方法
- *
+ *
* @author ruoyi
*/
-@Api("用户信息管理")
+@Tag(name = "用户信息管理")
@RestController
@RequestMapping("/test/user")
public class TestController extends BaseController
@@ -37,19 +34,19 @@ public class TestController extends BaseController
users.put(1, new UserEntity(1, "admin", "admin123", "15888888888"));
users.put(2, new UserEntity(2, "ry", "admin123", "15666666666"));
}
-
- @ApiOperation("获取用户列表")
+
+ @Operation(summary = "获取用户列表")
@GetMapping("/list")
public R> userList()
{
List userList = new ArrayList(users.values());
return R.ok(userList);
}
-
- @ApiOperation("获取用户详细")
- @ApiImplicitParam(name = "userId", value = "用户ID", required = true, dataType = "int", paramType = "path", dataTypeClass = Integer.class)
+
+ @Operation(summary = "获取用户详细")
@GetMapping("/{userId}")
- public R getUser(@PathVariable Integer userId)
+ public R getUser(@PathVariable(name = "userId")
+ Integer userId)
{
if (!users.isEmpty() && users.containsKey(userId))
{
@@ -60,14 +57,8 @@ public class TestController extends BaseController
return R.fail("用户不存在");
}
}
-
- @ApiOperation("新增用户")
- @ApiImplicitParams({
- @ApiImplicitParam(name = "userId", value = "用户id", dataType = "Integer", dataTypeClass = Integer.class),
- @ApiImplicitParam(name = "username", value = "用户名称", dataType = "String", dataTypeClass = String.class),
- @ApiImplicitParam(name = "password", value = "用户密码", dataType = "String", dataTypeClass = String.class),
- @ApiImplicitParam(name = "mobile", value = "用户手机", dataType = "String", dataTypeClass = String.class)
- })
+
+ @Operation(summary = "新增用户")
@PostMapping("/save")
public R save(UserEntity user)
{
@@ -78,10 +69,11 @@ public class TestController extends BaseController
users.put(user.getUserId(), user);
return R.ok();
}
-
- @ApiOperation("更新用户")
+
+ @Operation(summary = "更新用户")
@PutMapping("/update")
- public R update(@RequestBody UserEntity user)
+ public R update(@RequestBody
+ UserEntity user)
{
if (StringUtils.isNull(user) || StringUtils.isNull(user.getUserId()))
{
@@ -95,11 +87,11 @@ public class TestController extends BaseController
users.put(user.getUserId(), user);
return R.ok();
}
-
- @ApiOperation("删除用户信息")
- @ApiImplicitParam(name = "userId", value = "用户ID", required = true, dataType = "int", paramType = "path", dataTypeClass = Integer.class)
+
+ @Operation(summary = "删除用户信息")
@DeleteMapping("/{userId}")
- public R delete(@PathVariable Integer userId)
+ public R delete(@PathVariable(name = "userId")
+ Integer userId)
{
if (!users.isEmpty() && users.containsKey(userId))
{
@@ -113,26 +105,26 @@ public class TestController extends BaseController
}
}
-@ApiModel(value = "UserEntity", description = "用户实体")
+@Schema(description = "用户实体")
class UserEntity
{
- @ApiModelProperty("用户ID")
+ @Schema(title = "用户ID")
private Integer userId;
-
- @ApiModelProperty("用户名称")
+
+ @Schema(title = "用户名称")
private String username;
-
- @ApiModelProperty("用户密码")
+
+ @Schema(title = "用户密码")
private String password;
-
- @ApiModelProperty("用户手机")
+
+ @Schema(title = "用户手机")
private String mobile;
-
+
public UserEntity()
{
-
+
}
-
+
public UserEntity(Integer userId, String username, String password, String mobile)
{
this.userId = userId;
@@ -140,42 +132,42 @@ class UserEntity
this.password = password;
this.mobile = mobile;
}
-
+
public Integer getUserId()
{
return userId;
}
-
+
public void setUserId(Integer userId)
{
this.userId = userId;
}
-
+
public String getUsername()
{
return username;
}
-
+
public void setUsername(String username)
{
this.username = username;
}
-
+
public String getPassword()
{
return password;
}
-
+
public void setPassword(String password)
{
this.password = password;
}
-
+
public String getMobile()
{
return mobile;
}
-
+
public void setMobile(String mobile)
{
this.mobile = mobile;
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/core/config/SwaggerConfig.java b/ruoyi-admin/src/main/java/com/ruoyi/web/core/config/SwaggerConfig.java
index 0b94245079a0c667c0ba77bf93b3b20144fc1d0c..91cadcaff1988c676caa502b908a1d444a0e6a4e 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/core/config/SwaggerConfig.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/core/config/SwaggerConfig.java
@@ -1,26 +1,15 @@
package com.ruoyi.web.core.config;
-import java.util.ArrayList;
-import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.ruoyi.common.config.RuoYiConfig;
-import io.swagger.annotations.ApiOperation;
-import io.swagger.models.auth.In;
-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.Docket;
+import io.swagger.v3.oas.models.Components;
+import io.swagger.v3.oas.models.OpenAPI;
+import io.swagger.v3.oas.models.info.Contact;
+import io.swagger.v3.oas.models.info.Info;
+import io.swagger.v3.oas.models.security.SecurityRequirement;
+import io.swagger.v3.oas.models.security.SecurityScheme;
/**
* Swagger2的接口配置
@@ -33,93 +22,43 @@ public class SwaggerConfig
/** 系统基础配置 */
@Autowired
private RuoYiConfig ruoyiConfig;
-
- /** 是否开启swagger */
- @Value("${swagger.enabled}")
- private boolean enabled;
-
- /** 设置请求的统一前缀 */
- @Value("${swagger.pathMapping}")
- private String pathMapping;
-
+
/**
- * 创建API
+ * 自定义的 OpenAPI 对象
*/
@Bean
- public Docket createRestApi()
- {
- return new Docket(DocumentationType.OAS_30)
- // 是否启用Swagger
- .enable(enabled)
- // 用来创建该API的基本信息,展示在文档的页面中(自定义展示的信息)
- .apiInfo(apiInfo())
- // 设置哪些接口暴露给Swagger展示
- .select()
- // 扫描所有有注解的api,用这种方式更灵活
- .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
- // 扫描指定包中的swagger注解
- // .apis(RequestHandlerSelectors.basePackage("com.ruoyi.project.tool.swagger"))
- // 扫描所有 .apis(RequestHandlerSelectors.any())
- .paths(PathSelectors.any())
- .build()
- /* 设置安全模式,swagger可以设置访问token */
- .securitySchemes(securitySchemes())
- .securityContexts(securityContexts())
- .pathMapping(pathMapping);
- }
-
- /**
- * 安全模式,这里指定token通过Authorization头请求头传递
- */
- private List securitySchemes()
- {
- List apiKeyList = new ArrayList();
- apiKeyList.add(new ApiKey("Authorization", "Authorization", In.HEADER.toValue()));
- return apiKeyList;
- }
-
- /**
- * 安全上下文
- */
- private List securityContexts()
+ public OpenAPI customOpenApi()
{
- List securityContexts = new ArrayList<>();
- securityContexts.add(
- SecurityContext.builder()
- .securityReferences(defaultAuth())
- .operationSelector(o -> o.requestMappingPattern().matches("/.*"))
- .build());
- return securityContexts;
+ return new OpenAPI().components(new Components()
+ // 设置认证的请求头
+ .addSecuritySchemes("apikey", securityScheme()))
+ .addSecurityItem(new SecurityRequirement().addList("apikey"))
+ .info(getApiInfo());
}
-
- /**
- * 默认的安全上引用
- */
- private List defaultAuth()
+
+ @Bean
+ public SecurityScheme securityScheme()
{
- AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
- AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
- authorizationScopes[0] = authorizationScope;
- List securityReferences = new ArrayList<>();
- securityReferences.add(new SecurityReference("Authorization", authorizationScopes));
- return securityReferences;
+ return new SecurityScheme()
+ .type(SecurityScheme.Type.APIKEY)
+ .name("Authorization")
+ .in(SecurityScheme.In.HEADER)
+ .scheme("Bearer");
}
-
+
/**
* 添加摘要信息
*/
- private ApiInfo apiInfo()
+ public Info getApiInfo()
{
- // 用ApiInfoBuilder进行定制
- return new ApiInfoBuilder()
- // 设置标题
- .title("标题:若依管理系统_接口文档")
- // 描述
- .description("描述:用于管理集团旗下公司的人员信息,具体包括XXX,XXX模块...")
- // 作者信息
- .contact(new Contact(ruoyiConfig.getName(), null, null))
- // 版本
- .version("版本号:" + ruoyiConfig.getVersion())
- .build();
+ return new Info()
+ // 设置标题
+ .title("标题:若依管理系统_接口文档")
+ // 描述
+ .description("描述:用于管理集团旗下公司的人员信息,具体包括XXX,XXX模块...")
+ // 作者信息
+ .contact(new Contact().name(ruoyiConfig.getName()))
+ // 版本
+ .version("版本号:" + ruoyiConfig.getVersion());
}
}
diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml
index 6db16fbf88440a533cb01e8878f9314112bb0bc5..08fbc05f73457425d59d5c57efeda1750b8a4219 100644
--- a/ruoyi-admin/src/main/resources/application.yml
+++ b/ruoyi-admin/src/main/resources/application.yml
@@ -3,9 +3,9 @@ ruoyi:
# 名称
name: RuoYi
# 版本
- version: 3.8.7
+ version: 3.9.0
# 版权年份
- copyrightYear: 2024
+ copyrightYear: 2025
# 文件路径 示例( Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath)
profile: D:/ruoyi/uploadPath
# 获取ip地址开关
@@ -65,28 +65,29 @@ spring:
restart:
# 热部署开关
enabled: true
- # redis 配置
- redis:
- # 地址
- host: localhost
- # 端口,默认为6379
- port: 6379
- # 数据库索引
- database: 0
- # 密码
- password:
- # 连接超时时间
- timeout: 10s
- lettuce:
- pool:
- # 连接池中的最小空闲连接
- min-idle: 0
- # 连接池中的最大空闲连接
- max-idle: 8
- # 连接池的最大数据库连接数
- max-active: 8
- # #连接池最大阻塞等待时间(使用负值表示没有限制)
- max-wait: -1ms
+ data:
+ # redis 配置
+ redis:
+ # 地址
+ host: localhost
+ # 端口,默认为6379
+ port: 6379
+ # 数据库索引
+ database: 0
+ # 密码
+ password:
+ # 连接超时时间
+ timeout: 10s
+ lettuce:
+ pool:
+ # 连接池中的最小空闲连接
+ min-idle: 0
+ # 连接池中的最大空闲连接
+ max-idle: 8
+ # 连接池的最大数据库连接数
+ max-active: 8
+ # #连接池最大阻塞等待时间(使用负值表示没有限制)
+ max-wait: -1ms
# token配置
token:
@@ -112,12 +113,26 @@ pagehelper:
supportMethodsArguments: true
params: count=countSql
-# Swagger配置
-swagger:
- # 是否开启swagger
- enabled: true
- # 请求前缀
- pathMapping: /dev-api
+# Springdoc配置
+springdoc:
+ api-docs:
+ path: /v3/api-docs
+ swagger-ui:
+ enabled: true
+ path: /swagger-ui.html
+ tags-sorter: alpha
+ group-configs:
+ - group: 'default'
+ display-name: '测试模块'
+ paths-to-match: '/**'
+ packages-to-scan: com.ruoyi.web.controller.tool
+
+# 防盗链配置
+referer:
+ # 防盗链开关
+ enabled: false
+ # 允许的域名列表
+ allowed-domains: localhost,127.0.0.1,ruoyi.vip,www.ruoyi.vip
# 防止XSS攻击
xss:
diff --git a/ruoyi-common/pom.xml b/ruoyi-common/pom.xml
index 5b3273dba961d3d08e053590e1d85a0112a94b6f..7f8bf812722c6a58afb076e6526eabcb1314cdf7 100644
--- a/ruoyi-common/pom.xml
+++ b/ruoyi-common/pom.xml
@@ -5,7 +5,7 @@
ruoyi
com.ruoyi
- 3.8.7
+ 3.9.0
4.0.0
@@ -115,8 +115,8 @@
- javax.servlet
- javax.servlet-api
+ jakarta.servlet
+ jakarta.servlet-api
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Excel.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Excel.java
index 0d69d3940d5c5f2016d462b97c702394bab9bd0b..1d87437fd07e37ee816ba37b123a481cad2af10e 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Excel.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Excel.java
@@ -56,6 +56,7 @@ public @interface Excel
/**
* BigDecimal 舍入规则 默认:BigDecimal.ROUND_HALF_EVEN
*/
+ @SuppressWarnings("deprecation")
public int roundingMode() default BigDecimal.ROUND_HALF_EVEN;
/**
@@ -83,6 +84,11 @@ public @interface Excel
*/
public String prompt() default "";
+ /**
+ * 是否允许内容换行
+ */
+ public boolean wrapText() default false;
+
/**
* 设置只能选择不能输入的列内容.
*/
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java b/ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java
index a94c3fa35e7fc60555c30a734d7091b474ff7d5e..44ec99c63ac123a04c78e1f774b800d4b314848e 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java
@@ -158,7 +158,7 @@ public class Constants
/**
* 自动识别json对象白名单配置(仅允许解析的包名,范围越小越安全)
*/
- public static final String[] JSON_WHITELIST_STR = { "org.springframework", "com.ruoyi" };
+ public static final String[] JSON_WHITELIST_STR = { "com.ruoyi" };
/**
* 定时任务白名单配置(仅允许访问的包名,如其他需要可以自行添加)
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/constant/UserConstants.java b/ruoyi-common/src/main/java/com/ruoyi/common/constant/UserConstants.java
index b09b6f3d5c321ad6d57658b1bcf87f0f54b7ff89..f2b5ab58841752d2744b02542188949e00660743 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/constant/UserConstants.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/constant/UserConstants.java
@@ -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";
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/TreeSelect.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/TreeSelect.java
index 4a59e402a001a3f2603dd14454452be01412fb02..a762704c52093b48ee81a10b5f3abca59f682958 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/TreeSelect.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/TreeSelect.java
@@ -4,8 +4,10 @@ import java.io.Serializable;
import java.util.List;
import java.util.stream.Collectors;
import com.fasterxml.jackson.annotation.JsonInclude;
+import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.common.core.domain.entity.SysMenu;
+import com.ruoyi.common.utils.StringUtils;
/**
* Treeselect树结构实体类
@@ -22,6 +24,9 @@ public class TreeSelect implements Serializable
/** 节点名称 */
private String label;
+ /** 节点禁用 */
+ private boolean disabled = false;
+
/** 子节点 */
@JsonInclude(JsonInclude.Include.NON_EMPTY)
private List 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 getChildren()
{
return children;
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDept.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDept.java
index 693461b9fc1f989725df8f3f74d903a4d6b4e9a3..a9df3a6eb7c9449859ff6187fe492bd2ad989bdd 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDept.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDept.java
@@ -2,10 +2,10 @@ package com.ruoyi.common.core.domain.entity;
import java.util.ArrayList;
import java.util.List;
-import javax.validation.constraints.Email;
-import javax.validation.constraints.NotBlank;
-import javax.validation.constraints.NotNull;
-import javax.validation.constraints.Size;
+import jakarta.validation.constraints.Email;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.ruoyi.common.core.domain.BaseEntity;
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictData.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictData.java
index e79eaa20c1da39cbcb333f9de0555b76965a22f2..0305bff81abf1ef432f6967400a3175c2662e6c8 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictData.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictData.java
@@ -1,7 +1,7 @@
package com.ruoyi.common.core.domain.entity;
-import javax.validation.constraints.NotBlank;
-import javax.validation.constraints.Size;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.Size;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.ruoyi.common.annotation.Excel;
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictType.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictType.java
index 0befbf434da391b8b8fa20a236587ce0565ee8a1..272f2ca230b7f306f86af8e47119fb4ba0d73ebd 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictType.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictType.java
@@ -1,8 +1,8 @@
package com.ruoyi.common.core.domain.entity;
-import javax.validation.constraints.NotBlank;
-import javax.validation.constraints.Pattern;
-import javax.validation.constraints.Size;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.Pattern;
+import jakarta.validation.constraints.Size;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.ruoyi.common.annotation.Excel;
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysMenu.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysMenu.java
index 9f3a6f6593065e8f9f76adf8f78dca93d1bbc09c..6dddb79e79b0155c1e4d27b0705eb532e1785f0c 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysMenu.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysMenu.java
@@ -2,9 +2,9 @@ package com.ruoyi.common.core.domain.entity;
import java.util.ArrayList;
import java.util.List;
-import javax.validation.constraints.NotBlank;
-import javax.validation.constraints.NotNull;
-import javax.validation.constraints.Size;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.ruoyi.common.core.domain.BaseEntity;
@@ -42,6 +42,9 @@ public class SysMenu extends BaseEntity
/** 路由参数 */
private String query;
+ /** 路由名称,默认和路由地址相同的驼峰格式(注意:因为vue3版本的router会删除名称相同路由,为避免名字的冲突,特殊情况可以自定义) */
+ private String routeName;
+
/** 是否为外链(0是 1否) */
private String isFrame;
@@ -53,7 +56,7 @@ public class SysMenu extends BaseEntity
/** 显示状态(0显示 1隐藏) */
private String visible;
-
+
/** 菜单状态(0正常 1停用) */
private String status;
@@ -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())
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysRole.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysRole.java
index 2396f8022e24058dd392cc981abe75515d83e1cd..db4898f0679328026d9c5357c5ffdf4c2f35713e 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysRole.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysRole.java
@@ -1,9 +1,9 @@
package com.ruoyi.common.core.domain.entity;
import java.util.Set;
-import javax.validation.constraints.NotBlank;
-import javax.validation.constraints.NotNull;
-import javax.validation.constraints.Size;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.ruoyi.common.annotation.Excel;
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java
index c33d91246247813ea0f95e742bedb29aff991d2a..88ed25db265c66bec59da7bd50ae7ae43c39b575 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java
@@ -2,7 +2,7 @@ package com.ruoyi.common.core.domain.entity;
import java.util.Date;
import java.util.List;
-import javax.validation.constraints.*;
+import jakarta.validation.constraints.*;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.ruoyi.common.annotation.Excel;
@@ -55,8 +55,8 @@ public class SysUser extends BaseEntity
/** 密码 */
private String password;
- /** 帐号状态(0正常 1停用) */
- @Excel(name = "帐号状态", readConverterExp = "0=正常,1=停用")
+ /** 账号状态(0正常 1停用) */
+ @Excel(name = "账号状态", readConverterExp = "0=正常,1=停用")
private String status;
/** 删除标志(0代表存在 2代表删除) */
@@ -70,6 +70,9 @@ public class SysUser extends BaseEntity
@Excel(name = "最后登录时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss", type = Type.EXPORT)
private Date loginDate;
+ /** 密码最后更新时间 */
+ private Date pwdUpdateDate;
+
/** 部门对象 */
@Excels({
@Excel(name = "部门名称", targetAttr = "deptName", type = Type.EXPORT),
@@ -247,6 +250,16 @@ public class SysUser extends BaseEntity
this.loginDate = loginDate;
}
+ public Date getPwdUpdateDate()
+ {
+ return pwdUpdateDate;
+ }
+
+ public void setPwdUpdateDate(Date pwdUpdateDate)
+ {
+ this.pwdUpdateDate = pwdUpdateDate;
+ }
+
public SysDept getDept()
{
return dept;
@@ -313,6 +326,7 @@ public class SysUser extends BaseEntity
.append("delFlag", getDelFlag())
.append("loginIp", getLoginIp())
.append("loginDate", getLoginDate())
+ .append("pwdUpdateDate", getPwdUpdateDate())
.append("createBy", getCreateBy())
.append("createTime", getCreateTime())
.append("updateBy", getUpdateBy())
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/page/TableDataInfo.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/page/TableDataInfo.java
index a3487b8ed118c9dfbe2e8394c64f4a11aaf57c1b..f0befe7cff2d3e40fffd9f6c18a8408d8653b396 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/core/page/TableDataInfo.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/page/TableDataInfo.java
@@ -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;
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/text/Convert.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/text/Convert.java
index 938d0c9f22134f4536de73fba99c6901ee81d450..32bbfdb8140847fd9abb0ce2bae57e7e01fed4c3 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/core/text/Convert.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/text/Convert.java
@@ -8,7 +8,6 @@ import java.nio.charset.Charset;
import java.text.NumberFormat;
import java.util.Set;
import com.ruoyi.common.utils.StringUtils;
-import org.apache.commons.lang3.ArrayUtils;
/**
* 类型转换器
@@ -541,7 +540,7 @@ public class Convert
/**
* 转换为boolean
- * String支持的值为:true、false、yes、ok、no,1,0 如果给定的值为空,或者转换失败,返回默认值
+ * String支持的值为:true、false、yes、ok、no、1、0、是、否, 如果给定的值为空,或者转换失败,返回默认值
* 转换失败不会报错
*
* @param value 被转换的值
@@ -570,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;
@@ -796,14 +797,23 @@ public class Convert
{
return (String) obj;
}
- else if (obj instanceof byte[])
+ else if (obj instanceof byte[] || obj instanceof Byte[])
{
- return str((byte[]) obj, charset);
- }
- else if (obj instanceof Byte[])
- {
- byte[] bytes = ArrayUtils.toPrimitive((Byte[]) obj);
- return str(bytes, charset);
+ if (obj instanceof byte[])
+ {
+ return str((byte[]) obj, charset);
+ }
+ else
+ {
+ Byte[] bytes = (Byte[]) obj;
+ int length = bytes.length;
+ byte[] dest = new byte[length];
+ for (int i = 0; i < length; i++)
+ {
+ dest[i] = bytes[i];
+ }
+ return str(dest, charset);
+ }
}
else if (obj instanceof ByteBuffer)
{
@@ -959,9 +969,7 @@ public class Convert
c[i] = (char) (c[i] - 65248);
}
}
- String returnString = new String(c);
-
- return returnString;
+ return new String(c);
}
/**
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/DesensitizedType.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/DesensitizedType.java
index 450812293b9fcb2dd522ee94b82aab871fc77b64..07f02eebc812827df0d9ff59e9ca595d88dacc7b 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/enums/DesensitizedType.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/DesensitizedType.java
@@ -23,7 +23,7 @@ public enum DesensitizedType
/**
* 身份证,中间10位星号替换
*/
- ID_CARD(s -> s.replaceAll("(\\d{4})\\d{10}(\\d{4})", "$1** **** ****$2")),
+ ID_CARD(s -> s.replaceAll("(\\d{4})\\d{10}(\\d{3}[Xx]|\\d{4})", "$1** **** ****$2")),
/**
* 手机号,中间4位星号替换
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/filter/RefererFilter.java b/ruoyi-common/src/main/java/com/ruoyi/common/filter/RefererFilter.java
new file mode 100644
index 0000000000000000000000000000000000000000..ac9ed163897a3f4d1b23b507b6a0a5d9bb0ab61c
--- /dev/null
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/filter/RefererFilter.java
@@ -0,0 +1,77 @@
+package com.ruoyi.common.filter;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+import jakarta.servlet.Filter;
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.FilterConfig;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.ServletRequest;
+import jakarta.servlet.ServletResponse;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+/**
+ * 防盗链过滤器
+ *
+ * @author ruoyi
+ */
+public class RefererFilter implements Filter
+{
+ /**
+ * 允许的域名列表
+ */
+ public List allowedDomains;
+
+ @Override
+ public void init(FilterConfig filterConfig) throws ServletException
+ {
+ String domains = filterConfig.getInitParameter("allowedDomains");
+ this.allowedDomains = Arrays.asList(domains.split(","));
+ }
+
+ @Override
+ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+ throws IOException, ServletException
+ {
+ HttpServletRequest req = (HttpServletRequest) request;
+ HttpServletResponse resp = (HttpServletResponse) response;
+
+ String referer = req.getHeader("Referer");
+
+ // 如果Referer为空,拒绝访问
+ if (referer == null || referer.isEmpty())
+ {
+ resp.sendError(HttpServletResponse.SC_FORBIDDEN, "Access denied: Referer header is required");
+ return;
+ }
+
+ // 检查Referer是否在允许的域名列表中
+ boolean allowed = false;
+ for (String domain : allowedDomains)
+ {
+ if (referer.contains(domain))
+ {
+ allowed = true;
+ break;
+ }
+ }
+
+ // 根据检查结果决定是否放行
+ if (allowed)
+ {
+ chain.doFilter(request, response);
+ }
+ else
+ {
+ resp.sendError(HttpServletResponse.SC_FORBIDDEN, "Access denied: Referer '" + referer + "' is not allowed");
+ }
+ }
+
+ @Override
+ public void destroy()
+ {
+
+ }
+}
\ No newline at end of file
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatableFilter.java b/ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatableFilter.java
index 3946d48dfff631f7d6d4ef6804d85b19c2896dc9..722e52e99079ae99bd31d4cb8ad34c467320e9dd 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatableFilter.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatableFilter.java
@@ -1,13 +1,13 @@
package com.ruoyi.common.filter;
import java.io.IOException;
-import javax.servlet.Filter;
-import javax.servlet.FilterChain;
-import javax.servlet.FilterConfig;
-import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.HttpServletRequest;
+import jakarta.servlet.Filter;
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.FilterConfig;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.ServletRequest;
+import jakarta.servlet.ServletResponse;
+import jakarta.servlet.http.HttpServletRequest;
import org.springframework.http.MediaType;
import com.ruoyi.common.utils.StringUtils;
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatedlyRequestWrapper.java b/ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatedlyRequestWrapper.java
index ca822edeca8e5fd4c5ace743d3f8cbffb367dbe8..53416dac80be12d0252c110d39cc5eb88a729cb9 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatedlyRequestWrapper.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatedlyRequestWrapper.java
@@ -4,11 +4,11 @@ import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
-import javax.servlet.ReadListener;
-import javax.servlet.ServletInputStream;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletRequestWrapper;
+import jakarta.servlet.ReadListener;
+import jakarta.servlet.ServletInputStream;
+import jakarta.servlet.ServletResponse;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletRequestWrapper;
import com.ruoyi.common.utils.http.HttpHelper;
import com.ruoyi.common.constant.Constants;
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/filter/XssFilter.java b/ruoyi-common/src/main/java/com/ruoyi/common/filter/XssFilter.java
index b4eaabcb6d53c1d015d9173aae678d61bed30c03..db6e9b3f80d293cbb8ef48421cc7dd47a11ab0fa 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/filter/XssFilter.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/filter/XssFilter.java
@@ -3,14 +3,14 @@ package com.ruoyi.common.filter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
-import javax.servlet.Filter;
-import javax.servlet.FilterChain;
-import javax.servlet.FilterConfig;
-import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.Filter;
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.FilterConfig;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.ServletRequest;
+import jakarta.servlet.ServletResponse;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.enums.HttpMethod;
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/filter/XssHttpServletRequestWrapper.java b/ruoyi-common/src/main/java/com/ruoyi/common/filter/XssHttpServletRequestWrapper.java
index e0cc40a8d0efcc54141c909dc9aabdacc7df3e02..9f89bdd382ea803511e9fd84a5df6bd86b008e32 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/filter/XssHttpServletRequestWrapper.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/filter/XssHttpServletRequestWrapper.java
@@ -2,10 +2,10 @@ package com.ruoyi.common.filter;
import java.io.ByteArrayInputStream;
import java.io.IOException;
-import javax.servlet.ReadListener;
-import javax.servlet.ServletInputStream;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletRequestWrapper;
+import jakarta.servlet.ReadListener;
+import jakarta.servlet.ServletInputStream;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletRequestWrapper;
import org.apache.commons.io.IOUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/Arith.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/Arith.java
index b6326c2b1bff208446fa9c45530b399f0ea7f0cd..9f95c0f356512427101ec6dfb957247954c7d77e 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/Arith.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/Arith.java
@@ -108,7 +108,6 @@ public class Arith
"The scale must be a positive integer or zero");
}
BigDecimal b = new BigDecimal(Double.toString(v));
- BigDecimal one = BigDecimal.ONE;
- return b.divide(one, scale, RoundingMode.HALF_UP).doubleValue();
+ return b.divide(BigDecimal.ONE, scale, RoundingMode.HALF_UP).doubleValue();
}
}
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/DateUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/DateUtils.java
index 20dbbb74604623b45f237b42911397f4b2b23784..07ac18e985be8893590df605be1a49ead8ce974c 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/DateUtils.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/DateUtils.java
@@ -16,6 +16,7 @@ import org.apache.commons.lang3.time.DateFormatUtils;
*
* @author ruoyi
*/
+@SuppressWarnings("deprecation")
public class DateUtils extends org.apache.commons.lang3.time.DateUtils
{
public static String YYYY = "yyyy";
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/ServletUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ServletUtils.java
index 292b8dacc156406923dbc92d3f65ab2ea4718758..a7882ee0c25534603430be8249a0f0349ec45a98 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/ServletUtils.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ServletUtils.java
@@ -7,10 +7,10 @@ import java.net.URLEncoder;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
-import javax.servlet.ServletRequest;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpSession;
+import jakarta.servlet.ServletRequest;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpSession;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/StringUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/StringUtils.java
index fc6c6b5e086527700cbd568e43a02fa7b1da983c..67988b104237838d7efc928b402afb89c433b6c9 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/StringUtils.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/StringUtils.java
@@ -15,6 +15,7 @@ import com.ruoyi.common.core.text.StrFormatter;
*
* @author ruoyi
*/
+@SuppressWarnings("deprecation")
public class StringUtils extends org.apache.commons.lang3.StringUtils
{
/** 空字符串 */
@@ -286,6 +287,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;
+ }
+
/**
* 判断是否为空,并且不是空白字符
*
@@ -355,6 +382,18 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils
return new HashSet(str2List(str, sep, true, false));
}
+ /**
+ * 字符串转list
+ *
+ * @param str 字符串
+ * @param sep 分隔符
+ * @return list集合
+ */
+ public static final List str2List(String str, String sep)
+ {
+ return str2List(str, sep, true, false);
+ }
+
/**
* 字符串转list
*
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/bean/BeanValidators.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/bean/BeanValidators.java
index d9821e0ec8c65a8ffc36a9036e77c3a3613edc16..03e8972191ebd32e1bcdb2a693a1de1b0469aea5 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/bean/BeanValidators.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/bean/BeanValidators.java
@@ -1,9 +1,9 @@
package com.ruoyi.common.utils.bean;
import java.util.Set;
-import javax.validation.ConstraintViolation;
-import javax.validation.ConstraintViolationException;
-import javax.validation.Validator;
+import jakarta.validation.ConstraintViolation;
+import jakarta.validation.ConstraintViolationException;
+import jakarta.validation.Validator;
/**
* bean对象属性验证
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUploadUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUploadUtils.java
index 5a0ef64004382e0f2c99dbbf0993d61e37bafbc9..75bbacdc66560eba52b70e886717c3e0d97520fb 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUploadUtils.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUploadUtils.java
@@ -13,11 +13,12 @@ import com.ruoyi.common.exception.file.FileSizeLimitExceededException;
import com.ruoyi.common.exception.file.InvalidExtensionException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.common.utils.uuid.IdUtils;
import com.ruoyi.common.utils.uuid.Seq;
/**
* 文件上传工具类
- *
+ *
* @author ruoyi
*/
public class FileUploadUtils
@@ -102,15 +103,35 @@ public class FileUploadUtils
throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException,
InvalidExtensionException
{
- int fileNamelength = Objects.requireNonNull(file.getOriginalFilename()).length();
- if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH)
+ return upload(baseDir, file, allowedExtension, false);
+ }
+
+ /**
+ * 文件上传
+ *
+ * @param baseDir 相对应用的基目录
+ * @param file 上传的文件
+ * @param useCustomNaming 系统自定义文件名
+ * @param allowedExtension 上传文件类型
+ * @return 返回上传成功的文件名
+ * @throws FileSizeLimitExceededException 如果超出最大大小
+ * @throws FileNameLengthLimitExceededException 文件名太长
+ * @throws IOException 比如读写文件出错时
+ * @throws InvalidExtensionException 文件校验异常
+ */
+ public static final String upload(String baseDir, MultipartFile file, String[] allowedExtension, boolean useCustomNaming)
+ throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException,
+ InvalidExtensionException
+ {
+ int fileNameLength = Objects.requireNonNull(file.getOriginalFilename()).length();
+ if (fileNameLength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH)
{
throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH);
}
assertAllowed(file, allowedExtension);
- String fileName = extractFilename(file);
+ String fileName = useCustomNaming ? uuidFilename(file) : extractFilename(file);
String absPath = getAbsoluteFile(baseDir, fileName).getAbsolutePath();
file.transferTo(Paths.get(absPath));
@@ -118,12 +139,19 @@ public class FileUploadUtils
}
/**
- * 编码文件名
+ * 编码文件名(日期格式目录 + 原文件名 + 序列值 + 后缀)
*/
public static final String extractFilename(MultipartFile file)
{
- return StringUtils.format("{}/{}_{}.{}", DateUtils.datePath(),
- FilenameUtils.getBaseName(file.getOriginalFilename()), Seq.getId(Seq.uploadSeqType), getExtension(file));
+ return StringUtils.format("{}/{}_{}.{}", DateUtils.datePath(), FilenameUtils.getBaseName(file.getOriginalFilename()), Seq.getId(Seq.uploadSeqType), getExtension(file));
+ }
+
+ /**
+ * 编编码文件名(日期格式目录 + UUID + 后缀)
+ */
+ public static final String uuidFilename(MultipartFile file)
+ {
+ return StringUtils.format("{}/{}.{}", DateUtils.datePath(), IdUtils.fastSimpleUUID(), getExtension(file));
}
public static final File getAbsoluteFile(String uploadDir, String fileName) throws IOException
@@ -216,7 +244,7 @@ public class FileUploadUtils
/**
* 获取文件名的后缀
- *
+ *
* @param file 表单文件
* @return 后缀名
*/
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUtils.java
index 5c96f7cba232d47279fb1ce6d46a51355d7f3746..7cdd4980293ebd7d86ea2ac2d2ae2eaa85606e4b 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUtils.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUtils.java
@@ -9,15 +9,16 @@ import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.ArrayUtils;
import com.ruoyi.common.config.RuoYiConfig;
+import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.uuid.IdUtils;
-import org.apache.commons.io.FilenameUtils;
/**
* 文件处理工具类
@@ -103,6 +104,17 @@ public class FileUtils
return FileUploadUtils.getPathFileName(uploadDir, pathName);
}
+ /**
+ * 移除路径中的请求前缀片段
+ *
+ * @param filePath 文件路径
+ * @return 移除后的文件路径
+ */
+ public static String stripPrefix(String filePath)
+ {
+ return StringUtils.substringAfter(filePath, Constants.RESOURCE_PREFIX);
+ }
+
/**
* 删除文件
*
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/http/HttpHelper.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/http/HttpHelper.java
index 8baad384a44a0d4b8202da52a79502770fe9cee5..359ee07b40b443e4ad60586987e41ee3a82577b3 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/http/HttpHelper.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/http/HttpHelper.java
@@ -5,7 +5,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
-import javax.servlet.ServletRequest;
+import jakarta.servlet.ServletRequest;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/http/HttpUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/http/HttpUtils.java
index d3b61cad5fc32af71d9f069171e38bbb2ebc5f56..d50578915f58cf12a67bb0d9122d1cd06913994e 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/http/HttpUtils.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/http/HttpUtils.java
@@ -21,6 +21,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.utils.StringUtils;
+import org.springframework.http.MediaType;
/**
* 通用http发送方法
@@ -125,6 +126,19 @@ public class HttpUtils
* @return 所代表远程资源的响应结果
*/
public static String sendPost(String url, String param)
+ {
+ return sendPost(url, param, MediaType.APPLICATION_FORM_URLENCODED_VALUE);
+ }
+
+ /**
+ * 向指定 URL 发送POST方法的请求
+ *
+ * @param url 发送请求的 URL
+ * @param param 请求参数
+ * @param contentType 内容类型
+ * @return 所代表远程资源的响应结果
+ */
+ public static String sendPost(String url, String param, String contentType)
{
PrintWriter out = null;
BufferedReader in = null;
@@ -138,7 +152,7 @@ public class HttpUtils
conn.setRequestProperty("connection", "Keep-Alive");
conn.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)");
conn.setRequestProperty("Accept-Charset", "utf-8");
- conn.setRequestProperty("contentType", "utf-8");
+ conn.setRequestProperty("Content-Type", contentType);
conn.setDoOutput(true);
conn.setDoInput(true);
out = new PrintWriter(conn.getOutputStream());
@@ -190,6 +204,11 @@ public class HttpUtils
}
public static String sendSSLPost(String url, String param)
+ {
+ return sendSSLPost(url, param, MediaType.APPLICATION_FORM_URLENCODED_VALUE);
+ }
+
+ public static String sendSSLPost(String url, String param, String contentType)
{
StringBuilder result = new StringBuilder();
String urlNameString = url + "?" + param;
@@ -204,7 +223,7 @@ public class HttpUtils
conn.setRequestProperty("connection", "Keep-Alive");
conn.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)");
conn.setRequestProperty("Accept-Charset", "utf-8");
- conn.setRequestProperty("contentType", "utf-8");
+ conn.setRequestProperty("Content-Type", contentType);
conn.setDoOutput(true);
conn.setDoInput(true);
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/IpUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/IpUtils.java
index 33162402d0ea24add99e7979c6fb922b774a3155..24f89b6df44e7077dfe390416174d81695e3e2a3 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/IpUtils.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/IpUtils.java
@@ -2,7 +2,7 @@ package com.ruoyi.common.utils.ip;
import java.net.InetAddress;
import java.net.UnknownHostException;
-import javax.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletRequest;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.StringUtils;
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java
index 64f97baa36eb3d945ccfef43ee8095fab9a56d0b..a09386dd10429182bc9fbf80229c4ae6c45ad035 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java
@@ -23,7 +23,7 @@ import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.RegExUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
@@ -95,6 +95,8 @@ public class ExcelUtil
{
private static final Logger log = LoggerFactory.getLogger(ExcelUtil.class);
+ public static final String SEPARATOR = ",";
+
public static final String FORMULA_REGEX_STR = "=|-|\\+|@";
public static final String[] FORMULA_STR = { "=", "-", "+", "@" };
@@ -185,14 +187,14 @@ public class ExcelUtil
private Map statistics = new HashMap();
/**
- * 数字格式
+ * 实体对象
*/
- private static final DecimalFormat DOUBLE_FORMAT = new DecimalFormat("######0.00");
+ public Class clazz;
/**
- * 实体对象
+ * 需要显示列属性
*/
- public Class clazz;
+ public String[] includeFields;
/**
* 需要排除列属性
@@ -204,11 +206,20 @@ public class ExcelUtil
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)
{
@@ -238,8 +249,6 @@ public class ExcelUtil
{
if (StringUtils.isNotEmpty(title))
{
- subMergedFirstRowNum++;
- subMergedLastRowNum++;
int titleLastCol = this.fields.size() - 1;
if (isSubList())
{
@@ -250,7 +259,7 @@ public class ExcelUtil
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));
}
}
@@ -261,23 +270,31 @@ public class ExcelUtil
{
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++;
}
@@ -290,11 +307,23 @@ public class ExcelUtil
* @return 转换后集合
*/
public List importExcel(InputStream is)
+ {
+ return importExcel(is, 0);
+ }
+
+ /**
+ * 对excel表单默认第一个索引名转换成list
+ *
+ * @param is 输入流
+ * @param titleNum 标题占用行数
+ * @return 转换后集合
+ */
+ public List importExcel(InputStream is, int titleNum)
{
List list = null;
try
{
- list = importExcel(is, 0);
+ list = importExcel(StringUtils.EMPTY, is, titleNum);
}
catch (Exception e)
{
@@ -308,18 +337,6 @@ public class ExcelUtil
return list;
}
- /**
- * 对excel表单默认第一个索引名转换成list
- *
- * @param is 输入流
- * @param titleNum 标题占用行数
- * @return 转换后集合
- */
- public List importExcel(InputStream is, int titleNum) throws Exception
- {
- return importExcel(StringUtils.EMPTY, is, titleNum);
- }
-
/**
* 对excel表单指定表格索引名转换成list
*
@@ -340,7 +357,7 @@ public class ExcelUtil
throw new IOException("文件sheet不存在");
}
boolean isXSSFWorkbook = !(wb instanceof HSSFWorkbook);
- Map pictures;
+ Map> pictures = null;
if (isXSSFWorkbook)
{
pictures = getSheetPictures07((XSSFSheet) sheet, (XSSFWorkbook) wb);
@@ -397,7 +414,7 @@ public class ExcelUtil
Object val = this.getCellValue(row, entry.getKey());
// 如果不存在实例则新建.
- entity = (entity == null ? clazz.newInstance() : entity);
+ entity = (entity == null ? clazz.getDeclaredConstructor().newInstance() : entity);
// 从map中得到对应列的field.
Field field = (Field) entry.getValue()[0];
Excel attr = (Excel) entry.getValue()[1];
@@ -406,7 +423,7 @@ public class ExcelUtil
if (String.class == fieldType)
{
String s = Convert.toStr(val);
- if (StringUtils.endsWith(s, ".0"))
+ if (s.matches("^\\d+\\.0$"))
{
val = StringUtils.substringBefore(s, ".0");
}
@@ -484,16 +501,15 @@ public class ExcelUtil
}
else if (ColumnType.IMAGE == attr.cellType() && StringUtils.isNotEmpty(pictures))
{
- PictureData image = pictures.get(row.getRowNum() + "_" + entry.getKey());
- if (image == null)
- {
- val = "";
- }
- else
+ StringBuilder propertyString = new StringBuilder();
+ List images = pictures.get(row.getRowNum() + "_" + entry.getKey());
+ for (PictureData picture : images)
{
- byte[] data = image.getData();
- val = FileUtils.writeImportBytes(data);
+ byte[] data = picture.getData();
+ String fileName = FileUtils.writeImportBytes(data);
+ propertyString.append(fileName).append(SEPARATOR);
}
+ val = StringUtils.stripEnd(propertyString.toString(), SEPARATOR);
}
ReflectUtils.invokeSetter(entity, propertyName, val);
}
@@ -711,64 +727,91 @@ public class ExcelUtil
{
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 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;
}
/**
@@ -813,6 +856,7 @@ public class ExcelUtil
style = wb.createCellStyle();
style.setAlignment(HorizontalAlignment.CENTER);
style.setVerticalAlignment(VerticalAlignment.CENTER);
+ style.setDataFormat(dataFormat.getFormat("######0.00"));
Font totalFont = wb.createFont();
totalFont.setFontName("Arial");
totalFont.setFontHeightInPoints((short) 10);
@@ -903,7 +947,7 @@ public class ExcelUtil
*/
public void annotationDataStyles(Map 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();
@@ -919,6 +963,7 @@ public class ExcelUtil
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);
@@ -947,7 +992,7 @@ public class ExcelUtil
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));
@@ -989,12 +1034,15 @@ public class ExcelUtil
else if (ColumnType.IMAGE == attr.cellType())
{
ClientAnchor anchor = new XSSFClientAnchor(0, 0, 0, 0, (short) cell.getColumnIndex(), cell.getRow().getRowNum(), (short) (cell.getColumnIndex() + 1), cell.getRow().getRowNum() + 1);
- String imagePath = Convert.toStr(value);
- if (StringUtils.isNotEmpty(imagePath))
+ String propertyValue = Convert.toStr(value);
+ if (StringUtils.isNotEmpty(propertyValue))
{
- byte[] data = ImageUtils.getImage(imagePath);
- getDrawingPatriarch(cell.getSheet()).createPicture(anchor,
- cell.getSheet().getWorkbook().addPicture(data, getImageType(data)));
+ List imagePaths = StringUtils.str2List(propertyValue, SEPARATOR);
+ for (String imagePath : imagePaths)
+ {
+ byte[] data = ImageUtils.getImage(imagePath);
+ getDrawingPatriarch(cell.getSheet()).createPicture(anchor, cell.getSheet().getWorkbook().addPicture(data, getImageType(data)));
+ }
}
}
}
@@ -1071,6 +1119,7 @@ public class ExcelUtil
/**
* 添加单元格
*/
+ @SuppressWarnings("deprecation")
public Cell addCell(Excel attr, Row row, T vo, Field field, int column)
{
Cell cell = null;
@@ -1085,10 +1134,12 @@ public class ExcelUtil
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);
@@ -1098,6 +1149,7 @@ public class ExcelUtil
String dictType = attr.dictType();
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))
@@ -1236,7 +1288,7 @@ public class ExcelUtil
public static String convertByExp(String propertyValue, String converterExp, String separator)
{
StringBuilder propertyString = new StringBuilder();
- String[] convertSource = converterExp.split(",");
+ String[] convertSource = converterExp.split(SEPARATOR);
for (String item : convertSource)
{
String[] itemArray = item.split("=");
@@ -1273,7 +1325,7 @@ public class ExcelUtil
public static String reverseByExp(String propertyValue, String converterExp, String separator)
{
StringBuilder propertyString = new StringBuilder();
- String[] convertSource = converterExp.split(",");
+ String[] convertSource = converterExp.split(SEPARATOR);
for (String item : convertSource)
{
String[] itemArray = item.split("=");
@@ -1336,7 +1388,7 @@ public class ExcelUtil
{
try
{
- Object instance = excel.handler().newInstance();
+ Object instance = excel.handler().getDeclaredConstructor().newInstance();
Method formatMethod = excel.handler().getMethod("format", new Class[] { Object.class, String[].class, Cell.class, Workbook.class });
value = formatMethod.invoke(instance, value, excel.args(), cell, this.wb);
}
@@ -1387,7 +1439,7 @@ public class ExcelUtil
{
cell = row.createCell(key);
cell.setCellStyle(styles.get("total"));
- cell.setCellValue(DOUBLE_FORMAT.format(statistics.get(key)));
+ cell.setCellValue(statistics.get(key));
}
statistics.clear();
}
@@ -1398,8 +1450,7 @@ public class ExcelUtil
*/
public String encodingFilename(String filename)
{
- filename = UUID.randomUUID() + "_" + filename + ".xlsx";
- return filename;
+ return UUID.randomUUID() + "_" + filename + ".xlsx";
}
/**
@@ -1429,6 +1480,7 @@ public class ExcelUtil
*/
private Object getTargetValue(T vo, Field field, Excel excel) throws Exception
{
+ field.setAccessible(true);
Object o = field.get(vo);
if (StringUtils.isNotEmpty(excel.targetAttr()))
{
@@ -1488,46 +1540,83 @@ public class ExcelUtil
List tempFields = new ArrayList<>();
tempFields.addAll(Arrays.asList(clazz.getSuperclass().getDeclaredFields()));
tempFields.addAll(Arrays.asList(clazz.getDeclaredFields()));
- for (Field field : tempFields)
+ if (StringUtils.isNotEmpty(includeFields))
+ {
+ for (Field field : tempFields)
+ {
+ if (ArrayUtils.contains(this.includeFields, field.getName()) || field.isAnnotationPresent(Excels.class))
+ {
+ addField(fields, field);
+ }
+ }
+ }
+ else if (StringUtils.isNotEmpty(excludeFields))
{
- if (!ArrayUtils.contains(this.excludeFields, field.getName()))
+ for (Field field : tempFields)
{
- // 单注解
- if (field.isAnnotationPresent(Excel.class))
+ if (!ArrayUtils.contains(this.excludeFields, field.getName()))
{
- Excel attr = field.getAnnotation(Excel.class);
- if (attr != null && (attr.type() == Type.ALL || attr.type() == type))
+ addField(fields, field);
+ }
+ }
+ }
+ else
+ {
+ for (Field field : tempFields)
+ {
+ addField(fields, field);
+ }
+ }
+ return fields;
+ }
+
+ /**
+ * 添加字段信息
+ */
+ public void addField(List