mirror of
https://github.com/dromara/RuoYi-Vue-Plus.git
synced 2025-09-24 07:19:46 +08:00
Compare commits
185 Commits
Author | SHA1 | Date | |
---|---|---|---|
c631a084f3 | |||
022d33bc41 | |||
47379dd702 | |||
8c8b53e266 | |||
9e8a58af43 | |||
4409b96c74 | |||
9253186c27 | |||
b9eee8f399 | |||
230d5e7d56 | |||
68404f3f6e | |||
a312794423 | |||
98e9c11378 | |||
d9b8dd9cc2 | |||
f366457aa1 | |||
311df68fa6 | |||
c5d071dbaf | |||
b55409bf37 | |||
11f548b53b | |||
d8b264a13d | |||
77407899e8 | |||
6296a50259 | |||
754b138abe | |||
811177f530 | |||
54a45d0900 | |||
b55d8faa67 | |||
1ad5a526e7 | |||
ecdb629438 | |||
df5eb800cd | |||
f3fa1386e7 | |||
a31d4a92d6 | |||
0804854175 | |||
bb265274c5 | |||
9895517097 | |||
93dda236dc | |||
8619d97749 | |||
6762ab739a | |||
5b43d0e29f | |||
0662756e8c | |||
5cd6dcff4f | |||
0b0439b72e | |||
0abc2e9624 | |||
7343cdbe70 | |||
ded82af207 | |||
16d58bacbc | |||
a5abbbf988 | |||
5c040dc5cb | |||
6c41fd1cf3 | |||
b4cf1dbb6d | |||
eba4c88833 | |||
773b497761 | |||
3832dc933d | |||
7fd262fd8b | |||
5e44843854 | |||
09d72dceef | |||
684b84a16d | |||
35c289ab92 | |||
b91f02f5b7 | |||
ab7f6957b9 | |||
b13c10ccd6 | |||
eb23098a4d | |||
b02ad6a0cc | |||
2fd44be9db | |||
fda19131ae | |||
24d942f036 | |||
7484b9b6a9 | |||
9ae3e9902a | |||
cbd50e6b3d | |||
9a9507ce5a | |||
46e126dff7 | |||
56d7023e41 | |||
0e0370f532 | |||
025f3a9bcf | |||
4dc8af274b | |||
7f0661c007 | |||
a847a1dbcc | |||
a5ee33b559 | |||
8f87da19ea | |||
a2d8ff9286 | |||
8387ec6134 | |||
9e0fca3b54 | |||
f8c079c651 | |||
9b7adfdaf4 | |||
91a61b6927 | |||
d38fa0ec05 | |||
d7d398763f | |||
f6d4e23bf6 | |||
4b07faf89a | |||
31194414eb | |||
d6c49b915f | |||
b465972e7c | |||
7a5abcd0f8 | |||
5464dfb830 | |||
7a97377c28 | |||
453b8fcbb2 | |||
2a46cdbd88 | |||
995578c561 | |||
f88c93b335 | |||
edcd7c99ba | |||
dadf05c25c | |||
a2c43aceb1 | |||
4bd2691422 | |||
088002bd62 | |||
5722ba3f6c | |||
40aeeeced1 | |||
6f209cad99 | |||
db6796e740 | |||
4382cf2217 | |||
d93307151a | |||
f3d800d598 | |||
d2baaaaf7b | |||
c203d0af46 | |||
1f507f2d22 | |||
5cf3287064 | |||
57bcb5dbf6 | |||
e803388cad | |||
b9b76539ac | |||
bc06550918 | |||
61db843576 | |||
6c6d92a776 | |||
f8ac8c085e | |||
7c05920e73 | |||
2d7535012e | |||
654944b5c7 | |||
d2675744f4 | |||
e20dacbfd9 | |||
540afd839d | |||
b34c2fd6ee | |||
1ae44c8124 | |||
6fb34a7717 | |||
43982c5a36 | |||
d8062087c7 | |||
10f88f5458 | |||
c79c3ee5b6 | |||
cedb174ffd | |||
f9d9eb2d70 | |||
1a70cf658c | |||
242e26abee | |||
52d33195ec | |||
d8b486c793 | |||
00b6ca5dfe | |||
9ebaf936c1 | |||
dac463efa6 | |||
da1b18792b | |||
85b4adf400 | |||
70f39cb1ee | |||
d87dfd6397 | |||
1496220a74 | |||
996835d124 | |||
f6f3db1701 | |||
6ee3085260 | |||
a6f6562693 | |||
d8585d0ee7 | |||
4761237849 | |||
f1a09711c1 | |||
346a3cf7f1 | |||
c42229bc54 | |||
317ab351ce | |||
e1579622de | |||
f2ff839106 | |||
c494ab9963 | |||
15f7d4dc47 | |||
dd9ef8431b | |||
e729a924d4 | |||
aeacfea536 | |||
43b31edb75 | |||
8448bcdc6d | |||
e1efdb8602 | |||
409849db1a | |||
9d6b0b35a3 | |||
7f00861e68 | |||
d2b1177580 | |||
ef91551449 | |||
4063b0c2dd | |||
5117fe2958 | |||
9ed60f22de | |||
8498ead56c | |||
53ae4ea6bc | |||
4c8137daf2 | |||
6e017c35f7 | |||
06cda3fb18 | |||
b38151a0bb | |||
2790b5ddc4 | |||
f70951d374 | |||
9ebe1ae918 | |||
06e6f7af0c |
@ -1,13 +1,49 @@
|
|||||||
### 使用版本(未按照模板填写直接删除)
|
### 使用版本(未按照模板填写直接删除)
|
||||||
|
|
||||||
|
- jdk版本(带上尾号): 例如 1.8.0_202
|
||||||
|
- 框架版本(项目启动时输出的版本号): 例如 4.4.0
|
||||||
|
- 其他依赖版本(你觉得有必要的):
|
||||||
|
|
||||||
|
### 问题前提
|
||||||
|
|
||||||
|
> 功能不好用 不会用 是否已经看过项目文档
|
||||||
|
> 项目运行报错 是否已经拿着报错信息去百度 常见报错百度百度足以
|
||||||
|
> 是否搜索过其他issue 一些已经解决的问题 会在issue内留下解决方法
|
||||||
|
> 无法线上解决或者与框架无关的问题的欢迎加VIP群跟作者一对一谈
|
||||||
|
|
||||||
|
### 异常模块
|
||||||
|
|
||||||
|
> 此报错都涉及到那些系统模块
|
||||||
|
|
||||||
|
例如 ruoyi-system ruoyi-auth 等等
|
||||||
|
|
||||||
### 问题描述
|
### 问题描述
|
||||||
|
|
||||||
|
> 越详细越容易直击问题所在
|
||||||
|
|
||||||
|
已知: XXX功能不好用 或 XXX数据不正常 等等
|
||||||
|
|
||||||
### 希望结果
|
### 希望结果
|
||||||
|
|
||||||
|
> 想知道你觉得怎么样是正常或者合理的
|
||||||
|
|
||||||
|
希望功能可以有XXX结果 或者 XXX现象
|
||||||
|
|
||||||
### 重现步骤
|
### 重现步骤
|
||||||
|
|
||||||
|
> 作者并不知道这个问题是如何出现的
|
||||||
|
|
||||||
### 报错信息(截图为主 请勿发混乱格式)
|
- 1
|
||||||
|
- 2
|
||||||
|
- 3
|
||||||
|
|
||||||
|
### 相关代码与报错信息(请勿发混乱格式)
|
||||||
|
|
||||||
|
> 代码可按照如下形式提供或者截图均可 越详细越好
|
||||||
|
> 大多数问题都是 代码编写错误问题 逻辑问题 或者用法错误等问题
|
||||||
|
|
||||||
|
```java
|
||||||
|
public class XXX {
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
@ -1,7 +1,7 @@
|
|||||||
### 更改目的 解决了什么问题(请提交到dev分支)
|
### 更改目的 解决了什么问题(请提交到dev分支)
|
||||||
|
|
||||||
|
|
||||||
### 描述 做了哪些改动
|
### 改动逻辑 这么写的思路(让作者更好的理解你的意图)
|
||||||
|
|
||||||
|
|
||||||
### 测试 都做了哪些测试(未经过测试不采纳)
|
### 测试 都做了哪些测试(未经过测试不采纳)
|
@ -2,7 +2,7 @@
|
|||||||
<configuration default="false" name="ruoyi-monitor-admin" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
|
<configuration default="false" name="ruoyi-monitor-admin" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
|
||||||
<deployment type="dockerfile">
|
<deployment type="dockerfile">
|
||||||
<settings>
|
<settings>
|
||||||
<option name="imageTag" value="ruoyi/ruoyi-monitor-admin:4.4.0" />
|
<option name="imageTag" value="ruoyi/ruoyi-monitor-admin:4.7.0" />
|
||||||
<option name="buildOnly" value="true" />
|
<option name="buildOnly" value="true" />
|
||||||
<option name="sourceFilePath" value="ruoyi-extend/ruoyi-monitor-admin/Dockerfile" />
|
<option name="sourceFilePath" value="ruoyi-extend/ruoyi-monitor-admin/Dockerfile" />
|
||||||
</settings>
|
</settings>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<configuration default="false" name="ruoyi-server" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
|
<configuration default="false" name="ruoyi-server" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
|
||||||
<deployment type="dockerfile">
|
<deployment type="dockerfile">
|
||||||
<settings>
|
<settings>
|
||||||
<option name="imageTag" value="ruoyi/ruoyi-server:4.4.0" />
|
<option name="imageTag" value="ruoyi/ruoyi-server:4.7.0" />
|
||||||
<option name="buildOnly" value="true" />
|
<option name="buildOnly" value="true" />
|
||||||
<option name="sourceFilePath" value="ruoyi-admin/Dockerfile" />
|
<option name="sourceFilePath" value="ruoyi-admin/Dockerfile" />
|
||||||
</settings>
|
</settings>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<configuration default="false" name="ruoyi-xxl-job-admin" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
|
<configuration default="false" name="ruoyi-xxl-job-admin" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
|
||||||
<deployment type="dockerfile">
|
<deployment type="dockerfile">
|
||||||
<settings>
|
<settings>
|
||||||
<option name="imageTag" value="ruoyi/ruoyi-xxl-job-admin:4.4.0" />
|
<option name="imageTag" value="ruoyi/ruoyi-xxl-job-admin:4.7.0" />
|
||||||
<option name="buildOnly" value="true" />
|
<option name="buildOnly" value="true" />
|
||||||
<option name="sourceFilePath" value="ruoyi-extend/ruoyi-xxl-job-admin/Dockerfile" />
|
<option name="sourceFilePath" value="ruoyi-extend/ruoyi-xxl-job-admin/Dockerfile" />
|
||||||
</settings>
|
</settings>
|
||||||
|
237
README.md
237
README.md
@ -1,10 +1,16 @@
|
|||||||
|
<img src="https://foruda.gitee.com/images/1679673773341074847/178e8451_1766278.png" width="50%" height="50%">
|
||||||
|
<div style="height: 10px; clear: both;"></div>
|
||||||
|
|
||||||
|
- - -
|
||||||
|
|
||||||
## 平台简介
|
## 平台简介
|
||||||
[](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus)
|
|
||||||
[](https://github.com/JavaLionLi/RuoYi-Vue-Plus)
|
[](https://gitee.com/dromara/RuoYi-Vue-Plus)
|
||||||
[](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/blob/master/LICENSE)
|
[](https://github.com/dromara/RuoYi-Vue-Plus)
|
||||||
|
[](https://gitee.com/dromara/RuoYi-Vue-Plus/blob/master/LICENSE)
|
||||||
[](https://www.jetbrains.com/?from=RuoYi-Vue-Plus)
|
[](https://www.jetbrains.com/?from=RuoYi-Vue-Plus)
|
||||||
<br>
|
<br>
|
||||||
[](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus)
|
[](https://gitee.com/dromara/RuoYi-Vue-Plus)
|
||||||
[]()
|
[]()
|
||||||
[]()
|
[]()
|
||||||
[]()
|
[]()
|
||||||
@ -14,62 +20,95 @@
|
|||||||
> 项目代码、文档 均开源免费可商用 遵循开源协议在项目中保留开源协议文件即可<br>
|
> 项目代码、文档 均开源免费可商用 遵循开源协议在项目中保留开源协议文件即可<br>
|
||||||
活到老写到老 为兴趣而开源 为学习而开源 为让大家真正可以学到技术而开源
|
活到老写到老 为兴趣而开源 为学习而开源 为让大家真正可以学到技术而开源
|
||||||
|
|
||||||
> 系统演示: [传送门](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/pages?sort_id=4836388&doc_id=1469725)
|
> 系统演示: [传送门](https://gitee.com/dromara/RuoYi-Vue-Plus/wikis/pages?sort_id=4836388&doc_id=1469725)
|
||||||
|
|
||||||
| 功能介绍 | 使用技术 | 文档地址 | 特性注意事项 |
|
# 本框架与RuoYi的功能差异
|
||||||
|----------|---------------------|---------------------------------------------------------------------------------------------------|----------------------------|
|
|
||||||
| 当前框架 | RuoYi-Vue-Plus | [RuoYi-Vue-Plus文档](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/pages) | 重写RuoYi-Vue全方位升级(不兼容原框架) |
|
| 功能 | 本框架 | RuoYi |
|
||||||
| 微服务分支 | RuoYi-Cloud-Plus | [微服务分支地址](https://gitee.com/JavaLionLi/RuoYi-Cloud-Plus) | 重写RuoYi-Cloud全方位升级(不兼容原框架) |
|
|-------------|-------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------|
|
||||||
| 单体分支 | RuoYi-Vue-Plus-fast | [fast分支地址](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/tree/fast/) | 单体应用结构 |
|
| 前端项目 | 基于vue3-element-admin开源项目重写<br/>Vue3 + TS + ElementPlus | 基于Vue2/Vue3 + JS |
|
||||||
| Vue3分支 | RuoYi-Vue-Plus-UI | [UI地址](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus-UI) | 由于组件还未完善 仅供学习 |
|
| 后端项目结构 | 采用插件化 + 扩展包形式 结构解耦 易于扩展 | 模块相互注入耦合严重难以扩展 |
|
||||||
| 原框架 | RuoYi-Vue | [RuoYi-Vue官网](http://ruoyi.vip/) | 定期同步需要的功能 |
|
| 后端代码风格 | 严格遵守Alibaba规范与项目统一配置的代码格式化 | 代码书写与常规结构不同阅读障碍大 |
|
||||||
| 前端开发框架 | Vue、Element UI | [Element UI官网](https://element.eleme.cn/#/zh-CN) | |
|
| Web容器 | 采用 Undertow 基于 XNIO 的高性能容器 | 采用 Tomcat |
|
||||||
| 后端开发框架 | SpringBoot | [SpringBoot官网](https://spring.io/projects/spring-boot/#learn) | |
|
| 权限认证 | 采用 Sa-Token、Jwt 静态使用功能齐全 低耦合 高扩展 | Spring Security 配置繁琐扩展性极差 |
|
||||||
| 容器框架 | Undertow | [Undertow官网](https://undertow.io/) | 基于 XNIO 的高性能容器 |
|
| 权限注解 | 采用 Sa-Token 支持注解 登录校验、角色校验、权限校验、二级认证校验、HttpBasic校验、忽略校验<br/>角色与权限校验支持多种条件 如 `AND` `OR` 或 `权限 OR 角色` 等复杂表达式 | 只支持是否存在匹配 |
|
||||||
| 权限认证框架 | Sa-Token、Jwt | [Sa-Token官网](https://sa-token.dev33.cn/) | 强解耦、强扩展 |
|
| 关系数据库支持 | 原生支持 MySQL、Oracle、PostgreSQL、SQLServer<br/>可同时使用异构切换 | 支持 Mysql、Oracle 不支持同时使用、不支持异构切换 |
|
||||||
| 关系数据库 | MySQL | [MySQL官网](https://dev.mysql.com/) | 适配 8.X 最低 5.7 |
|
| 缓存数据库 | 支持 Redis 5-7 支持大部分新功能特性 如 分布式限流、分布式队列 | Redis 简单 get set 支持 |
|
||||||
| 关系数据库 | Oracle | [Oracle官网](https://www.oracle.com/cn/database/) | 适配 11g 12c |
|
| Redis客户端 | 采用 Redisson Redis官方推荐 基于Netty的客户端工具<br/>支持Redis 90%以上的命令 底层优化规避很多不正确的用法 例如: keys被转换为scan<br/>支持单机、哨兵、单主集群、多主集群等模式 | Lettuce + RedisTemplate 支持模式少 工具使用繁琐<br/>连接池采用 common-pool Bug多经常性出问题 |
|
||||||
| 关系数据库 | PostgreSQL | [PostgreSQL官网](https://www.postgresql.org/) | 适配 13 14 |
|
| 缓存注解 | 采用 Spring-Cache 注解 对其扩展了实现支持了更多功能<br/>例如 过期时间 最大空闲时间 组最大长度等 只需一个注解即可完成数据自动缓存 | 需手动编写Redis代码逻辑 |
|
||||||
| 关系数据库 | SQLServer | [SQLServer官网](https://docs.microsoft.com/zh-cn/sql/sql-server) | 适配 2017 2019 |
|
| ORM框架 | 采用 Mybatis-Plus 基于对象几乎不用写SQL全java操作 功能强大插件众多<br/>例如多租户插件 分页插件 乐观锁插件等等 | 采用 Mybatis 基于XML需要手写SQL |
|
||||||
| 缓存数据库 | Redis | [Redis官网](https://redis.io/) | 适配 6.X 最低 4.X |
|
| SQL监控 | 采用 p6spy 可输出完整SQL与执行时间监控 | log输出 需手动拼接sql与参数无法快速查看调试问题 |
|
||||||
| 数据库框架 | Mybatis-Plus | [Mybatis-Plus文档](https://baomidou.com/guide/) | 快速 CRUD 增加开发效率 |
|
| 数据分页 | 采用 Mybatis-Plus 分页插件<br/>框架对其进行了扩展 对象化分页对象 支持多种方式传参 支持前端多排序 复杂排序 | 采用 PageHelper 仅支持单查询分页 参数只能从param传 只能单排序 功能扩展性差 体验不好 |
|
||||||
| 数据库框架 | p6spy | [p6spy官网](https://p6spy.readthedocs.io/) | 更强劲的 SQL 分析 |
|
| 数据权限 | 采用 Mybatis-Plus 插件 自行分析拼接SQL 无感式过滤<br/>只需为Mapper设置好注解条件 支持多种自定义 不限于部门角色 | 采用 注解+aop 实现 基于部门角色 生成的sql兼容性差 不支持其他业务扩展<br/>生成sql后需手动拼接到具体业务sql上 对于多个Mapper查询不起作用 |
|
||||||
| 多数据源框架 | dynamic-datasource | [dynamic-ds文档](https://www.kancloud.cn/tracy5546/dynamic-datasource/content) | 支持主从与多种类数据库异构 |
|
| 数据脱敏 | 采用 注解 + jackson 序列化期间脱敏 支持不同模块不同的脱敏条件<br/>支持多种策略 如身份证、手机号、地址、邮箱、银行卡等 可自行扩展 | 无 |
|
||||||
| 序列化框架 | Jackson | [Jackson官网](https://github.com/FasterXML/jackson) | 统一使用 jackson 高效可靠 |
|
| 数据加解密 | 采用 注解 + mybatis 拦截器 对存取数据期间自动加解密<br/>支持多种策略 如BASE64、AES、RSA、SM2、SM4等 | 无 |
|
||||||
| Redis客户端 | Redisson | [Redisson文档](https://github.com/redisson/redisson/wiki/%E7%9B%AE%E5%BD%95) | 支持单机、集群配置 |
|
| 数据翻译 | 采用 注解 + jackson 序列化期间动态修改数据 数据进行翻译<br/>支持多种模式: `映射翻译` `直接翻译` `其他扩展条件翻译` 接口化两步即可完成自定义扩展 内置多种翻译实现 | 无 |
|
||||||
| 分布式限流 | Redisson | [Redisson文档](https://github.com/redisson/redisson/wiki/%E7%9B%AE%E5%BD%95) | 全局、请求IP、集群ID 多种限流 |
|
| 多数据源框架 | 采用 dynamic-datasource 支持世面大部分数据库<br/>通过yml配置即可动态管理异构不同种类的数据库 也可通过前端页面添加数据源<br/>支持spel表达式从请求头参数等条件切换数据源 | 基于 druid 手动编写代码配置数据源 配置繁琐 支持性差 |
|
||||||
| 分布式队列 | Redisson | [Redisson文档](https://github.com/redisson/redisson/wiki/%E7%9B%AE%E5%BD%95) | 普通队列、延迟队列、优先队列 等 |
|
| 多数据源事务 | 采用 dynamic-datasource 支持多数据源不同种类的数据库事务回滚 | 不支持 |
|
||||||
| 分布式锁 | Lock4j | [Lock4j官网](https://gitee.com/baomidou/lock4j) | 注解锁、工具锁 多种多样 |
|
| 数据库连接池 | 采用 HikariCP Spring官方内置连接池 配置简单 以性能与稳定性闻名天下 | 采用 druid bug众多 社区维护差 活跃度低 配置众多繁琐性能一般 |
|
||||||
| 分布式幂等 | Redisson | [Lock4j文档](https://gitee.com/baomidou/lock4j) | 拦截重复提交 |
|
| 数据库主键 | 采用 雪花ID 基于时间戳的 有序增长 唯一ID 再也不用为分库分表 数据合并主键冲突重复而发愁 | 采用 数据库自增ID 支持数据量有限 不支持多数据源主键唯一 |
|
||||||
| 分布式链路追踪 | Apache SkyWalking | [Apache SkyWalking文档](https://skywalking.apache.org/docs/) | 链路追踪、网格分析、度量聚合、可视化 |
|
| WebSocket协议 | 基于 Spring 封装的 WebSocket 协议 扩展了Token鉴权与分布式会话同步 不再只是基于单机的废物 | 无 |
|
||||||
| 分布式任务调度 | Xxl-Job | [Xxl-Job官网](https://www.xuxueli.com/xxl-job/) | 高性能 高可靠 易扩展 |
|
| 序列化 | 采用 Jackson Spring官方内置序列化 靠谱!!! | 采用 fastjson bugjson 远近闻名 |
|
||||||
| 文件存储 | Minio | [Minio文档](https://docs.min.io/) | 本地存储 |
|
| 分布式幂等 | 参考美团GTIS防重系统简化实现(细节可看文档) | 手动编写注解基于aop实现 |
|
||||||
| 文件存储 | 七牛、阿里、腾讯 | [OSS使用文档](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/pages?sort_id=4359146&doc_id=1469725) | 云存储 |
|
| 分布式任务调度 | 采用 Xxl-Job 天生支持分布式 统一的管理中心 | 采用 Quartz 基于数据库锁性能差 集群需要做很多配置与改造 |
|
||||||
| 短信模块 | 阿里、腾讯 | [短信使用文档](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/pages?sort_id=5578491&doc_id=1469725) | 短信发送 |
|
| 文件存储 | 采用 Minio 分布式文件存储 天生支持多机、多硬盘、多分片、多副本存储<br/>支持权限管理 安全可靠 文件可加密存储 | 采用 本机文件存储 文件裸漏 易丢失泄漏 不支持集群有单点效应 |
|
||||||
| 监控框架 | SpringBoot-Admin | [SpringBoot-Admin文档](https://codecentric.github.io/spring-boot-admin/current/) | 全方位服务监控 |
|
| 云存储 | 采用 AWS S3 协议客户端 支持 七牛、阿里、腾讯 等一切支持S3协议的厂家 | 不支持 |
|
||||||
| 校验框架 | Validation | [Validation文档](https://docs.jboss.org/hibernate/stable/validator/reference/en-US/html_single/) | 增强接口安全性、严谨性 支持国际化 |
|
| 短信 | 支持 阿里、腾讯 只需在yml配置好厂家密钥即可使用 接口化支持扩展其他厂家 | 不支持 |
|
||||||
| Excel框架 | Alibaba EasyExcel | [EasyExcel文档](https://www.yuque.com/easyexcel/doc/easyexcel) | 性能优异 扩展性强 |
|
| 邮件 | 采用 mail-api 通用协议支持大部分邮件厂商 | 不支持 |
|
||||||
| 文档框架 | SpringDoc、javadoc | [接口文档](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/pages?sort_id=5805266&doc_id=1469725) | 无注解零入侵基于java注释 |
|
| 接口文档 | 采用 SpringDoc、javadoc 无注解零入侵基于java注释<br/>只需把注释写好 无需再写一大堆的文档注解了 | 采用 Springfox 已停止维护 需要编写大量的注解来支持文档生成 |
|
||||||
| 工具类框架 | Hutool、Lombok | [Hutool文档](https://www.hutool.cn/docs/) | 减少代码冗余 增加安全性 |
|
| 校验框架 | 采用 Validation 支持注解与工具类校验 注解支持国际化 | 仅支持注解 且注解不支持国际化 |
|
||||||
| 代码生成器 | 适配MP、SpringDoc规范化代码 | [代码生成文档](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/pages?sort_id=5522329&doc_id=1469725) | 一键生成前后端代码 |
|
| Excel框架 | 采用 Alibaba EasyExcel 基于插件化<br/>框架对其增加了很多功能 例如 自动合并相同内容 自动排列布局 字典翻译等 | 基于 POI 手写实现 功能有限 复杂 扩展性差 |
|
||||||
| 部署方式 | Docker | [Docker文档](https://docs.docker.com/) | 容器编排 一键部署业务集群 |
|
| 工具类框架 | 采用 Hutool、Lombok 上百种工具覆盖90%的使用需求 基于注解自动生成 get set 等简化框架大量代码 | 手写工具稳定性差易出问题 工具数量有限 代码臃肿需自己手写 get set 等 |
|
||||||
| 国际化 | SpringMessage | [SpringMVC文档](https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc) | Spring标准国际化方案 |
|
| 监控框架 | 采用 SpringBoot-Admin 基于SpringBoot官方 actuator 探针机制<br/>实时监控服务状态 框架还为其扩展了在线日志查看监控 | 无 |
|
||||||
|
| 链路追踪 | 采用 Apache SkyWalking 还在为请求不知道去哪了 到哪出了问题而烦恼吗<br/>用了它即可实时查看请求经过的每一处每一个节点 | 无 |
|
||||||
|
| 代码生成器 | 只需设计好表结构 一键生成所有crud代码与页面<br/>降低80%的开发量 把精力都投入到业务设计上<br/>框架为其适配MP、SpringDoc规范化代码 同时支持动态多数据源代码生成 | 代码生成原生结构 只支持单数据源生成 |
|
||||||
|
| 部署方式 | 支持 Docker 编排 一键搭建所有环境 让开发人员从此不再为搭建环境而烦恼 | 原生jar部署 其他环境需手动下载安装 自行搭建 |
|
||||||
|
| 项目路径修改 | 提供详细的修改方案文档 并为其做了一些改动 非常简单即可修改成自己想要的 | 需要做很多改造 文档说明有限 |
|
||||||
|
| 国际化 | 基于请求头动态返回不同语种的文本内容 开发难度低 有对应的工具类 支持大部分注解内容国际化 | 只提供基础功能 其他需自行编写扩展 |
|
||||||
|
| 代码单例测试 | 提供单例测试 使用方式编写方法与maven多环境单测插件 | 只提供基础功能 其他需自行编写扩展 |
|
||||||
|
| Demo案例 | 提供框架功能的实际使用案例 单独一个模块提供了很多很全 | 无 |
|
||||||
|
|
||||||
|
|
||||||
|
## 本框架与RuoYi的业务差异
|
||||||
|
|
||||||
|
| 业务 | 功能说明 | 本框架 | RuoYi |
|
||||||
|
|--------|-----------------------------------------|-----|------------------|
|
||||||
|
| 用户管理 | 用户的管理配置 如:新增用户、分配用户所属部门、角色、岗位等 | 支持 | 支持 |
|
||||||
|
| 部门管理 | 配置系统组织机构(公司、部门、小组) 树结构展现支持数据权限 | 支持 | 支持 |
|
||||||
|
| 岗位管理 | 配置系统用户所属担任职务 | 支持 | 支持 |
|
||||||
|
| 菜单管理 | 配置系统菜单、操作权限、按钮权限标识等 | 支持 | 支持 |
|
||||||
|
| 角色管理 | 角色菜单权限分配、设置角色按机构进行数据范围权限划分 | 支持 | 支持 |
|
||||||
|
| 字典管理 | 对系统中经常使用的一些较为固定的数据进行维护 | 支持 | 支持 |
|
||||||
|
| 参数管理 | 对系统动态配置常用参数 | 支持 | 支持 |
|
||||||
|
| 通知公告 | 系统通知公告信息发布维护 | 支持 | 支持 |
|
||||||
|
| 操作日志 | 系统正常操作日志记录和查询 系统异常信息日志记录和查询 | 支持 | 支持 |
|
||||||
|
| 登录日志 | 系统登录日志记录查询包含登录异常 | 支持 | 支持 |
|
||||||
|
| 文件管理 | 系统文件展示、上传、下载、删除等管理 | 支持 | 无 |
|
||||||
|
| 文件配置管理 | 系统文件上传、下载所需要的配置信息动态添加、修改、删除等管理 | 支持 | 无 |
|
||||||
|
| 在线用户管理 | 已登录系统的在线用户信息监控与强制踢出操作 | 支持 | 支持 |
|
||||||
|
| 定时任务 | 运行报表、任务管理(添加、修改、删除)、日志管理、执行器管理等 | 支持 | 仅支持任务与日志管理 |
|
||||||
|
| 代码生成 | 多数据源前后端代码的生成(java、html、xml、sql)支持CRUD下载 | 支持 | 仅支持单数据源 |
|
||||||
|
| 系统接口 | 根据业务代码自动生成相关的api接口文档 | 支持 | 支持 |
|
||||||
|
| 服务监控 | 监视集群系统CPU、内存、磁盘、堆栈、在线日志、Spring相关配置等 | 支持 | 仅支持单机CPU、内存、磁盘监控 |
|
||||||
|
| 缓存监控 | 对系统的缓存信息查询,命令统计等。 | 支持 | 支持 |
|
||||||
|
| 在线构建器 | 拖动表单元素生成相应的HTML代码。 | 支持 | 支持 |
|
||||||
|
| 使用案例 | 系统的一些功能案例 | 支持 | 不支持 |
|
||||||
|
|
||||||
## 参考文档
|
## 参考文档
|
||||||
|
|
||||||
使用框架前请仔细阅读文档重点注意事项
|
使用框架前请仔细阅读文档重点注意事项
|
||||||
<br>
|
<br>
|
||||||
>[初始化项目 必看](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/pages?sort_id=4164117&doc_id=1469725)
|
>[初始化项目 必看](https://gitee.com/dromara/RuoYi-Vue-Plus/wikis/pages?sort_id=4164117&doc_id=1469725)
|
||||||
>>[https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/pages?sort_id=4164117&doc_id=1469725](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/pages?sort_id=4164117&doc_id=1469725)
|
>>[https://gitee.com/dromara/RuoYi-Vue-Plus/wikis/pages?sort_id=4164117&doc_id=1469725](https://gitee.com/dromara/RuoYi-Vue-Plus/wikis/pages?sort_id=4164117&doc_id=1469725)
|
||||||
>
|
>
|
||||||
>[专栏与视频 入门必看](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/pages?sort_id=5473272&doc_id=1469725)
|
>[专栏与视频 入门必看](https://gitee.com/dromara/RuoYi-Vue-Plus/wikis/pages?sort_id=5473272&doc_id=1469725)
|
||||||
>>[https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/pages?sort_id=5473272&doc_id=1469725](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/pages?sort_id=5473272&doc_id=1469725)
|
>>[https://gitee.com/dromara/RuoYi-Vue-Plus/wikis/pages?sort_id=5473272&doc_id=1469725](https://gitee.com/dromara/RuoYi-Vue-Plus/wikis/pages?sort_id=5473272&doc_id=1469725)
|
||||||
>
|
>
|
||||||
>[部署项目 必看](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/pages?sort_id=4219382&doc_id=1469725)
|
>[部署项目 必看](https://gitee.com/dromara/RuoYi-Vue-Plus/wikis/pages?sort_id=4219382&doc_id=1469725)
|
||||||
>>[https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/pages?sort_id=4219382&doc_id=1469725](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/pages?sort_id=4219382&doc_id=1469725)
|
>>[https://gitee.com/dromara/RuoYi-Vue-Plus/wikis/pages?sort_id=4219382&doc_id=1469725](https://gitee.com/dromara/RuoYi-Vue-Plus/wikis/pages?sort_id=4219382&doc_id=1469725)
|
||||||
>
|
>
|
||||||
>[参考文档 Wiki](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/pages)
|
>[参考文档 Wiki](https://gitee.com/dromara/RuoYi-Vue-Plus/wikis/pages)
|
||||||
>>[https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/pages](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/pages)
|
>>[https://gitee.com/dromara/RuoYi-Vue-Plus/wikis/pages](https://gitee.com/dromara/RuoYi-Vue-Plus/wikis/pages)
|
||||||
|
|
||||||
## 软件架构图
|
## 软件架构图
|
||||||
|
|
||||||
@ -83,81 +122,43 @@
|
|||||||
### 其他
|
### 其他
|
||||||
|
|
||||||
* 同步升级 RuoYi-Vue
|
* 同步升级 RuoYi-Vue
|
||||||
* GitHub 地址 [RuoYi-Vue-Plus-github](https://github.com/JavaLionLi/RuoYi-Vue-Plus)
|
* GitHub 地址 [RuoYi-Vue-Plus-github](https://github.com/dromara/RuoYi-Vue-Plus)
|
||||||
* 单模块 分支 [RuoYi-Vue-Plus-fast](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/tree/fast/)
|
* 单模块 分支 [RuoYi-Vue-Plus-fast](https://gitee.com/dromara/RuoYi-Vue-Plus/tree/fast/)
|
||||||
* 微服务 分支 [RuoYi-Cloud-Plus](https://gitee.com/JavaLionLi/RuoYi-Cloud-Plus)
|
* 微服务 分支 [RuoYi-Cloud-Plus](https://gitee.com/JavaLionLi/RuoYi-Cloud-Plus)
|
||||||
* 用户扩展项目 [扩展项目列表](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/pages?sort_id=4478302&doc_id=1469725)
|
* Vue3 分支 [RuoYi-Vue-Plus-UI](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus-UI)
|
||||||
|
* 用户扩展项目 [扩展项目列表](https://gitee.com/dromara/RuoYi-Vue-Plus/wikis/pages?sort_id=4478302&doc_id=1469725)
|
||||||
|
|
||||||
## 加群与捐献
|
## 加群与捐献
|
||||||
>[加群与捐献](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/加群与捐献?sort_id=4104598)
|
>[加群与捐献](https://gitee.com/dromara/RuoYi-Vue-Plus/wikis/加群与捐献?sort_id=4104598)
|
||||||
>>[https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/加群与捐献?sort_id=4104598](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/加群与捐献?sort_id=4104598)
|
>>[https://gitee.com/dromara/RuoYi-Vue-Plus/wikis/加群与捐献?sort_id=4104598](https://gitee.com/dromara/RuoYi-Vue-Plus/wikis/加群与捐献?sort_id=4104598)
|
||||||
|
|
||||||
## 捐献作者
|
## 捐献作者
|
||||||
作者为兼职做开源,平时还需要工作,如果帮到了您可以请作者吃个盒饭
|
作者为兼职做开源,平时还需要工作,如果帮到了您可以请作者吃个盒饭
|
||||||
<img src="https://images.gitee.com/uploads/images/2022/0218/213734_b1b8197f_1766278.jpeg" width="300px" height="450px" />
|
<img src="https://images.gitee.com/uploads/images/2022/0218/213734_b1b8197f_1766278.jpeg" width="300px" height="450px" />
|
||||||
<img src="https://images.gitee.com/uploads/images/2021/0525/101713_3d18b119_1766278.jpeg" width="300px" height="450px" />
|
<img src="https://images.gitee.com/uploads/images/2021/0525/101713_3d18b119_1766278.jpeg" width="300px" height="450px" />
|
||||||
|
|
||||||
## 业务功能
|
|
||||||
|
|
||||||
| 功能 | 介绍 |
|
|
||||||
|---|---|
|
|
||||||
| 用户管理 | 用户是系统操作者,该功能主要完成系统用户配置。 |
|
|
||||||
| 部门管理 | 配置系统组织机构(公司、部门、小组),树结构展现支持数据权限。 |
|
|
||||||
| 岗位管理 | 配置系统用户所属担任职务。 |
|
|
||||||
| 菜单管理 | 配置系统菜单,操作权限,按钮权限标识等。 |
|
|
||||||
| 角色管理 | 角色菜单权限分配、设置角色按机构进行数据范围权限划分。 |
|
|
||||||
| 字典管理 | 对系统中经常使用的一些较为固定的数据进行维护。 |
|
|
||||||
| 参数管理 | 对系统动态配置常用参数。 |
|
|
||||||
| 通知公告 | 系统通知公告信息发布维护。 |
|
|
||||||
| 操作日志 | 系统正常操作日志记录和查询;系统异常信息日志记录和查询。 |
|
|
||||||
| 登录日志 | 系统登录日志记录查询包含登录异常。 |
|
|
||||||
| 文件管理 | 系统文件上传、下载等管理。 |
|
|
||||||
| 定时任务 | 在线(添加、修改、删除)任务调度包含执行结果日志。 |
|
|
||||||
| 代码生成 | 前后端代码的生成(java、html、xml、sql)支持CRUD下载 。 |
|
|
||||||
| 系统接口 | 根据业务代码自动生成相关的api接口文档。 |
|
|
||||||
| 服务监控 | 监视集群系统CPU、内存、磁盘、堆栈、在线日志、Spring相关配置等。 |
|
|
||||||
| 缓存监控 | 对系统的缓存信息查询,命令统计等。 |
|
|
||||||
| 在线构建器 | 拖动表单元素生成相应的HTML代码。 |
|
|
||||||
| 连接池监视 | 监视当前系统数据库连接池状态,可进行分析SQL找出系统性能瓶颈。 |
|
|
||||||
| 使用案例 | 系统的一些功能案例 |
|
|
||||||
|
|
||||||
## 演示图例
|
## 演示图例
|
||||||
|
|
||||||
<table border="1" cellpadding="1" cellspacing="1" style="width:500px">
|
| | |
|
||||||
<tbody>
|
|--------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------|
|
||||||
<tr>
|
|  |  |
|
||||||
<td><img src="https://oscimg.oschina.net/oscnet/up-972235bcbe3518dedd351ff0e2ee7d1031c.png" width="1920" /></td>
|
|  |  |
|
||||||
<td><img src="https://oscimg.oschina.net/oscnet/up-5e0097702fa91e2e36391de8127676a7fa1.png" width="1920" /></td>
|
|  |  |
|
||||||
</tr>
|
|  |  |
|
||||||
<tr>
|
|  |  |
|
||||||
<td>
|
|  |  |
|
||||||
<p><img src="https://oscimg.oschina.net/oscnet/up-e56e3828f48cd9886d88731766f06d5f3c1.png" width="1920" /></p>
|
|  |  |
|
||||||
</td>
|
|  |  |
|
||||||
<td><img src="https://oscimg.oschina.net/oscnet/up-0715990ea1a9f254ec2138fcd063c1f556a.png" width="1920" /></td>
|
|  |  |
|
||||||
</tr>
|
|  |  |
|
||||||
<tr>
|
|  |  |
|
||||||
<td><img src="https://oscimg.oschina.net/oscnet/up-eaf5417ccf921bb64abb959e3d8e290467f.png" width="1920" /></td>
|
|  |  |
|
||||||
<td><img src="https://oscimg.oschina.net/oscnet/up-fc285cf33095ebf8318de6999af0f473861.png" width="1920" /></td>
|
|  |  |
|
||||||
</tr>
|
|  |  |
|
||||||
<tr>
|
|  |  |
|
||||||
<td><img src="https://oscimg.oschina.net/oscnet/up-60c83fd8bd61c29df6dbf47c88355e9c272.png" width="1920" /></td>
|
|  |  |
|
||||||
<td><img src="https://oscimg.oschina.net/oscnet/up-7f731948c8b73c7d90f67f9e1c7a534d5c3.png" width="1920" /></td>
|
|  |  |
|
||||||
</tr>
|
|  |  |
|
||||||
<tr>
|
|  |  |
|
||||||
<td><img src="https://oscimg.oschina.net/oscnet/up-e4de89b5e2d20c52d3c3a47f9eb88eb8526.png" width="1920" /></td>
|
|
||||||
<td><img src="https://oscimg.oschina.net/oscnet/up-8791d823a508eb90e67c604f36f57491a67.png" width="1920" /></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><img src="https://oscimg.oschina.net/oscnet/up-4589afd99982ead331785299b894174feb6.png" width="1920" /></td>
|
|
||||||
<td><img src="https://oscimg.oschina.net/oscnet/up-8ea177cdacaea20995daf2f596b15232561.png" width="1920" /></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><img src="https://oscimg.oschina.net/oscnet/up-32d1d04c55c11f74c9129fbbc58399728c4.png" width="1920" /></td>
|
|
||||||
<td><img src="https://oscimg.oschina.net/oscnet/up-04fa118f7631b7ae6fd72299ca0a1430a63.png" width="1920" /></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><img src="https://oscimg.oschina.net/oscnet/up-fe7e85b65827802bfaadf3acd42568b58c7.png" width="1920" /></td>
|
|
||||||
<td><img src="https://oscimg.oschina.net/oscnet/up-eff2b02a54f8188022d8498cfe6af6fcc06.png" width="1920" /></td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
140
pom.xml
140
pom.xml
@ -6,49 +6,80 @@
|
|||||||
|
|
||||||
<groupId>com.ruoyi</groupId>
|
<groupId>com.ruoyi</groupId>
|
||||||
<artifactId>ruoyi-vue-plus</artifactId>
|
<artifactId>ruoyi-vue-plus</artifactId>
|
||||||
<version>4.4.0</version>
|
<version>4.7.0</version>
|
||||||
|
|
||||||
<name>RuoYi-Vue-Plus</name>
|
<name>RuoYi-Vue-Plus</name>
|
||||||
<url>https://gitee.com/JavaLionLi/RuoYi-Vue-Plus</url>
|
<url>https://gitee.com/dromara/RuoYi-Vue-Plus</url>
|
||||||
<description>RuoYi-Vue-Plus后台管理系统</description>
|
<description>RuoYi-Vue-Plus后台管理系统</description>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<ruoyi-vue-plus.version>4.4.0</ruoyi-vue-plus.version>
|
<ruoyi-vue-plus.version>4.7.0</ruoyi-vue-plus.version>
|
||||||
<spring-boot.version>2.7.6</spring-boot.version>
|
<spring-boot.version>2.7.11</spring-boot.version>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||||
<java.version>1.8</java.version>
|
<java.version>1.8</java.version>
|
||||||
<maven-jar-plugin.version>3.2.2</maven-jar-plugin.version>
|
<maven-jar-plugin.version>3.2.2</maven-jar-plugin.version>
|
||||||
<spring-boot.mybatis>2.2.2</spring-boot.mybatis>
|
<spring-boot.mybatis>2.2.2</spring-boot.mybatis>
|
||||||
<springdoc.version>1.6.13</springdoc.version>
|
<springdoc.version>1.6.15</springdoc.version>
|
||||||
<poi.version>5.2.3</poi.version>
|
<poi.version>5.2.3</poi.version>
|
||||||
<easyexcel.version>3.1.3</easyexcel.version>
|
<easyexcel.version>3.2.1</easyexcel.version>
|
||||||
<velocity.version>2.3</velocity.version>
|
<velocity.version>2.3</velocity.version>
|
||||||
<satoken.version>1.33.0</satoken.version>
|
<satoken.version>1.34.0</satoken.version>
|
||||||
<mybatis-plus.version>3.5.2</mybatis-plus.version>
|
<mybatis-plus.version>3.5.3.1</mybatis-plus.version>
|
||||||
<p6spy.version>3.9.1</p6spy.version>
|
<p6spy.version>3.9.1</p6spy.version>
|
||||||
<hutool.version>5.8.10</hutool.version>
|
<hutool.version>5.8.18</hutool.version>
|
||||||
<okhttp.version>4.10.0</okhttp.version>
|
<okhttp.version>4.10.0</okhttp.version>
|
||||||
<spring-boot-admin.version>2.7.7</spring-boot-admin.version>
|
<spring-boot-admin.version>2.7.10</spring-boot-admin.version>
|
||||||
<redisson.version>3.18.0</redisson.version>
|
<redisson.version>3.20.1</redisson.version>
|
||||||
<lock4j.version>2.2.3</lock4j.version>
|
<lock4j.version>2.2.3</lock4j.version>
|
||||||
<dynamic-ds.version>3.5.2</dynamic-ds.version>
|
<dynamic-ds.version>3.5.2</dynamic-ds.version>
|
||||||
<alibaba-ttl.version>2.14.2</alibaba-ttl.version>
|
<alibaba-ttl.version>2.14.2</alibaba-ttl.version>
|
||||||
<xxl-job.version>2.3.1</xxl-job.version>
|
<xxl-job.version>2.4.0</xxl-job.version>
|
||||||
<lombok.version>1.18.24</lombok.version>
|
<lombok.version>1.18.26</lombok.version>
|
||||||
|
<bouncycastle.version>1.72</bouncycastle.version>
|
||||||
|
<!-- 离线IP地址定位库 -->
|
||||||
|
<ip2region.version>2.7.0</ip2region.version>
|
||||||
|
|
||||||
<!-- 统一 guava 版本 解决隐式漏洞问题 -->
|
|
||||||
<guava.version>31.1-jre</guava.version>
|
|
||||||
<!-- 临时修复 snakeyaml 漏洞 -->
|
<!-- 临时修复 snakeyaml 漏洞 -->
|
||||||
<snakeyaml.version>1.32</snakeyaml.version>
|
<snakeyaml.version>1.33</snakeyaml.version>
|
||||||
|
|
||||||
<!-- OSS 配置 -->
|
<!-- OSS 配置 -->
|
||||||
<aws-java-sdk-s3.version>1.12.349</aws-java-sdk-s3.version>
|
<aws-java-sdk-s3.version>1.12.400</aws-java-sdk-s3.version>
|
||||||
<!-- SMS 配置 -->
|
<!-- SMS 配置 -->
|
||||||
<aliyun.sms.version>2.0.22</aliyun.sms.version>
|
<aliyun.sms.version>2.0.23</aliyun.sms.version>
|
||||||
<tencent.sms.version>3.1.635</tencent.sms.version>
|
<tencent.sms.version>3.1.687</tencent.sms.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
|
<profiles>
|
||||||
|
<profile>
|
||||||
|
<id>local</id>
|
||||||
|
<properties>
|
||||||
|
<!-- 环境标识,需要与配置文件的名称相对应 -->
|
||||||
|
<profiles.active>local</profiles.active>
|
||||||
|
<logging.level>debug</logging.level>
|
||||||
|
</properties>
|
||||||
|
</profile>
|
||||||
|
<profile>
|
||||||
|
<id>dev</id>
|
||||||
|
<properties>
|
||||||
|
<!-- 环境标识,需要与配置文件的名称相对应 -->
|
||||||
|
<profiles.active>dev</profiles.active>
|
||||||
|
<logging.level>debug</logging.level>
|
||||||
|
</properties>
|
||||||
|
<activation>
|
||||||
|
<!-- 默认环境 -->
|
||||||
|
<activeByDefault>true</activeByDefault>
|
||||||
|
</activation>
|
||||||
|
</profile>
|
||||||
|
<profile>
|
||||||
|
<id>prod</id>
|
||||||
|
<properties>
|
||||||
|
<profiles.active>prod</profiles.active>
|
||||||
|
<logging.level>warn</logging.level>
|
||||||
|
</properties>
|
||||||
|
</profile>
|
||||||
|
</profiles>
|
||||||
|
|
||||||
<!-- 依赖声明 -->
|
<!-- 依赖声明 -->
|
||||||
<dependencyManagement>
|
<dependencyManagement>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
@ -149,11 +180,7 @@
|
|||||||
<artifactId>mybatis-plus-boot-starter</artifactId>
|
<artifactId>mybatis-plus-boot-starter</artifactId>
|
||||||
<version>${mybatis-plus.version}</version>
|
<version>${mybatis-plus.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>com.baomidou</groupId>
|
|
||||||
<artifactId>mybatis-plus-extension</artifactId>
|
|
||||||
<version>${mybatis-plus.version}</version>
|
|
||||||
</dependency>
|
|
||||||
<!-- sql性能分析插件 -->
|
<!-- sql性能分析插件 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>p6spy</groupId>
|
<groupId>p6spy</groupId>
|
||||||
@ -201,7 +228,19 @@
|
|||||||
<groupId>org.redisson</groupId>
|
<groupId>org.redisson</groupId>
|
||||||
<artifactId>redisson-spring-boot-starter</artifactId>
|
<artifactId>redisson-spring-boot-starter</artifactId>
|
||||||
<version>${redisson.version}</version>
|
<version>${redisson.version}</version>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.redisson</groupId>
|
||||||
|
<artifactId>redisson-spring-data-30</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.redisson</groupId>
|
||||||
|
<artifactId>redisson-spring-data-27</artifactId>
|
||||||
|
<version>${redisson.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.baomidou</groupId>
|
<groupId>com.baomidou</groupId>
|
||||||
<artifactId>lock4j-redisson-spring-boot-starter</artifactId>
|
<artifactId>lock4j-redisson-spring-boot-starter</artifactId>
|
||||||
@ -221,11 +260,11 @@
|
|||||||
<version>${alibaba-ttl.version}</version>
|
<version>${alibaba-ttl.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- 统一 guava 版本 解决隐式漏洞问题 -->
|
<!-- 离线IP地址定位库 ip2region -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.google.guava</groupId>
|
<groupId>org.lionsoul</groupId>
|
||||||
<artifactId>guava</artifactId>
|
<artifactId>ip2region</artifactId>
|
||||||
<version>${guava.version}</version>
|
<version>${ip2region.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- 临时修复 snakeyaml 漏洞 -->
|
<!-- 临时修复 snakeyaml 漏洞 -->
|
||||||
@ -235,6 +274,13 @@
|
|||||||
<version>${snakeyaml.version}</version>
|
<version>${snakeyaml.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 加密包引入 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.bouncycastle</groupId>
|
||||||
|
<artifactId>bcprov-jdk15to18</artifactId>
|
||||||
|
<version>${bouncycastle.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- 定时任务 -->
|
<!-- 定时任务 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.ruoyi</groupId>
|
<groupId>com.ruoyi</groupId>
|
||||||
@ -374,8 +420,8 @@
|
|||||||
<repositories>
|
<repositories>
|
||||||
<repository>
|
<repository>
|
||||||
<id>public</id>
|
<id>public</id>
|
||||||
<name>huawei nexus</name>
|
<name>aliyun nexus</name>
|
||||||
<url>https://mirrors.huaweicloud.com/repository/maven/</url>
|
<url>https://maven.aliyun.com/repository/public/</url>
|
||||||
<releases>
|
<releases>
|
||||||
<enabled>true</enabled>
|
<enabled>true</enabled>
|
||||||
</releases>
|
</releases>
|
||||||
@ -385,8 +431,8 @@
|
|||||||
<pluginRepositories>
|
<pluginRepositories>
|
||||||
<pluginRepository>
|
<pluginRepository>
|
||||||
<id>public</id>
|
<id>public</id>
|
||||||
<name>huawei nexus</name>
|
<name>aliyun nexus</name>
|
||||||
<url>https://mirrors.huaweicloud.com/repository/maven/</url>
|
<url>https://maven.aliyun.com/repository/public/</url>
|
||||||
<releases>
|
<releases>
|
||||||
<enabled>true</enabled>
|
<enabled>true</enabled>
|
||||||
</releases>
|
</releases>
|
||||||
@ -396,36 +442,6 @@
|
|||||||
</pluginRepository>
|
</pluginRepository>
|
||||||
</pluginRepositories>
|
</pluginRepositories>
|
||||||
|
|
||||||
<profiles>
|
|
||||||
<profile>
|
|
||||||
<id>local</id>
|
|
||||||
<properties>
|
|
||||||
<!-- 环境标识,需要与配置文件的名称相对应 -->
|
|
||||||
<profiles.active>local</profiles.active>
|
|
||||||
<logging.level>debug</logging.level>
|
|
||||||
</properties>
|
|
||||||
</profile>
|
|
||||||
<profile>
|
|
||||||
<id>dev</id>
|
|
||||||
<properties>
|
|
||||||
<!-- 环境标识,需要与配置文件的名称相对应 -->
|
|
||||||
<profiles.active>dev</profiles.active>
|
|
||||||
<logging.level>debug</logging.level>
|
|
||||||
</properties>
|
|
||||||
<activation>
|
|
||||||
<!-- 默认环境 -->
|
|
||||||
<activeByDefault>true</activeByDefault>
|
|
||||||
</activation>
|
|
||||||
</profile>
|
|
||||||
<profile>
|
|
||||||
<id>prod</id>
|
|
||||||
<properties>
|
|
||||||
<profiles.active>prod</profiles.active>
|
|
||||||
<logging.level>warn</logging.level>
|
|
||||||
</properties>
|
|
||||||
</profile>
|
|
||||||
</profiles>
|
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>ruoyi-vue-plus</artifactId>
|
<artifactId>ruoyi-vue-plus</artifactId>
|
||||||
<groupId>com.ruoyi</groupId>
|
<groupId>com.ruoyi</groupId>
|
||||||
<version>4.4.0</version>
|
<version>4.7.0</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
@ -3,7 +3,6 @@ package com.ruoyi.web.controller.common;
|
|||||||
import cn.dev33.satoken.annotation.SaIgnore;
|
import cn.dev33.satoken.annotation.SaIgnore;
|
||||||
import cn.hutool.captcha.AbstractCaptcha;
|
import cn.hutool.captcha.AbstractCaptcha;
|
||||||
import cn.hutool.captcha.generator.CodeGenerator;
|
import cn.hutool.captcha.generator.CodeGenerator;
|
||||||
import cn.hutool.core.convert.Convert;
|
|
||||||
import cn.hutool.core.util.IdUtil;
|
import cn.hutool.core.util.IdUtil;
|
||||||
import cn.hutool.core.util.RandomUtil;
|
import cn.hutool.core.util.RandomUtil;
|
||||||
import com.ruoyi.common.constant.CacheConstants;
|
import com.ruoyi.common.constant.CacheConstants;
|
||||||
@ -11,16 +10,21 @@ import com.ruoyi.common.constant.Constants;
|
|||||||
import com.ruoyi.common.core.domain.R;
|
import com.ruoyi.common.core.domain.R;
|
||||||
import com.ruoyi.common.enums.CaptchaType;
|
import com.ruoyi.common.enums.CaptchaType;
|
||||||
import com.ruoyi.common.utils.StringUtils;
|
import com.ruoyi.common.utils.StringUtils;
|
||||||
|
import com.ruoyi.common.utils.email.MailUtils;
|
||||||
import com.ruoyi.common.utils.redis.RedisUtils;
|
import com.ruoyi.common.utils.redis.RedisUtils;
|
||||||
import com.ruoyi.common.utils.reflect.ReflectUtils;
|
import com.ruoyi.common.utils.reflect.ReflectUtils;
|
||||||
import com.ruoyi.common.utils.spring.SpringUtils;
|
import com.ruoyi.common.utils.spring.SpringUtils;
|
||||||
import com.ruoyi.framework.config.properties.CaptchaProperties;
|
import com.ruoyi.framework.config.properties.CaptchaProperties;
|
||||||
|
import com.ruoyi.framework.config.properties.MailProperties;
|
||||||
import com.ruoyi.sms.config.properties.SmsProperties;
|
import com.ruoyi.sms.config.properties.SmsProperties;
|
||||||
import com.ruoyi.sms.core.SmsTemplate;
|
import com.ruoyi.sms.core.SmsTemplate;
|
||||||
import com.ruoyi.sms.entity.SmsResult;
|
import com.ruoyi.sms.entity.SmsResult;
|
||||||
import com.ruoyi.system.service.ISysConfigService;
|
import com.ruoyi.system.service.ISysConfigService;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.expression.Expression;
|
||||||
|
import org.springframework.expression.ExpressionParser;
|
||||||
|
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
@ -45,6 +49,7 @@ public class CaptchaController {
|
|||||||
private final CaptchaProperties captchaProperties;
|
private final CaptchaProperties captchaProperties;
|
||||||
private final SmsProperties smsProperties;
|
private final SmsProperties smsProperties;
|
||||||
private final ISysConfigService configService;
|
private final ISysConfigService configService;
|
||||||
|
private final MailProperties mailProperties;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 短信验证码
|
* 短信验证码
|
||||||
@ -52,8 +57,7 @@ public class CaptchaController {
|
|||||||
* @param phonenumber 用户手机号
|
* @param phonenumber 用户手机号
|
||||||
*/
|
*/
|
||||||
@GetMapping("/captchaSms")
|
@GetMapping("/captchaSms")
|
||||||
public R<Void> smsCaptcha(@NotBlank(message = "{user.phonenumber.not.blank}")
|
public R<Void> smsCaptcha(@NotBlank(message = "{user.phonenumber.not.blank}") String phonenumber) {
|
||||||
String phonenumber) {
|
|
||||||
if (!smsProperties.getEnabled()) {
|
if (!smsProperties.getEnabled()) {
|
||||||
return R.fail("当前系统没有开启短信功能!");
|
return R.fail("当前系统没有开启短信功能!");
|
||||||
}
|
}
|
||||||
@ -73,6 +77,28 @@ public class CaptchaController {
|
|||||||
return R.ok();
|
return R.ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 邮箱验证码
|
||||||
|
*
|
||||||
|
* @param email 邮箱
|
||||||
|
*/
|
||||||
|
@GetMapping("/captchaEmail")
|
||||||
|
public R<Void> emailCode(@NotBlank(message = "{user.email.not.blank}") String email) {
|
||||||
|
if (!mailProperties.getEnabled()) {
|
||||||
|
return R.fail("当前系统没有开启邮箱功能!");
|
||||||
|
}
|
||||||
|
String key = CacheConstants.CAPTCHA_CODE_KEY + email;
|
||||||
|
String code = RandomUtil.randomNumbers(4);
|
||||||
|
RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
|
||||||
|
try {
|
||||||
|
MailUtils.sendText(email, "登录验证码", "您本次验证码为:" + code + ",有效性为" + Constants.CAPTCHA_EXPIRATION + "分钟,请尽快填写。");
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("验证码短信发送异常 => {}", e.getMessage());
|
||||||
|
return R.fail(e.getMessage());
|
||||||
|
}
|
||||||
|
return R.ok();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 生成验证码
|
* 生成验证码
|
||||||
*/
|
*/
|
||||||
@ -95,28 +121,16 @@ public class CaptchaController {
|
|||||||
AbstractCaptcha captcha = SpringUtils.getBean(captchaProperties.getCategory().getClazz());
|
AbstractCaptcha captcha = SpringUtils.getBean(captchaProperties.getCategory().getClazz());
|
||||||
captcha.setGenerator(codeGenerator);
|
captcha.setGenerator(codeGenerator);
|
||||||
captcha.createCode();
|
captcha.createCode();
|
||||||
String code = isMath ? getCodeResult(captcha.getCode()) : captcha.getCode();
|
String code = captcha.getCode();
|
||||||
|
if (isMath) {
|
||||||
|
ExpressionParser parser = new SpelExpressionParser();
|
||||||
|
Expression exp = parser.parseExpression(StringUtils.remove(code, "="));
|
||||||
|
code = exp.getValue(String.class);
|
||||||
|
}
|
||||||
RedisUtils.setCacheObject(verifyKey, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
|
RedisUtils.setCacheObject(verifyKey, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
|
||||||
ajax.put("uuid", uuid);
|
ajax.put("uuid", uuid);
|
||||||
ajax.put("img", captcha.getImageBase64());
|
ajax.put("img", captcha.getImageBase64());
|
||||||
return R.ok(ajax);
|
return R.ok(ajax);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getCodeResult(String capStr) {
|
|
||||||
int numberLength = captchaProperties.getNumberLength();
|
|
||||||
int a = Convert.toInt(StringUtils.substring(capStr, 0, numberLength).trim());
|
|
||||||
char operator = capStr.charAt(numberLength);
|
|
||||||
int b = Convert.toInt(StringUtils.substring(capStr, numberLength + 1, numberLength + 1 + numberLength).trim());
|
|
||||||
switch (operator) {
|
|
||||||
case '*':
|
|
||||||
return Convert.toStr(a * b);
|
|
||||||
case '+':
|
|
||||||
return Convert.toStr(a + b);
|
|
||||||
case '-':
|
|
||||||
return Convert.toStr(a - b);
|
|
||||||
default:
|
|
||||||
return StringUtils.EMPTY;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,6 @@ public class CacheController {
|
|||||||
private final static List<SysCache> CACHES = new ArrayList<>();
|
private final static List<SysCache> CACHES = new ArrayList<>();
|
||||||
|
|
||||||
static {
|
static {
|
||||||
CACHES.add(new SysCache(CacheConstants.LOGIN_TOKEN_KEY, "用户信息"));
|
|
||||||
CACHES.add(new SysCache(CacheConstants.ONLINE_TOKEN_KEY, "在线用户"));
|
CACHES.add(new SysCache(CacheConstants.ONLINE_TOKEN_KEY, "在线用户"));
|
||||||
CACHES.add(new SysCache(CacheNames.SYS_CONFIG, "配置信息"));
|
CACHES.add(new SysCache(CacheNames.SYS_CONFIG, "配置信息"));
|
||||||
CACHES.add(new SysCache(CacheNames.SYS_DICT, "数据字典"));
|
CACHES.add(new SysCache(CacheNames.SYS_DICT, "数据字典"));
|
||||||
|
@ -45,7 +45,7 @@ public class SysUserOnlineController extends BaseController {
|
|||||||
List<String> keys = StpUtil.searchTokenValue("", 0, -1, false);
|
List<String> keys = StpUtil.searchTokenValue("", 0, -1, false);
|
||||||
List<UserOnlineDTO> userOnlineDTOList = new ArrayList<>();
|
List<UserOnlineDTO> userOnlineDTOList = new ArrayList<>();
|
||||||
for (String key : keys) {
|
for (String key : keys) {
|
||||||
String token = key.replace(CacheConstants.LOGIN_TOKEN_KEY, "");
|
String token = StringUtils.substringAfterLast(key, ":");
|
||||||
// 如果已经过期则跳过
|
// 如果已经过期则跳过
|
||||||
if (StpUtil.stpLogic.getTokenActivityTimeoutByToken(token) < -1) {
|
if (StpUtil.stpLogic.getTokenActivityTimeoutByToken(token) < -1) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -79,7 +79,7 @@ public class SysConfigController extends BaseController {
|
|||||||
@Log(title = "参数管理", businessType = BusinessType.INSERT)
|
@Log(title = "参数管理", businessType = BusinessType.INSERT)
|
||||||
@PostMapping
|
@PostMapping
|
||||||
public R<Void> add(@Validated @RequestBody SysConfig config) {
|
public R<Void> add(@Validated @RequestBody SysConfig config) {
|
||||||
if (UserConstants.NOT_UNIQUE.equals(configService.checkConfigKeyUnique(config))) {
|
if (!configService.checkConfigKeyUnique(config)) {
|
||||||
return R.fail("新增参数'" + config.getConfigName() + "'失败,参数键名已存在");
|
return R.fail("新增参数'" + config.getConfigName() + "'失败,参数键名已存在");
|
||||||
}
|
}
|
||||||
configService.insertConfig(config);
|
configService.insertConfig(config);
|
||||||
@ -93,7 +93,7 @@ public class SysConfigController extends BaseController {
|
|||||||
@Log(title = "参数管理", businessType = BusinessType.UPDATE)
|
@Log(title = "参数管理", businessType = BusinessType.UPDATE)
|
||||||
@PutMapping
|
@PutMapping
|
||||||
public R<Void> edit(@Validated @RequestBody SysConfig config) {
|
public R<Void> edit(@Validated @RequestBody SysConfig config) {
|
||||||
if (UserConstants.NOT_UNIQUE.equals(configService.checkConfigKeyUnique(config))) {
|
if (!configService.checkConfigKeyUnique(config)) {
|
||||||
return R.fail("修改参数'" + config.getConfigName() + "'失败,参数键名已存在");
|
return R.fail("修改参数'" + config.getConfigName() + "'失败,参数键名已存在");
|
||||||
}
|
}
|
||||||
configService.updateConfig(config);
|
configService.updateConfig(config);
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package com.ruoyi.web.controller.system;
|
package com.ruoyi.web.controller.system;
|
||||||
|
|
||||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||||
import cn.hutool.core.util.ArrayUtil;
|
import cn.hutool.core.convert.Convert;
|
||||||
import com.ruoyi.common.annotation.Log;
|
import com.ruoyi.common.annotation.Log;
|
||||||
import com.ruoyi.common.constant.UserConstants;
|
import com.ruoyi.common.constant.UserConstants;
|
||||||
import com.ruoyi.common.core.controller.BaseController;
|
import com.ruoyi.common.core.controller.BaseController;
|
||||||
@ -49,7 +49,7 @@ public class SysDeptController extends BaseController {
|
|||||||
public R<List<SysDept>> excludeChild(@PathVariable(value = "deptId", required = false) Long deptId) {
|
public R<List<SysDept>> excludeChild(@PathVariable(value = "deptId", required = false) Long deptId) {
|
||||||
List<SysDept> depts = deptService.selectDeptList(new SysDept());
|
List<SysDept> depts = deptService.selectDeptList(new SysDept());
|
||||||
depts.removeIf(d -> d.getDeptId().equals(deptId)
|
depts.removeIf(d -> d.getDeptId().equals(deptId)
|
||||||
|| ArrayUtil.contains(StringUtils.split(d.getAncestors(), ","), deptId + ""));
|
|| StringUtils.splitList(d.getAncestors()).contains(Convert.toStr(deptId)));
|
||||||
return R.ok(depts);
|
return R.ok(depts);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,7 +72,7 @@ public class SysDeptController extends BaseController {
|
|||||||
@Log(title = "部门管理", businessType = BusinessType.INSERT)
|
@Log(title = "部门管理", businessType = BusinessType.INSERT)
|
||||||
@PostMapping
|
@PostMapping
|
||||||
public R<Void> add(@Validated @RequestBody SysDept dept) {
|
public R<Void> add(@Validated @RequestBody SysDept dept) {
|
||||||
if (UserConstants.NOT_UNIQUE.equals(deptService.checkDeptNameUnique(dept))) {
|
if (!deptService.checkDeptNameUnique(dept)) {
|
||||||
return R.fail("新增部门'" + dept.getDeptName() + "'失败,部门名称已存在");
|
return R.fail("新增部门'" + dept.getDeptName() + "'失败,部门名称已存在");
|
||||||
}
|
}
|
||||||
return toAjax(deptService.insertDept(dept));
|
return toAjax(deptService.insertDept(dept));
|
||||||
@ -87,7 +87,7 @@ public class SysDeptController extends BaseController {
|
|||||||
public R<Void> edit(@Validated @RequestBody SysDept dept) {
|
public R<Void> edit(@Validated @RequestBody SysDept dept) {
|
||||||
Long deptId = dept.getDeptId();
|
Long deptId = dept.getDeptId();
|
||||||
deptService.checkDeptDataScope(deptId);
|
deptService.checkDeptDataScope(deptId);
|
||||||
if (UserConstants.NOT_UNIQUE.equals(deptService.checkDeptNameUnique(dept))) {
|
if (!deptService.checkDeptNameUnique(dept)) {
|
||||||
return R.fail("修改部门'" + dept.getDeptName() + "'失败,部门名称已存在");
|
return R.fail("修改部门'" + dept.getDeptName() + "'失败,部门名称已存在");
|
||||||
} else if (dept.getParentId().equals(deptId)) {
|
} else if (dept.getParentId().equals(deptId)) {
|
||||||
return R.fail("修改部门'" + dept.getDeptName() + "'失败,上级部门不能是自己");
|
return R.fail("修改部门'" + dept.getDeptName() + "'失败,上级部门不能是自己");
|
||||||
|
@ -69,7 +69,7 @@ public class SysDictTypeController extends BaseController {
|
|||||||
@Log(title = "字典类型", businessType = BusinessType.INSERT)
|
@Log(title = "字典类型", businessType = BusinessType.INSERT)
|
||||||
@PostMapping
|
@PostMapping
|
||||||
public R<Void> add(@Validated @RequestBody SysDictType dict) {
|
public R<Void> add(@Validated @RequestBody SysDictType dict) {
|
||||||
if (UserConstants.NOT_UNIQUE.equals(dictTypeService.checkDictTypeUnique(dict))) {
|
if (!dictTypeService.checkDictTypeUnique(dict)) {
|
||||||
return R.fail("新增字典'" + dict.getDictName() + "'失败,字典类型已存在");
|
return R.fail("新增字典'" + dict.getDictName() + "'失败,字典类型已存在");
|
||||||
}
|
}
|
||||||
dictTypeService.insertDictType(dict);
|
dictTypeService.insertDictType(dict);
|
||||||
@ -83,7 +83,7 @@ public class SysDictTypeController extends BaseController {
|
|||||||
@Log(title = "字典类型", businessType = BusinessType.UPDATE)
|
@Log(title = "字典类型", businessType = BusinessType.UPDATE)
|
||||||
@PutMapping
|
@PutMapping
|
||||||
public R<Void> edit(@Validated @RequestBody SysDictType dict) {
|
public R<Void> edit(@Validated @RequestBody SysDictType dict) {
|
||||||
if (UserConstants.NOT_UNIQUE.equals(dictTypeService.checkDictTypeUnique(dict))) {
|
if (!dictTypeService.checkDictTypeUnique(dict)) {
|
||||||
return R.fail("修改字典'" + dict.getDictName() + "'失败,字典类型已存在");
|
return R.fail("修改字典'" + dict.getDictName() + "'失败,字典类型已存在");
|
||||||
}
|
}
|
||||||
dictTypeService.updateDictType(dict);
|
dictTypeService.updateDictType(dict);
|
||||||
|
@ -5,6 +5,7 @@ import com.ruoyi.common.constant.Constants;
|
|||||||
import com.ruoyi.common.core.domain.R;
|
import com.ruoyi.common.core.domain.R;
|
||||||
import com.ruoyi.common.core.domain.entity.SysMenu;
|
import com.ruoyi.common.core.domain.entity.SysMenu;
|
||||||
import com.ruoyi.common.core.domain.entity.SysUser;
|
import com.ruoyi.common.core.domain.entity.SysUser;
|
||||||
|
import com.ruoyi.common.core.domain.model.EmailLoginBody;
|
||||||
import com.ruoyi.common.core.domain.model.LoginBody;
|
import com.ruoyi.common.core.domain.model.LoginBody;
|
||||||
import com.ruoyi.common.core.domain.model.LoginUser;
|
import com.ruoyi.common.core.domain.model.LoginUser;
|
||||||
import com.ruoyi.common.core.domain.model.SmsLoginBody;
|
import com.ruoyi.common.core.domain.model.SmsLoginBody;
|
||||||
@ -13,7 +14,6 @@ import com.ruoyi.system.domain.vo.RouterVo;
|
|||||||
import com.ruoyi.system.service.ISysMenuService;
|
import com.ruoyi.system.service.ISysMenuService;
|
||||||
import com.ruoyi.system.service.ISysUserService;
|
import com.ruoyi.system.service.ISysUserService;
|
||||||
import com.ruoyi.system.service.SysLoginService;
|
import com.ruoyi.system.service.SysLoginService;
|
||||||
import com.ruoyi.system.service.SysPermissionService;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
@ -39,7 +39,6 @@ public class SysLoginController {
|
|||||||
private final SysLoginService loginService;
|
private final SysLoginService loginService;
|
||||||
private final ISysMenuService menuService;
|
private final ISysMenuService menuService;
|
||||||
private final ISysUserService userService;
|
private final ISysUserService userService;
|
||||||
private final SysPermissionService permissionService;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 登录方法
|
* 登录方法
|
||||||
@ -59,7 +58,7 @@ public class SysLoginController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 短信登录(示例)
|
* 短信登录
|
||||||
*
|
*
|
||||||
* @param smsLoginBody 登录信息
|
* @param smsLoginBody 登录信息
|
||||||
* @return 结果
|
* @return 结果
|
||||||
@ -74,6 +73,21 @@ public class SysLoginController {
|
|||||||
return R.ok(ajax);
|
return R.ok(ajax);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 邮件登录
|
||||||
|
*
|
||||||
|
* @param body 登录信息
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
@PostMapping("/emailLogin")
|
||||||
|
public R<Map<String, Object>> emailLogin(@Validated @RequestBody EmailLoginBody body) {
|
||||||
|
Map<String, Object> ajax = new HashMap<>();
|
||||||
|
// 生成令牌
|
||||||
|
String token = loginService.emailLogin(body.getEmail(), body.getEmailCode());
|
||||||
|
ajax.put(Constants.TOKEN, token);
|
||||||
|
return R.ok(ajax);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 小程序登录(示例)
|
* 小程序登录(示例)
|
||||||
*
|
*
|
||||||
|
@ -82,7 +82,7 @@ public class SysMenuController extends BaseController {
|
|||||||
@Log(title = "菜单管理", businessType = BusinessType.INSERT)
|
@Log(title = "菜单管理", businessType = BusinessType.INSERT)
|
||||||
@PostMapping
|
@PostMapping
|
||||||
public R<Void> add(@Validated @RequestBody SysMenu menu) {
|
public R<Void> add(@Validated @RequestBody SysMenu menu) {
|
||||||
if (UserConstants.NOT_UNIQUE.equals(menuService.checkMenuNameUnique(menu))) {
|
if (!menuService.checkMenuNameUnique(menu)) {
|
||||||
return R.fail("新增菜单'" + menu.getMenuName() + "'失败,菜单名称已存在");
|
return R.fail("新增菜单'" + menu.getMenuName() + "'失败,菜单名称已存在");
|
||||||
} else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath())) {
|
} else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath())) {
|
||||||
return R.fail("新增菜单'" + menu.getMenuName() + "'失败,地址必须以http(s)://开头");
|
return R.fail("新增菜单'" + menu.getMenuName() + "'失败,地址必须以http(s)://开头");
|
||||||
@ -97,7 +97,7 @@ public class SysMenuController extends BaseController {
|
|||||||
@Log(title = "菜单管理", businessType = BusinessType.UPDATE)
|
@Log(title = "菜单管理", businessType = BusinessType.UPDATE)
|
||||||
@PutMapping
|
@PutMapping
|
||||||
public R<Void> edit(@Validated @RequestBody SysMenu menu) {
|
public R<Void> edit(@Validated @RequestBody SysMenu menu) {
|
||||||
if (UserConstants.NOT_UNIQUE.equals(menuService.checkMenuNameUnique(menu))) {
|
if (!menuService.checkMenuNameUnique(menu)) {
|
||||||
return R.fail("修改菜单'" + menu.getMenuName() + "'失败,菜单名称已存在");
|
return R.fail("修改菜单'" + menu.getMenuName() + "'失败,菜单名称已存在");
|
||||||
} else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath())) {
|
} else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath())) {
|
||||||
return R.fail("修改菜单'" + menu.getMenuName() + "'失败,地址必须以http(s)://开头");
|
return R.fail("修改菜单'" + menu.getMenuName() + "'失败,地址必须以http(s)://开头");
|
||||||
|
@ -2,10 +2,7 @@ package com.ruoyi.web.controller.system;
|
|||||||
|
|
||||||
|
|
||||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||||
import cn.hutool.core.convert.Convert;
|
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
import cn.hutool.http.HttpException;
|
|
||||||
import cn.hutool.http.HttpUtil;
|
|
||||||
import com.ruoyi.common.annotation.Log;
|
import com.ruoyi.common.annotation.Log;
|
||||||
import com.ruoyi.common.core.controller.BaseController;
|
import com.ruoyi.common.core.controller.BaseController;
|
||||||
import com.ruoyi.common.core.domain.PageQuery;
|
import com.ruoyi.common.core.domain.PageQuery;
|
||||||
@ -13,11 +10,6 @@ import com.ruoyi.common.core.domain.R;
|
|||||||
import com.ruoyi.common.core.page.TableDataInfo;
|
import com.ruoyi.common.core.page.TableDataInfo;
|
||||||
import com.ruoyi.common.core.validate.QueryGroup;
|
import com.ruoyi.common.core.validate.QueryGroup;
|
||||||
import com.ruoyi.common.enums.BusinessType;
|
import com.ruoyi.common.enums.BusinessType;
|
||||||
import com.ruoyi.common.exception.ServiceException;
|
|
||||||
import com.ruoyi.common.utils.file.FileUtils;
|
|
||||||
import com.ruoyi.oss.core.OssClient;
|
|
||||||
import com.ruoyi.oss.factory.OssFactory;
|
|
||||||
import com.ruoyi.system.domain.SysOss;
|
|
||||||
import com.ruoyi.system.domain.bo.SysOssBo;
|
import com.ruoyi.system.domain.bo.SysOssBo;
|
||||||
import com.ruoyi.system.domain.vo.SysOssVo;
|
import com.ruoyi.system.domain.vo.SysOssVo;
|
||||||
import com.ruoyi.system.service.ISysOssService;
|
import com.ruoyi.system.service.ISysOssService;
|
||||||
@ -80,7 +72,7 @@ public class SysOssController extends BaseController {
|
|||||||
@PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
|
@PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
|
||||||
public R<Map<String, String>> upload(@RequestPart("file") MultipartFile file) {
|
public R<Map<String, String>> upload(@RequestPart("file") MultipartFile file) {
|
||||||
if (ObjectUtil.isNull(file)) {
|
if (ObjectUtil.isNull(file)) {
|
||||||
throw new ServiceException("上传文件不能为空");
|
return R.fail("上传文件不能为空");
|
||||||
}
|
}
|
||||||
SysOssVo oss = iSysOssService.upload(file);
|
SysOssVo oss = iSysOssService.upload(file);
|
||||||
Map<String, String> map = new HashMap<>(2);
|
Map<String, String> map = new HashMap<>(2);
|
||||||
|
@ -69,9 +69,9 @@ public class SysPostController extends BaseController {
|
|||||||
@Log(title = "岗位管理", businessType = BusinessType.INSERT)
|
@Log(title = "岗位管理", businessType = BusinessType.INSERT)
|
||||||
@PostMapping
|
@PostMapping
|
||||||
public R<Void> add(@Validated @RequestBody SysPost post) {
|
public R<Void> add(@Validated @RequestBody SysPost post) {
|
||||||
if (UserConstants.NOT_UNIQUE.equals(postService.checkPostNameUnique(post))) {
|
if (!postService.checkPostNameUnique(post)) {
|
||||||
return R.fail("新增岗位'" + post.getPostName() + "'失败,岗位名称已存在");
|
return R.fail("新增岗位'" + post.getPostName() + "'失败,岗位名称已存在");
|
||||||
} else if (UserConstants.NOT_UNIQUE.equals(postService.checkPostCodeUnique(post))) {
|
} else if (!postService.checkPostCodeUnique(post)) {
|
||||||
return R.fail("新增岗位'" + post.getPostName() + "'失败,岗位编码已存在");
|
return R.fail("新增岗位'" + post.getPostName() + "'失败,岗位编码已存在");
|
||||||
}
|
}
|
||||||
return toAjax(postService.insertPost(post));
|
return toAjax(postService.insertPost(post));
|
||||||
@ -84,9 +84,9 @@ public class SysPostController extends BaseController {
|
|||||||
@Log(title = "岗位管理", businessType = BusinessType.UPDATE)
|
@Log(title = "岗位管理", businessType = BusinessType.UPDATE)
|
||||||
@PutMapping
|
@PutMapping
|
||||||
public R<Void> edit(@Validated @RequestBody SysPost post) {
|
public R<Void> edit(@Validated @RequestBody SysPost post) {
|
||||||
if (UserConstants.NOT_UNIQUE.equals(postService.checkPostNameUnique(post))) {
|
if (!postService.checkPostNameUnique(post)) {
|
||||||
return R.fail("修改岗位'" + post.getPostName() + "'失败,岗位名称已存在");
|
return R.fail("修改岗位'" + post.getPostName() + "'失败,岗位名称已存在");
|
||||||
} else if (UserConstants.NOT_UNIQUE.equals(postService.checkPostCodeUnique(post))) {
|
} else if (!postService.checkPostCodeUnique(post)) {
|
||||||
return R.fail("修改岗位'" + post.getPostName() + "'失败,岗位编码已存在");
|
return R.fail("修改岗位'" + post.getPostName() + "'失败,岗位编码已存在");
|
||||||
}
|
}
|
||||||
return toAjax(postService.updatePost(post));
|
return toAjax(postService.updatePost(post));
|
||||||
|
@ -58,12 +58,10 @@ public class SysProfileController extends BaseController {
|
|||||||
@Log(title = "个人信息", businessType = BusinessType.UPDATE)
|
@Log(title = "个人信息", businessType = BusinessType.UPDATE)
|
||||||
@PutMapping
|
@PutMapping
|
||||||
public R<Void> updateProfile(@RequestBody SysUser user) {
|
public R<Void> updateProfile(@RequestBody SysUser user) {
|
||||||
if (StringUtils.isNotEmpty(user.getPhonenumber())
|
if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) {
|
||||||
&& UserConstants.NOT_UNIQUE.equals(userService.checkPhoneUnique(user))) {
|
|
||||||
return R.fail("修改用户'" + user.getUserName() + "'失败,手机号码已存在");
|
return R.fail("修改用户'" + user.getUserName() + "'失败,手机号码已存在");
|
||||||
}
|
}
|
||||||
if (StringUtils.isNotEmpty(user.getEmail())
|
if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user)) {
|
||||||
&& UserConstants.NOT_UNIQUE.equals(userService.checkEmailUnique(user))) {
|
|
||||||
return R.fail("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在");
|
return R.fail("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在");
|
||||||
}
|
}
|
||||||
user.setUserId(getUserId());
|
user.setUserId(getUserId());
|
||||||
|
@ -1,19 +1,15 @@
|
|||||||
package com.ruoyi.web.controller.system;
|
package com.ruoyi.web.controller.system;
|
||||||
|
|
||||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
|
||||||
import com.ruoyi.common.annotation.Log;
|
import com.ruoyi.common.annotation.Log;
|
||||||
import com.ruoyi.common.constant.UserConstants;
|
|
||||||
import com.ruoyi.common.core.controller.BaseController;
|
import com.ruoyi.common.core.controller.BaseController;
|
||||||
import com.ruoyi.common.core.domain.PageQuery;
|
import com.ruoyi.common.core.domain.PageQuery;
|
||||||
import com.ruoyi.common.core.domain.R;
|
import com.ruoyi.common.core.domain.R;
|
||||||
import com.ruoyi.common.core.domain.entity.SysDept;
|
import com.ruoyi.common.core.domain.entity.SysDept;
|
||||||
import com.ruoyi.common.core.domain.entity.SysRole;
|
import com.ruoyi.common.core.domain.entity.SysRole;
|
||||||
import com.ruoyi.common.core.domain.entity.SysUser;
|
import com.ruoyi.common.core.domain.entity.SysUser;
|
||||||
import com.ruoyi.common.core.domain.model.LoginUser;
|
|
||||||
import com.ruoyi.common.core.page.TableDataInfo;
|
import com.ruoyi.common.core.page.TableDataInfo;
|
||||||
import com.ruoyi.common.enums.BusinessType;
|
import com.ruoyi.common.enums.BusinessType;
|
||||||
import com.ruoyi.common.helper.LoginHelper;
|
|
||||||
import com.ruoyi.common.utils.poi.ExcelUtil;
|
import com.ruoyi.common.utils.poi.ExcelUtil;
|
||||||
import com.ruoyi.system.domain.SysUserRole;
|
import com.ruoyi.system.domain.SysUserRole;
|
||||||
import com.ruoyi.system.service.ISysDeptService;
|
import com.ruoyi.system.service.ISysDeptService;
|
||||||
@ -84,9 +80,9 @@ public class SysRoleController extends BaseController {
|
|||||||
@Log(title = "角色管理", businessType = BusinessType.INSERT)
|
@Log(title = "角色管理", businessType = BusinessType.INSERT)
|
||||||
@PostMapping
|
@PostMapping
|
||||||
public R<Void> add(@Validated @RequestBody SysRole role) {
|
public R<Void> add(@Validated @RequestBody SysRole role) {
|
||||||
if (UserConstants.NOT_UNIQUE.equals(roleService.checkRoleNameUnique(role))) {
|
if (!roleService.checkRoleNameUnique(role)) {
|
||||||
return R.fail("新增角色'" + role.getRoleName() + "'失败,角色名称已存在");
|
return R.fail("新增角色'" + role.getRoleName() + "'失败,角色名称已存在");
|
||||||
} else if (UserConstants.NOT_UNIQUE.equals(roleService.checkRoleKeyUnique(role))) {
|
} else if (!roleService.checkRoleKeyUnique(role)) {
|
||||||
return R.fail("新增角色'" + role.getRoleName() + "'失败,角色权限已存在");
|
return R.fail("新增角色'" + role.getRoleName() + "'失败,角色权限已存在");
|
||||||
}
|
}
|
||||||
return toAjax(roleService.insertRole(role));
|
return toAjax(roleService.insertRole(role));
|
||||||
@ -102,20 +98,14 @@ public class SysRoleController extends BaseController {
|
|||||||
public R<Void> edit(@Validated @RequestBody SysRole role) {
|
public R<Void> edit(@Validated @RequestBody SysRole role) {
|
||||||
roleService.checkRoleAllowed(role);
|
roleService.checkRoleAllowed(role);
|
||||||
roleService.checkRoleDataScope(role.getRoleId());
|
roleService.checkRoleDataScope(role.getRoleId());
|
||||||
if (UserConstants.NOT_UNIQUE.equals(roleService.checkRoleNameUnique(role))) {
|
if (!roleService.checkRoleNameUnique(role)) {
|
||||||
return R.fail("修改角色'" + role.getRoleName() + "'失败,角色名称已存在");
|
return R.fail("修改角色'" + role.getRoleName() + "'失败,角色名称已存在");
|
||||||
} else if (UserConstants.NOT_UNIQUE.equals(roleService.checkRoleKeyUnique(role))) {
|
} else if (!roleService.checkRoleKeyUnique(role)) {
|
||||||
return R.fail("修改角色'" + role.getRoleName() + "'失败,角色权限已存在");
|
return R.fail("修改角色'" + role.getRoleName() + "'失败,角色权限已存在");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (roleService.updateRole(role) > 0) {
|
if (roleService.updateRole(role) > 0) {
|
||||||
// 更新缓存用户权限
|
roleService.cleanOnlineUserByRole(role.getRoleId());
|
||||||
LoginUser loginUser = getLoginUser();
|
|
||||||
SysUser sysUser = userService.selectUserById(loginUser.getUserId());
|
|
||||||
if (ObjectUtil.isNotNull(sysUser) && !sysUser.isAdmin()) {
|
|
||||||
loginUser.setMenuPermission(permissionService.getMenuPermission(sysUser));
|
|
||||||
LoginHelper.setLoginUser(loginUser);
|
|
||||||
}
|
|
||||||
return R.ok();
|
return R.ok();
|
||||||
}
|
}
|
||||||
return R.fail("修改角色'" + role.getRoleName() + "'失败,请联系管理员");
|
return R.fail("修改角色'" + role.getRoleName() + "'失败,请联系管理员");
|
||||||
|
@ -136,13 +136,11 @@ public class SysUserController extends BaseController {
|
|||||||
@Log(title = "用户管理", businessType = BusinessType.INSERT)
|
@Log(title = "用户管理", businessType = BusinessType.INSERT)
|
||||||
@PostMapping
|
@PostMapping
|
||||||
public R<Void> add(@Validated @RequestBody SysUser user) {
|
public R<Void> add(@Validated @RequestBody SysUser user) {
|
||||||
if (UserConstants.NOT_UNIQUE.equals(userService.checkUserNameUnique(user))) {
|
if (!userService.checkUserNameUnique(user)) {
|
||||||
return R.fail("新增用户'" + user.getUserName() + "'失败,登录账号已存在");
|
return R.fail("新增用户'" + user.getUserName() + "'失败,登录账号已存在");
|
||||||
} else if (StringUtils.isNotEmpty(user.getPhonenumber())
|
} else if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) {
|
||||||
&& UserConstants.NOT_UNIQUE.equals(userService.checkPhoneUnique(user))) {
|
|
||||||
return R.fail("新增用户'" + user.getUserName() + "'失败,手机号码已存在");
|
return R.fail("新增用户'" + user.getUserName() + "'失败,手机号码已存在");
|
||||||
} else if (StringUtils.isNotEmpty(user.getEmail())
|
} else if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user)) {
|
||||||
&& UserConstants.NOT_UNIQUE.equals(userService.checkEmailUnique(user))) {
|
|
||||||
return R.fail("新增用户'" + user.getUserName() + "'失败,邮箱账号已存在");
|
return R.fail("新增用户'" + user.getUserName() + "'失败,邮箱账号已存在");
|
||||||
}
|
}
|
||||||
user.setPassword(BCrypt.hashpw(user.getPassword()));
|
user.setPassword(BCrypt.hashpw(user.getPassword()));
|
||||||
@ -158,13 +156,11 @@ public class SysUserController extends BaseController {
|
|||||||
public R<Void> edit(@Validated @RequestBody SysUser user) {
|
public R<Void> edit(@Validated @RequestBody SysUser user) {
|
||||||
userService.checkUserAllowed(user);
|
userService.checkUserAllowed(user);
|
||||||
userService.checkUserDataScope(user.getUserId());
|
userService.checkUserDataScope(user.getUserId());
|
||||||
if (UserConstants.NOT_UNIQUE.equals(userService.checkUserNameUnique(user))) {
|
if (!userService.checkUserNameUnique(user)) {
|
||||||
return R.fail("修改用户'" + user.getUserName() + "'失败,登录账号已存在");
|
return R.fail("修改用户'" + user.getUserName() + "'失败,登录账号已存在");
|
||||||
} else if (StringUtils.isNotEmpty(user.getPhonenumber())
|
} else if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) {
|
||||||
&& UserConstants.NOT_UNIQUE.equals(userService.checkPhoneUnique(user))) {
|
|
||||||
return R.fail("修改用户'" + user.getUserName() + "'失败,手机号码已存在");
|
return R.fail("修改用户'" + user.getUserName() + "'失败,手机号码已存在");
|
||||||
} else if (StringUtils.isNotEmpty(user.getEmail())
|
} else if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user)) {
|
||||||
&& UserConstants.NOT_UNIQUE.equals(userService.checkEmailUnique(user))) {
|
|
||||||
return R.fail("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在");
|
return R.fail("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在");
|
||||||
}
|
}
|
||||||
return toAjax(userService.updateUser(user));
|
return toAjax(userService.updateUser(user));
|
||||||
|
@ -86,15 +86,17 @@ spring:
|
|||||||
# 最小空闲线程数量
|
# 最小空闲线程数量
|
||||||
minIdle: 10
|
minIdle: 10
|
||||||
# 配置获取连接等待超时的时间
|
# 配置获取连接等待超时的时间
|
||||||
connectionTimeout: 10000
|
connectionTimeout: 30000
|
||||||
# 校验超时时间
|
# 校验超时时间
|
||||||
validationTimeout: 5000
|
validationTimeout: 5000
|
||||||
# 空闲连接存活最大时间,默认10分钟
|
# 空闲连接存活最大时间,默认10分钟
|
||||||
idleTimeout: 60000
|
idleTimeout: 600000
|
||||||
# 此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认30分钟
|
# 此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认30分钟
|
||||||
maxLifetime: 900000
|
maxLifetime: 1800000
|
||||||
# 连接测试query(配置检测连接是否有效)
|
# 连接测试query(配置检测连接是否有效)
|
||||||
connectionTestQuery: SELECT 1
|
connectionTestQuery: SELECT 1
|
||||||
|
# 多久检查一次连接的活性
|
||||||
|
keepaliveTime: 30000
|
||||||
|
|
||||||
--- # redis 单机配置(单机与集群只能开启一个另一个需要注释掉)
|
--- # redis 单机配置(单机与集群只能开启一个另一个需要注释掉)
|
||||||
spring:
|
spring:
|
||||||
|
@ -89,15 +89,17 @@ spring:
|
|||||||
# 最小空闲线程数量
|
# 最小空闲线程数量
|
||||||
minIdle: 10
|
minIdle: 10
|
||||||
# 配置获取连接等待超时的时间
|
# 配置获取连接等待超时的时间
|
||||||
connectionTimeout: 10000
|
connectionTimeout: 30000
|
||||||
# 校验超时时间
|
# 校验超时时间
|
||||||
validationTimeout: 5000
|
validationTimeout: 5000
|
||||||
# 空闲连接存活最大时间,默认10分钟
|
# 空闲连接存活最大时间,默认10分钟
|
||||||
idleTimeout: 60000
|
idleTimeout: 600000
|
||||||
# 此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认30分钟
|
# 此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认30分钟
|
||||||
maxLifetime: 900000
|
maxLifetime: 1800000
|
||||||
# 连接测试query(配置检测连接是否有效)
|
# 连接测试query(配置检测连接是否有效)
|
||||||
connectionTestQuery: SELECT 1
|
connectionTestQuery: SELECT 1
|
||||||
|
# 多久检查一次连接的活性
|
||||||
|
keepaliveTime: 30000
|
||||||
|
|
||||||
--- # redis 单机配置(单机与集群只能开启一个另一个需要注释掉)
|
--- # redis 单机配置(单机与集群只能开启一个另一个需要注释掉)
|
||||||
spring:
|
spring:
|
||||||
|
@ -51,7 +51,7 @@ logging:
|
|||||||
level:
|
level:
|
||||||
com.ruoyi: @logging.level@
|
com.ruoyi: @logging.level@
|
||||||
org.springframework: warn
|
org.springframework: warn
|
||||||
config: classpath:logback.xml
|
config: classpath:logback-plus.xml
|
||||||
|
|
||||||
# 用户配置
|
# 用户配置
|
||||||
user:
|
user:
|
||||||
@ -83,6 +83,9 @@ spring:
|
|||||||
restart:
|
restart:
|
||||||
# 热部署开关
|
# 热部署开关
|
||||||
enabled: true
|
enabled: true
|
||||||
|
mvc:
|
||||||
|
format:
|
||||||
|
date-time: yyyy-MM-dd HH:mm:ss
|
||||||
jackson:
|
jackson:
|
||||||
# 日期格式化
|
# 日期格式化
|
||||||
date-format: yyyy-MM-dd HH:mm:ss
|
date-format: yyyy-MM-dd HH:mm:ss
|
||||||
@ -125,8 +128,10 @@ security:
|
|||||||
- /**/*.html
|
- /**/*.html
|
||||||
- /**/*.css
|
- /**/*.css
|
||||||
- /**/*.js
|
- /**/*.js
|
||||||
# swagger 文档配置
|
# 公共路径
|
||||||
- /favicon.ico
|
- /favicon.ico
|
||||||
|
- /error
|
||||||
|
# swagger 文档配置
|
||||||
- /*/api-docs
|
- /*/api-docs
|
||||||
- /*/api-docs/**
|
- /*/api-docs/**
|
||||||
# actuator 监控配置
|
# actuator 监控配置
|
||||||
@ -177,10 +182,22 @@ mybatis-plus:
|
|||||||
# 字段验证策略之 select,在 select 的时候的字段验证策略既 wrapper 根据内部 entity 生成的 where 条件
|
# 字段验证策略之 select,在 select 的时候的字段验证策略既 wrapper 根据内部 entity 生成的 where 条件
|
||||||
where-strategy: NOT_NULL
|
where-strategy: NOT_NULL
|
||||||
|
|
||||||
|
# 数据加密
|
||||||
|
mybatis-encryptor:
|
||||||
|
# 是否开启加密
|
||||||
|
enable: false
|
||||||
|
# 默认加密算法
|
||||||
|
algorithm: BASE64
|
||||||
|
# 编码方式 BASE64/HEX。默认BASE64
|
||||||
|
encode: BASE64
|
||||||
|
# 安全秘钥 对称算法的秘钥 如:AES,SM4
|
||||||
|
password:
|
||||||
|
# 公私钥 非对称算法的公私钥 如:SM2,RSA
|
||||||
|
publicKey:
|
||||||
|
privateKey:
|
||||||
|
|
||||||
# Swagger配置
|
# Swagger配置
|
||||||
swagger:
|
swagger:
|
||||||
# 是否开启swagger
|
|
||||||
enabled: true
|
|
||||||
info:
|
info:
|
||||||
# 标题
|
# 标题
|
||||||
title: '标题:${ruoyi.name}后台管理系统_接口文档'
|
title: '标题:${ruoyi.name}后台管理系统_接口文档'
|
||||||
@ -192,7 +209,7 @@ swagger:
|
|||||||
contact:
|
contact:
|
||||||
name: Lion Li
|
name: Lion Li
|
||||||
email: crazylionli@163.com
|
email: crazylionli@163.com
|
||||||
url: https://gitee.com/JavaLionLi/RuoYi-Vue-Plus
|
url: https://gitee.com/dromara/RuoYi-Vue-Plus
|
||||||
components:
|
components:
|
||||||
# 鉴权方式配置
|
# 鉴权方式配置
|
||||||
security-schemes:
|
security-schemes:
|
||||||
@ -202,6 +219,12 @@ swagger:
|
|||||||
name: ${sa-token.token-name}
|
name: ${sa-token.token-name}
|
||||||
|
|
||||||
springdoc:
|
springdoc:
|
||||||
|
api-docs:
|
||||||
|
# 是否开启接口文档
|
||||||
|
enabled: true
|
||||||
|
swagger-ui:
|
||||||
|
# 持久化认证数据
|
||||||
|
persistAuthorization: true
|
||||||
#这里定义了两个分组,可定义多个,也可以不定义
|
#这里定义了两个分组,可定义多个,也可以不定义
|
||||||
group-configs:
|
group-configs:
|
||||||
- group: 1.演示模块
|
- group: 1.演示模块
|
||||||
|
@ -18,6 +18,7 @@ user.password.not.blank=用户密码不能为空
|
|||||||
user.password.length.valid=用户密码长度必须在{min}到{max}个字符之间
|
user.password.length.valid=用户密码长度必须在{min}到{max}个字符之间
|
||||||
user.password.not.valid=* 5-50个字符
|
user.password.not.valid=* 5-50个字符
|
||||||
user.email.not.valid=邮箱格式错误
|
user.email.not.valid=邮箱格式错误
|
||||||
|
user.email.not.blank=邮箱不能为空
|
||||||
user.phonenumber.not.blank=用户手机号不能为空
|
user.phonenumber.not.blank=用户手机号不能为空
|
||||||
user.mobile.phone.number.not.valid=手机号格式错误
|
user.mobile.phone.number.not.valid=手机号格式错误
|
||||||
user.login.success=登录成功
|
user.login.success=登录成功
|
||||||
@ -41,5 +42,8 @@ repeat.submit.message=不允许重复提交,请稍候再试
|
|||||||
rate.limiter.message=访问过于频繁,请稍候再试
|
rate.limiter.message=访问过于频繁,请稍候再试
|
||||||
sms.code.not.blank=短信验证码不能为空
|
sms.code.not.blank=短信验证码不能为空
|
||||||
sms.code.retry.limit.count=短信验证码输入错误{0}次
|
sms.code.retry.limit.count=短信验证码输入错误{0}次
|
||||||
sms.code.retry.limit.exceed=短信验证码输入错误{0}次,帐户锁定{0}分钟
|
sms.code.retry.limit.exceed=短信验证码输入错误{0}次,帐户锁定{1}分钟
|
||||||
|
email.code.not.blank=邮箱验证码不能为空
|
||||||
|
email.code.retry.limit.count=邮箱验证码输入错误{0}次
|
||||||
|
email.code.retry.limit.exceed=邮箱验证码输入错误{0}次,帐户锁定{1}分钟
|
||||||
xcx.code.not.blank=小程序code不能为空
|
xcx.code.not.blank=小程序code不能为空
|
||||||
|
@ -18,6 +18,7 @@ user.password.not.blank=Password cannot be empty
|
|||||||
user.password.length.valid=Password length must be between {min} and {max} characters
|
user.password.length.valid=Password length must be between {min} and {max} characters
|
||||||
user.password.not.valid=* 5-50 characters
|
user.password.not.valid=* 5-50 characters
|
||||||
user.email.not.valid=Mailbox format error
|
user.email.not.valid=Mailbox format error
|
||||||
|
user.email.not.blank=Mailbox cannot be blank
|
||||||
user.phonenumber.not.blank=Phone number cannot be blank
|
user.phonenumber.not.blank=Phone number cannot be blank
|
||||||
user.mobile.phone.number.not.valid=Phone number format error
|
user.mobile.phone.number.not.valid=Phone number format error
|
||||||
user.login.success=Login successful
|
user.login.success=Login successful
|
||||||
@ -41,5 +42,8 @@ repeat.submit.message=Repeat submit is not allowed, please try again later
|
|||||||
rate.limiter.message=Visit too frequently, please try again later
|
rate.limiter.message=Visit too frequently, please try again later
|
||||||
sms.code.not.blank=Sms code cannot be blank
|
sms.code.not.blank=Sms code cannot be blank
|
||||||
sms.code.retry.limit.count=Sms code input error {0} times
|
sms.code.retry.limit.count=Sms code input error {0} times
|
||||||
sms.code.retry.limit.exceed=Sms code input error {0} times, account locked for {0} minutes
|
sms.code.retry.limit.exceed=Sms code input error {0} times, account locked for {1} minutes
|
||||||
|
email.code.not.blank=Email code cannot be blank
|
||||||
|
email.code.retry.limit.count=Email code input error {0} times
|
||||||
|
email.code.retry.limit.exceed=Email code input error {0} times, account locked for {1} minutes
|
||||||
xcx.code.not.blank=Mini program code cannot be blank
|
xcx.code.not.blank=Mini program code cannot be blank
|
||||||
|
@ -18,6 +18,7 @@ user.password.not.blank=用户密码不能为空
|
|||||||
user.password.length.valid=用户密码长度必须在{min}到{max}个字符之间
|
user.password.length.valid=用户密码长度必须在{min}到{max}个字符之间
|
||||||
user.password.not.valid=* 5-50个字符
|
user.password.not.valid=* 5-50个字符
|
||||||
user.email.not.valid=邮箱格式错误
|
user.email.not.valid=邮箱格式错误
|
||||||
|
user.email.not.blank=邮箱不能为空
|
||||||
user.phonenumber.not.blank=用户手机号不能为空
|
user.phonenumber.not.blank=用户手机号不能为空
|
||||||
user.mobile.phone.number.not.valid=手机号格式错误
|
user.mobile.phone.number.not.valid=手机号格式错误
|
||||||
user.login.success=登录成功
|
user.login.success=登录成功
|
||||||
@ -41,5 +42,8 @@ repeat.submit.message=不允许重复提交,请稍候再试
|
|||||||
rate.limiter.message=访问过于频繁,请稍候再试
|
rate.limiter.message=访问过于频繁,请稍候再试
|
||||||
sms.code.not.blank=短信验证码不能为空
|
sms.code.not.blank=短信验证码不能为空
|
||||||
sms.code.retry.limit.count=短信验证码输入错误{0}次
|
sms.code.retry.limit.count=短信验证码输入错误{0}次
|
||||||
sms.code.retry.limit.exceed=短信验证码输入错误{0}次,帐户锁定{0}分钟
|
sms.code.retry.limit.exceed=短信验证码输入错误{0}次,帐户锁定{1}分钟
|
||||||
|
email.code.not.blank=邮箱验证码不能为空
|
||||||
|
email.code.retry.limit.count=邮箱验证码输入错误{0}次
|
||||||
|
email.code.retry.limit.exceed=邮箱验证码输入错误{0}次,帐户锁定{1}分钟
|
||||||
xcx.code.not.blank=小程序code不能为空
|
xcx.code.not.blank=小程序code不能为空
|
||||||
|
BIN
ruoyi-admin/src/main/resources/ip2region.xdb
Normal file
BIN
ruoyi-admin/src/main/resources/ip2region.xdb
Normal file
Binary file not shown.
@ -5,7 +5,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>ruoyi-vue-plus</artifactId>
|
<artifactId>ruoyi-vue-plus</artifactId>
|
||||||
<groupId>com.ruoyi</groupId>
|
<groupId>com.ruoyi</groupId>
|
||||||
<version>4.4.0</version>
|
<version>4.7.0</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
@ -79,10 +79,6 @@
|
|||||||
<groupId>com.baomidou</groupId>
|
<groupId>com.baomidou</groupId>
|
||||||
<artifactId>mybatis-plus-boot-starter</artifactId>
|
<artifactId>mybatis-plus-boot-starter</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>com.baomidou</groupId>
|
|
||||||
<artifactId>mybatis-plus-extension</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- dynamic-datasource 多数据源-->
|
<!-- dynamic-datasource 多数据源-->
|
||||||
<dependency>
|
<dependency>
|
||||||
@ -147,11 +143,28 @@
|
|||||||
<artifactId>redisson-spring-boot-starter</artifactId>
|
<artifactId>redisson-spring-boot-starter</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.redisson</groupId>
|
||||||
|
<artifactId>redisson-spring-data-27</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.baomidou</groupId>
|
<groupId>com.baomidou</groupId>
|
||||||
<artifactId>lock4j-redisson-spring-boot-starter</artifactId>
|
<artifactId>lock4j-redisson-spring-boot-starter</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 加密包引入 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.bouncycastle</groupId>
|
||||||
|
<artifactId>bcprov-jdk15to18</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 离线IP地址定位库 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.lionsoul</groupId>
|
||||||
|
<artifactId>ip2region</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
@ -13,7 +13,9 @@ import java.lang.annotation.Target;
|
|||||||
* 字典数据映射注解
|
* 字典数据映射注解
|
||||||
*
|
*
|
||||||
* @author itino
|
* @author itino
|
||||||
|
* @deprecated 建议使用通用翻译注解
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Target({ElementType.FIELD, ElementType.METHOD})
|
@Target({ElementType.FIELD, ElementType.METHOD})
|
||||||
@JacksonAnnotationsInside
|
@JacksonAnnotationsInside
|
||||||
|
@ -0,0 +1,44 @@
|
|||||||
|
package com.ruoyi.common.annotation;
|
||||||
|
|
||||||
|
import com.ruoyi.common.enums.AlgorithmType;
|
||||||
|
import com.ruoyi.common.enums.EncodeType;
|
||||||
|
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字段加密注解
|
||||||
|
*
|
||||||
|
* @author 老马
|
||||||
|
*/
|
||||||
|
@Documented
|
||||||
|
@Inherited
|
||||||
|
@Target({ElementType.FIELD})
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface EncryptField {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加密算法
|
||||||
|
*/
|
||||||
|
AlgorithmType algorithm() default AlgorithmType.DEFAULT;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 秘钥。AES、SM4需要
|
||||||
|
*/
|
||||||
|
String password() default "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 公钥。RSA、SM2需要
|
||||||
|
*/
|
||||||
|
String publicKey() default "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 公钥。RSA、SM2需要
|
||||||
|
*/
|
||||||
|
String privateKey() default "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 编码方式。对加密算法为BASE64的不起作用
|
||||||
|
*/
|
||||||
|
EncodeType encode() default EncodeType.DEFAULT;
|
||||||
|
|
||||||
|
}
|
@ -1,5 +1,7 @@
|
|||||||
package com.ruoyi.common.annotation;
|
package com.ruoyi.common.annotation;
|
||||||
|
|
||||||
|
import com.ruoyi.common.utils.StringUtils;
|
||||||
|
|
||||||
import java.lang.annotation.*;
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -25,6 +27,6 @@ public @interface ExcelDictFormat {
|
|||||||
/**
|
/**
|
||||||
* 分隔符,读取字符串组内容
|
* 分隔符,读取字符串组内容
|
||||||
*/
|
*/
|
||||||
String separator() default ",";
|
String separator() default StringUtils.SEPARATOR;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,30 @@
|
|||||||
|
package com.ruoyi.common.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 枚举格式化
|
||||||
|
*
|
||||||
|
* @author Liang
|
||||||
|
*/
|
||||||
|
@Target({ElementType.FIELD})
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Inherited
|
||||||
|
public @interface ExcelEnumFormat {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字典枚举类型
|
||||||
|
*/
|
||||||
|
Class<? extends Enum<?>> enumClass();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字典枚举类中对应的code属性名称,默认为code
|
||||||
|
*/
|
||||||
|
String codeField() default "code";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字典枚举类中对应的text属性名称,默认为text
|
||||||
|
*/
|
||||||
|
String textField() default "text";
|
||||||
|
|
||||||
|
}
|
@ -38,4 +38,10 @@ public @interface Log {
|
|||||||
* 是否保存响应的参数
|
* 是否保存响应的参数
|
||||||
*/
|
*/
|
||||||
boolean isSaveResponseData() default true;
|
boolean isSaveResponseData() default true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 排除指定的请求参数
|
||||||
|
*/
|
||||||
|
String[] excludeParamNames() default {};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package com.ruoyi.common.annotation;
|
package com.ruoyi.common.annotation;
|
||||||
|
|
||||||
import com.ruoyi.common.constant.CacheConstants;
|
|
||||||
import com.ruoyi.common.enums.LimitType;
|
import com.ruoyi.common.enums.LimitType;
|
||||||
|
|
||||||
import java.lang.annotation.*;
|
import java.lang.annotation.*;
|
||||||
@ -15,9 +14,10 @@ import java.lang.annotation.*;
|
|||||||
@Documented
|
@Documented
|
||||||
public @interface RateLimiter {
|
public @interface RateLimiter {
|
||||||
/**
|
/**
|
||||||
* 限流key
|
* 限流key,支持使用Spring el表达式来动态获取方法上的参数值
|
||||||
|
* 格式类似于 #code.id #{#code}
|
||||||
*/
|
*/
|
||||||
String key() default CacheConstants.RATE_LIMIT_KEY;
|
String key() default "";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 限流时间,单位秒
|
* 限流时间,单位秒
|
||||||
@ -33,4 +33,9 @@ public @interface RateLimiter {
|
|||||||
* 限流类型
|
* 限流类型
|
||||||
*/
|
*/
|
||||||
LimitType limitType() default LimitType.DEFAULT;
|
LimitType limitType() default LimitType.DEFAULT;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提示消息 支持国际化 格式为 {code}
|
||||||
|
*/
|
||||||
|
String message() default "{rate.limiter.message}";
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,39 @@
|
|||||||
|
package com.ruoyi.common.annotation;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||||
|
import com.ruoyi.common.translation.handler.TranslationHandler;
|
||||||
|
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通用翻译注解
|
||||||
|
*
|
||||||
|
* @author Lion Li
|
||||||
|
*/
|
||||||
|
@Inherited
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target({ElementType.FIELD, ElementType.METHOD})
|
||||||
|
@Documented
|
||||||
|
@JacksonAnnotationsInside
|
||||||
|
@JsonSerialize(using = TranslationHandler.class)
|
||||||
|
public @interface Translation {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 类型 (需与实现类上的 {@link com.ruoyi.common.annotation.TranslationType} 注解type对应)
|
||||||
|
* <p>
|
||||||
|
* 默认取当前字段的值 如果设置了 @{@link Translation#mapper()} 则取映射字段的值
|
||||||
|
*/
|
||||||
|
String type();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 映射字段 (如果不为空则取此字段的值)
|
||||||
|
*/
|
||||||
|
String mapper() default "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 其他条件 例如: 字典type(sys_user_sex)
|
||||||
|
*/
|
||||||
|
String other() default "";
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
package com.ruoyi.common.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 翻译类型注解 (标注到{@link com.ruoyi.common.translation.TranslationInterface} 的实现类)
|
||||||
|
*
|
||||||
|
* @author Lion Li
|
||||||
|
*/
|
||||||
|
@Inherited
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target({ElementType.TYPE})
|
||||||
|
@Documented
|
||||||
|
public @interface TranslationType {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 类型
|
||||||
|
*/
|
||||||
|
String type();
|
||||||
|
|
||||||
|
}
|
@ -7,11 +7,6 @@ package com.ruoyi.common.constant;
|
|||||||
*/
|
*/
|
||||||
public interface CacheConstants {
|
public interface CacheConstants {
|
||||||
|
|
||||||
/**
|
|
||||||
* 登录用户 redis key
|
|
||||||
*/
|
|
||||||
String LOGIN_TOKEN_KEY = "Authorization:login:token:";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 在线用户 redis key
|
* 在线用户 redis key
|
||||||
*/
|
*/
|
||||||
|
@ -30,6 +30,16 @@ public interface CacheNames {
|
|||||||
*/
|
*/
|
||||||
String SYS_DICT = "sys_dict";
|
String SYS_DICT = "sys_dict";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户账户
|
||||||
|
*/
|
||||||
|
String SYS_USER_NAME = "sys_user_name#30d";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 部门
|
||||||
|
*/
|
||||||
|
String SYS_DEPT = "sys_dept#30d";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* OSS内容
|
* OSS内容
|
||||||
*/
|
*/
|
||||||
|
@ -0,0 +1,30 @@
|
|||||||
|
package com.ruoyi.common.constant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 翻译常量
|
||||||
|
*
|
||||||
|
* @author Lion Li
|
||||||
|
*/
|
||||||
|
public interface TransConstant {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户id转账号
|
||||||
|
*/
|
||||||
|
String USER_ID_TO_NAME = "user_id_to_name";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 部门id转名称
|
||||||
|
*/
|
||||||
|
String DEPT_ID_TO_NAME = "dept_id_to_name";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字典type转label
|
||||||
|
*/
|
||||||
|
String DICT_TYPE_TO_LABEL = "dict_type_to_label";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ossId转url
|
||||||
|
*/
|
||||||
|
String OSS_ID_TO_URL = "oss_id_to_url";
|
||||||
|
|
||||||
|
}
|
@ -112,12 +112,6 @@ public interface UserConstants {
|
|||||||
*/
|
*/
|
||||||
String INNER_LINK = "InnerLink";
|
String INNER_LINK = "InnerLink";
|
||||||
|
|
||||||
/**
|
|
||||||
* 校验返回结果码
|
|
||||||
*/
|
|
||||||
String UNIQUE = "0";
|
|
||||||
String NOT_UNIQUE = "1";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用户名长度限制
|
* 用户名长度限制
|
||||||
*/
|
*/
|
||||||
|
@ -0,0 +1,75 @@
|
|||||||
|
package com.ruoyi.common.convert;
|
||||||
|
|
||||||
|
import cn.hutool.core.annotation.AnnotationUtil;
|
||||||
|
import cn.hutool.core.convert.Convert;
|
||||||
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
|
import com.alibaba.excel.converters.Converter;
|
||||||
|
import com.alibaba.excel.enums.CellDataTypeEnum;
|
||||||
|
import com.alibaba.excel.metadata.GlobalConfiguration;
|
||||||
|
import com.alibaba.excel.metadata.data.ReadCellData;
|
||||||
|
import com.alibaba.excel.metadata.data.WriteCellData;
|
||||||
|
import com.alibaba.excel.metadata.property.ExcelContentProperty;
|
||||||
|
import com.ruoyi.common.annotation.ExcelEnumFormat;
|
||||||
|
import com.ruoyi.common.utils.reflect.ReflectUtils;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 枚举格式化转换处理
|
||||||
|
*
|
||||||
|
* @author Liang
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class ExcelEnumConvert implements Converter<Object> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<Object> supportJavaTypeKey() {
|
||||||
|
return Object.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CellDataTypeEnum supportExcelTypeKey() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
|
||||||
|
Object codeValue = cellData.getData();
|
||||||
|
// 如果是空值
|
||||||
|
if (ObjectUtil.isNull(codeValue)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Map<Object, String> enumValueMap = beforeConvert(contentProperty);
|
||||||
|
String textValue = enumValueMap.get(codeValue);
|
||||||
|
return Convert.convert(contentProperty.getField().getType(), textValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WriteCellData<String> convertToExcelData(Object object, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
|
||||||
|
if (ObjectUtil.isNull(object)) {
|
||||||
|
return new WriteCellData<>("");
|
||||||
|
}
|
||||||
|
Map<Object, String> enumValueMap = beforeConvert(contentProperty);
|
||||||
|
String value = Convert.toStr(enumValueMap.get(object), "");
|
||||||
|
return new WriteCellData<>(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<Object, String> beforeConvert(ExcelContentProperty contentProperty) {
|
||||||
|
ExcelEnumFormat anno = getAnnotation(contentProperty.getField());
|
||||||
|
Map<Object, String> enumValueMap = new HashMap<>();
|
||||||
|
Enum<?>[] enumConstants = anno.enumClass().getEnumConstants();
|
||||||
|
for (Enum<?> enumConstant : enumConstants) {
|
||||||
|
Object codeValue = ReflectUtils.invokeGetter(enumConstant, anno.codeField());
|
||||||
|
String textValue = ReflectUtils.invokeGetter(enumConstant, anno.textField());
|
||||||
|
enumValueMap.put(codeValue, textValue);
|
||||||
|
}
|
||||||
|
return enumValueMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExcelEnumFormat getAnnotation(Field field) {
|
||||||
|
return AnnotationUtil.getAnnotation(field, ExcelEnumFormat.class);
|
||||||
|
}
|
||||||
|
}
|
@ -87,8 +87,8 @@ public class PageQuery implements Serializable {
|
|||||||
// 兼容前端排序类型
|
// 兼容前端排序类型
|
||||||
isAsc = StringUtils.replaceEach(isAsc, new String[]{"ascending", "descending"}, new String[]{"asc", "desc"});
|
isAsc = StringUtils.replaceEach(isAsc, new String[]{"ascending", "descending"}, new String[]{"asc", "desc"});
|
||||||
|
|
||||||
String[] orderByArr = orderBy.split(",");
|
String[] orderByArr = orderBy.split(StringUtils.SEPARATOR);
|
||||||
String[] isAscArr = isAsc.split(",");
|
String[] isAscArr = isAsc.split(StringUtils.SEPARATOR);
|
||||||
if (isAscArr.length != 1 && isAscArr.length != orderByArr.length) {
|
if (isAscArr.length != 1 && isAscArr.length != orderByArr.length) {
|
||||||
throw new ServiceException("排序参数有误");
|
throw new ServiceException("排序参数有误");
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package com.ruoyi.common.core.domain.dto;
|
package com.ruoyi.common.core.domain.dto;
|
||||||
|
|
||||||
import lombok.Builder;
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ public class SysDept extends TreeEntity<SysDept> {
|
|||||||
* 部门名称
|
* 部门名称
|
||||||
*/
|
*/
|
||||||
@NotBlank(message = "部门名称不能为空")
|
@NotBlank(message = "部门名称不能为空")
|
||||||
@Size(min = 0, max = 30, message = "部门名称长度不能超过30个字符")
|
@Size(min = 0, max = 30, message = "部门名称长度不能超过{max}个字符")
|
||||||
private String deptName;
|
private String deptName;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -51,14 +51,14 @@ public class SysDept extends TreeEntity<SysDept> {
|
|||||||
/**
|
/**
|
||||||
* 联系电话
|
* 联系电话
|
||||||
*/
|
*/
|
||||||
@Size(min = 0, max = 11, message = "联系电话长度不能超过11个字符")
|
@Size(min = 0, max = 11, message = "联系电话长度不能超过{max}个字符")
|
||||||
private String phone;
|
private String phone;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 邮箱
|
* 邮箱
|
||||||
*/
|
*/
|
||||||
@Email(message = "邮箱格式不正确")
|
@Email(message = "邮箱格式不正确")
|
||||||
@Size(min = 0, max = 50, message = "邮箱长度不能超过50个字符")
|
@Size(min = 0, max = 50, message = "邮箱长度不能超过{max}个字符")
|
||||||
private String email;
|
private String email;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -44,7 +44,7 @@ public class SysDictData extends BaseEntity {
|
|||||||
*/
|
*/
|
||||||
@ExcelProperty(value = "字典标签")
|
@ExcelProperty(value = "字典标签")
|
||||||
@NotBlank(message = "字典标签不能为空")
|
@NotBlank(message = "字典标签不能为空")
|
||||||
@Size(min = 0, max = 100, message = "字典标签长度不能超过100个字符")
|
@Size(min = 0, max = 100, message = "字典标签长度不能超过{max}个字符")
|
||||||
private String dictLabel;
|
private String dictLabel;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -52,7 +52,7 @@ public class SysDictData extends BaseEntity {
|
|||||||
*/
|
*/
|
||||||
@ExcelProperty(value = "字典键值")
|
@ExcelProperty(value = "字典键值")
|
||||||
@NotBlank(message = "字典键值不能为空")
|
@NotBlank(message = "字典键值不能为空")
|
||||||
@Size(min = 0, max = 100, message = "字典键值长度不能超过100个字符")
|
@Size(min = 0, max = 100, message = "字典键值长度不能超过{max}个字符")
|
||||||
private String dictValue;
|
private String dictValue;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -60,13 +60,13 @@ public class SysDictData extends BaseEntity {
|
|||||||
*/
|
*/
|
||||||
@ExcelProperty(value = "字典类型")
|
@ExcelProperty(value = "字典类型")
|
||||||
@NotBlank(message = "字典类型不能为空")
|
@NotBlank(message = "字典类型不能为空")
|
||||||
@Size(min = 0, max = 100, message = "字典类型长度不能超过100个字符")
|
@Size(min = 0, max = 100, message = "字典类型长度不能超过{max}个字符")
|
||||||
private String dictType;
|
private String dictType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 样式属性(其他样式扩展)
|
* 样式属性(其他样式扩展)
|
||||||
*/
|
*/
|
||||||
@Size(min = 0, max = 100, message = "样式属性长度不能超过100个字符")
|
@Size(min = 0, max = 100, message = "样式属性长度不能超过{max}个字符")
|
||||||
private String cssClass;
|
private String cssClass;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -38,7 +38,7 @@ public class SysDictType extends BaseEntity {
|
|||||||
*/
|
*/
|
||||||
@ExcelProperty(value = "字典名称")
|
@ExcelProperty(value = "字典名称")
|
||||||
@NotBlank(message = "字典名称不能为空")
|
@NotBlank(message = "字典名称不能为空")
|
||||||
@Size(min = 0, max = 100, message = "字典类型名称长度不能超过100个字符")
|
@Size(min = 0, max = 100, message = "字典类型名称长度不能超过{max}个字符")
|
||||||
private String dictName;
|
private String dictName;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -46,7 +46,7 @@ public class SysDictType extends BaseEntity {
|
|||||||
*/
|
*/
|
||||||
@ExcelProperty(value = "字典类型")
|
@ExcelProperty(value = "字典类型")
|
||||||
@NotBlank(message = "字典类型不能为空")
|
@NotBlank(message = "字典类型不能为空")
|
||||||
@Size(min = 0, max = 100, message = "字典类型类型长度不能超过100个字符")
|
@Size(min = 0, max = 100, message = "字典类型类型长度不能超过{max}个字符")
|
||||||
@Pattern(regexp = "^[a-z][a-z0-9_]*$", message = "字典类型必须以字母开头,且只能为(小写字母,数字,下滑线)")
|
@Pattern(regexp = "^[a-z][a-z0-9_]*$", message = "字典类型必须以字母开头,且只能为(小写字母,数字,下滑线)")
|
||||||
private String dictType;
|
private String dictType;
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ public class SysMenu extends TreeEntity<SysMenu> {
|
|||||||
* 菜单名称
|
* 菜单名称
|
||||||
*/
|
*/
|
||||||
@NotBlank(message = "菜单名称不能为空")
|
@NotBlank(message = "菜单名称不能为空")
|
||||||
@Size(min = 0, max = 50, message = "菜单名称长度不能超过50个字符")
|
@Size(min = 0, max = 50, message = "菜单名称长度不能超过{max}个字符")
|
||||||
private String menuName;
|
private String menuName;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -44,13 +44,13 @@ public class SysMenu extends TreeEntity<SysMenu> {
|
|||||||
/**
|
/**
|
||||||
* 路由地址
|
* 路由地址
|
||||||
*/
|
*/
|
||||||
@Size(min = 0, max = 200, message = "路由地址不能超过200个字符")
|
@Size(min = 0, max = 200, message = "路由地址不能超过{max}个字符")
|
||||||
private String path;
|
private String path;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 组件路径
|
* 组件路径
|
||||||
*/
|
*/
|
||||||
@Size(min = 0, max = 200, message = "组件路径不能超过255个字符")
|
@Size(min = 0, max = 200, message = "组件路径不能超过{max}个字符")
|
||||||
private String component;
|
private String component;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -88,7 +88,7 @@ public class SysMenu extends TreeEntity<SysMenu> {
|
|||||||
* 权限字符串
|
* 权限字符串
|
||||||
*/
|
*/
|
||||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||||
@Size(min = 0, max = 100, message = "权限标识长度不能超过100个字符")
|
@Size(min = 0, max = 100, message = "权限标识长度不能超过{max}个字符")
|
||||||
private String perms;
|
private String perms;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -17,7 +17,6 @@ import lombok.NoArgsConstructor;
|
|||||||
import javax.validation.constraints.NotBlank;
|
import javax.validation.constraints.NotBlank;
|
||||||
import javax.validation.constraints.NotNull;
|
import javax.validation.constraints.NotNull;
|
||||||
import javax.validation.constraints.Size;
|
import javax.validation.constraints.Size;
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 角色表 sys_role
|
* 角色表 sys_role
|
||||||
@ -44,7 +43,7 @@ public class SysRole extends BaseEntity {
|
|||||||
*/
|
*/
|
||||||
@ExcelProperty(value = "角色名称")
|
@ExcelProperty(value = "角色名称")
|
||||||
@NotBlank(message = "角色名称不能为空")
|
@NotBlank(message = "角色名称不能为空")
|
||||||
@Size(min = 0, max = 30, message = "角色名称长度不能超过30个字符")
|
@Size(min = 0, max = 30, message = "角色名称长度不能超过{max}个字符")
|
||||||
private String roleName;
|
private String roleName;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -52,7 +51,7 @@ public class SysRole extends BaseEntity {
|
|||||||
*/
|
*/
|
||||||
@ExcelProperty(value = "角色权限")
|
@ExcelProperty(value = "角色权限")
|
||||||
@NotBlank(message = "权限字符不能为空")
|
@NotBlank(message = "权限字符不能为空")
|
||||||
@Size(min = 0, max = 100, message = "权限字符长度不能超过100个字符")
|
@Size(min = 0, max = 100, message = "权限字符长度不能超过{max}个字符")
|
||||||
private String roleKey;
|
private String roleKey;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -115,12 +114,6 @@ public class SysRole extends BaseEntity {
|
|||||||
@TableField(exist = false)
|
@TableField(exist = false)
|
||||||
private Long[] deptIds;
|
private Long[] deptIds;
|
||||||
|
|
||||||
/**
|
|
||||||
* 角色菜单权限
|
|
||||||
*/
|
|
||||||
@TableField(exist = false)
|
|
||||||
private Set<String> permissions;
|
|
||||||
|
|
||||||
public SysRole(Long roleId) {
|
public SysRole(Long roleId) {
|
||||||
this.roleId = roleId;
|
this.roleId = roleId;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package com.ruoyi.common.core.domain.entity;
|
package com.ruoyi.common.core.domain.entity;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.annotation.*;
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
import com.ruoyi.common.annotation.Sensitive;
|
import com.ruoyi.common.annotation.Sensitive;
|
||||||
import com.ruoyi.common.constant.UserConstants;
|
import com.ruoyi.common.constant.UserConstants;
|
||||||
import com.ruoyi.common.core.domain.BaseEntity;
|
import com.ruoyi.common.core.domain.BaseEntity;
|
||||||
@ -44,14 +46,14 @@ public class SysUser extends BaseEntity {
|
|||||||
*/
|
*/
|
||||||
@Xss(message = "用户账号不能包含脚本字符")
|
@Xss(message = "用户账号不能包含脚本字符")
|
||||||
@NotBlank(message = "用户账号不能为空")
|
@NotBlank(message = "用户账号不能为空")
|
||||||
@Size(min = 0, max = 30, message = "用户账号长度不能超过30个字符")
|
@Size(min = 0, max = 30, message = "用户账号长度不能超过{max}个字符")
|
||||||
private String userName;
|
private String userName;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用户昵称
|
* 用户昵称
|
||||||
*/
|
*/
|
||||||
@Xss(message = "用户昵称不能包含脚本字符")
|
@Xss(message = "用户昵称不能包含脚本字符")
|
||||||
@Size(min = 0, max = 30, message = "用户昵称长度不能超过30个字符")
|
@Size(min = 0, max = 30, message = "用户昵称长度不能超过{max}个字符")
|
||||||
private String nickName;
|
private String nickName;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -64,7 +66,7 @@ public class SysUser extends BaseEntity {
|
|||||||
*/
|
*/
|
||||||
@Sensitive(strategy = SensitiveStrategy.EMAIL)
|
@Sensitive(strategy = SensitiveStrategy.EMAIL)
|
||||||
@Email(message = "邮箱格式不正确")
|
@Email(message = "邮箱格式不正确")
|
||||||
@Size(min = 0, max = 50, message = "邮箱长度不能超过50个字符")
|
@Size(min = 0, max = 50, message = "邮箱长度不能超过{max}个字符")
|
||||||
private String email;
|
private String email;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -93,6 +95,12 @@ public class SysUser extends BaseEntity {
|
|||||||
)
|
)
|
||||||
private String password;
|
private String password;
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
@JsonProperty
|
||||||
|
public String getPassword() {
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 帐号状态(0正常 1停用)
|
* 帐号状态(0正常 1停用)
|
||||||
*/
|
*/
|
||||||
|
@ -0,0 +1,44 @@
|
|||||||
|
package com.ruoyi.common.core.domain.event;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录事件
|
||||||
|
*
|
||||||
|
* @author Lion Li
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class LogininforEvent implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户账号
|
||||||
|
*/
|
||||||
|
private String username;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录状态 0成功 1失败
|
||||||
|
*/
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提示消息
|
||||||
|
*/
|
||||||
|
private String message;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求体
|
||||||
|
*/
|
||||||
|
private HttpServletRequest request;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 其他参数
|
||||||
|
*/
|
||||||
|
private Object[] args;
|
||||||
|
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package com.ruoyi.common.core.domain.dto;
|
package com.ruoyi.common.core.domain.event;
|
||||||
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
@ -6,13 +6,13 @@ import java.io.Serializable;
|
|||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 通用操作日志实体
|
* 操作日志事件
|
||||||
*
|
*
|
||||||
* @author Lion Li
|
* @author Lion Li
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
public class OperLogDTO implements Serializable {
|
public class OperLogEvent implements Serializable {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
@ -0,0 +1,30 @@
|
|||||||
|
package com.ruoyi.common.core.domain.model;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import javax.validation.constraints.Email;
|
||||||
|
import javax.validation.constraints.NotBlank;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 短信登录对象
|
||||||
|
*
|
||||||
|
* @author Lion Li
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class EmailLoginBody {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 邮箱
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "{user.email.not.blank}")
|
||||||
|
@Email(message = "{user.email.not.valid}")
|
||||||
|
private String email;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 邮箱code
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "{email.code.not.blank}")
|
||||||
|
private String emailCode;
|
||||||
|
|
||||||
|
}
|
@ -1,7 +1,6 @@
|
|||||||
package com.ruoyi.common.core.domain.model;
|
package com.ruoyi.common.core.domain.model;
|
||||||
|
|
||||||
import com.ruoyi.common.core.domain.dto.RoleDTO;
|
import com.ruoyi.common.core.domain.dto.RoleDTO;
|
||||||
import com.ruoyi.common.helper.LoginHelper;
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
@ -111,7 +110,7 @@ public class LoginUser implements Serializable {
|
|||||||
if (userId == null) {
|
if (userId == null) {
|
||||||
throw new IllegalArgumentException("用户ID不能为空");
|
throw new IllegalArgumentException("用户ID不能为空");
|
||||||
}
|
}
|
||||||
return userType + LoginHelper.JOIN_CODE + userId;
|
return userType + ":" + userId;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -14,13 +14,13 @@ import javax.validation.constraints.NotBlank;
|
|||||||
public class SmsLoginBody {
|
public class SmsLoginBody {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用户名
|
* 手机号
|
||||||
*/
|
*/
|
||||||
@NotBlank(message = "{user.phonenumber.not.blank}")
|
@NotBlank(message = "{user.phonenumber.not.blank}")
|
||||||
private String phonenumber;
|
private String phonenumber;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用户密码
|
* 短信code
|
||||||
*/
|
*/
|
||||||
@NotBlank(message = "{sms.code.not.blank}")
|
@NotBlank(message = "{sms.code.not.blank}")
|
||||||
private String smsCode;
|
private String smsCode;
|
||||||
|
@ -4,16 +4,12 @@ import cn.hutool.core.collection.CollUtil;
|
|||||||
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
import com.baomidou.mybatisplus.core.enums.SqlMethod;
|
|
||||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
import com.baomidou.mybatisplus.core.metadata.TableInfo;
|
|
||||||
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
|
|
||||||
import com.baomidou.mybatisplus.core.toolkit.*;
|
import com.baomidou.mybatisplus.core.toolkit.*;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
|
import com.baomidou.mybatisplus.extension.toolkit.Db;
|
||||||
import com.ruoyi.common.utils.BeanCopyUtils;
|
import com.ruoyi.common.utils.BeanCopyUtils;
|
||||||
import org.apache.ibatis.binding.MapperMethod;
|
|
||||||
import org.apache.ibatis.logging.Log;
|
import org.apache.ibatis.logging.Log;
|
||||||
import org.apache.ibatis.logging.LogFactory;
|
import org.apache.ibatis.logging.LogFactory;
|
||||||
|
|
||||||
@ -21,7 +17,6 @@ import java.io.Serializable;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 自定义 Mapper 接口, 实现 自定义扩展
|
* 自定义 Mapper 接口, 实现 自定义扩展
|
||||||
@ -37,8 +32,6 @@ public interface BaseMapperPlus<M, T, V> extends BaseMapper<T> {
|
|||||||
|
|
||||||
Log log = LogFactory.getLog(BaseMapperPlus.class);
|
Log log = LogFactory.getLog(BaseMapperPlus.class);
|
||||||
|
|
||||||
int DEFAULT_BATCH_SIZE = 1000;
|
|
||||||
|
|
||||||
default Class<V> currentVoClass() {
|
default Class<V> currentVoClass() {
|
||||||
return (Class<V>) ReflectionKit.getSuperClassGenericType(this.getClass(), BaseMapperPlus.class, 2);
|
return (Class<V>) ReflectionKit.getSuperClassGenericType(this.getClass(), BaseMapperPlus.class, 2);
|
||||||
}
|
}
|
||||||
@ -59,79 +52,49 @@ public interface BaseMapperPlus<M, T, V> extends BaseMapper<T> {
|
|||||||
* 批量插入
|
* 批量插入
|
||||||
*/
|
*/
|
||||||
default boolean insertBatch(Collection<T> entityList) {
|
default boolean insertBatch(Collection<T> entityList) {
|
||||||
return insertBatch(entityList, DEFAULT_BATCH_SIZE);
|
return Db.saveBatch(entityList);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 批量更新
|
* 批量更新
|
||||||
*/
|
*/
|
||||||
default boolean updateBatchById(Collection<T> entityList) {
|
default boolean updateBatchById(Collection<T> entityList) {
|
||||||
return updateBatchById(entityList, DEFAULT_BATCH_SIZE);
|
return Db.updateBatchById(entityList);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 批量插入或更新
|
* 批量插入或更新
|
||||||
*/
|
*/
|
||||||
default boolean insertOrUpdateBatch(Collection<T> entityList) {
|
default boolean insertOrUpdateBatch(Collection<T> entityList) {
|
||||||
return insertOrUpdateBatch(entityList, DEFAULT_BATCH_SIZE);
|
return Db.saveOrUpdateBatch(entityList);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 批量插入(包含限制条数)
|
* 批量插入(包含限制条数)
|
||||||
*/
|
*/
|
||||||
default boolean insertBatch(Collection<T> entityList, int batchSize) {
|
default boolean insertBatch(Collection<T> entityList, int batchSize) {
|
||||||
String sqlStatement = SqlHelper.getSqlStatement(this.currentMapperClass(), SqlMethod.INSERT_ONE);
|
return Db.saveBatch(entityList, batchSize);
|
||||||
return SqlHelper.executeBatch(this.currentModelClass(), log, entityList, batchSize,
|
|
||||||
(sqlSession, entity) -> sqlSession.insert(sqlStatement, entity));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 批量更新(包含限制条数)
|
* 批量更新(包含限制条数)
|
||||||
*/
|
*/
|
||||||
default boolean updateBatchById(Collection<T> entityList, int batchSize) {
|
default boolean updateBatchById(Collection<T> entityList, int batchSize) {
|
||||||
String sqlStatement = SqlHelper.getSqlStatement(this.currentMapperClass(), SqlMethod.UPDATE_BY_ID);
|
return Db.updateBatchById(entityList, batchSize);
|
||||||
return SqlHelper.executeBatch(this.currentModelClass(), log, entityList, batchSize,
|
|
||||||
(sqlSession, entity) -> {
|
|
||||||
MapperMethod.ParamMap<T> param = new MapperMethod.ParamMap<>();
|
|
||||||
param.put(Constants.ENTITY, entity);
|
|
||||||
sqlSession.update(sqlStatement, param);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 批量插入或更新(包含限制条数)
|
* 批量插入或更新(包含限制条数)
|
||||||
*/
|
*/
|
||||||
default boolean insertOrUpdateBatch(Collection<T> entityList, int batchSize) {
|
default boolean insertOrUpdateBatch(Collection<T> entityList, int batchSize) {
|
||||||
TableInfo tableInfo = TableInfoHelper.getTableInfo(this.currentModelClass());
|
return Db.saveOrUpdateBatch(entityList, batchSize);
|
||||||
Assert.notNull(tableInfo, "error: can not execute. because can not find cache of TableInfo for entity!");
|
|
||||||
String keyProperty = tableInfo.getKeyProperty();
|
|
||||||
Assert.notEmpty(keyProperty, "error: can not execute. because can not find column for id from entity!");
|
|
||||||
return SqlHelper.saveOrUpdateBatch(this.currentModelClass(), this.currentMapperClass(), log, entityList, batchSize, (sqlSession, entity) -> {
|
|
||||||
Object idVal = tableInfo.getPropertyValue(entity, keyProperty);
|
|
||||||
String sqlStatement = SqlHelper.getSqlStatement(this.currentMapperClass(), SqlMethod.SELECT_BY_ID);
|
|
||||||
return StringUtils.checkValNull(idVal)
|
|
||||||
|| CollectionUtils.isEmpty(sqlSession.selectList(sqlStatement, entity));
|
|
||||||
}, (sqlSession, entity) -> {
|
|
||||||
MapperMethod.ParamMap<T> param = new MapperMethod.ParamMap<>();
|
|
||||||
param.put(Constants.ENTITY, entity);
|
|
||||||
String sqlStatement = SqlHelper.getSqlStatement(this.currentMapperClass(), SqlMethod.UPDATE_BY_ID);
|
|
||||||
sqlSession.update(sqlStatement, param);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 插入或更新(包含限制条数)
|
* 插入或更新(包含限制条数)
|
||||||
*/
|
*/
|
||||||
default boolean insertOrUpdate(T entity) {
|
default boolean insertOrUpdate(T entity) {
|
||||||
if (null != entity) {
|
return Db.saveOrUpdate(entity);
|
||||||
TableInfo tableInfo = TableInfoHelper.getTableInfo(this.currentModelClass());
|
|
||||||
Assert.notNull(tableInfo, "error: can not execute. because can not find cache of TableInfo for entity!");
|
|
||||||
String keyProperty = tableInfo.getKeyProperty();
|
|
||||||
Assert.notEmpty(keyProperty, "error: can not execute. because can not find column for id from entity!");
|
|
||||||
Object idVal = tableInfo.getPropertyValue(entity, tableInfo.getKeyProperty());
|
|
||||||
return StringUtils.checkValNull(idVal) || Objects.isNull(selectById((Serializable) idVal)) ? insert(entity) > 0 : updateById(entity) > 0;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
default V selectVoById(Serializable id) {
|
default V selectVoById(Serializable id) {
|
||||||
|
@ -0,0 +1,18 @@
|
|||||||
|
package com.ruoyi.common.core.service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通用 部门服务
|
||||||
|
*
|
||||||
|
* @author Lion Li
|
||||||
|
*/
|
||||||
|
public interface DeptService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过部门ID查询部门名称
|
||||||
|
*
|
||||||
|
* @param deptIds 部门ID串逗号分隔
|
||||||
|
* @return 部门名称串逗号分隔
|
||||||
|
*/
|
||||||
|
String selectDeptNameByIds(String deptIds);
|
||||||
|
|
||||||
|
}
|
@ -1,14 +0,0 @@
|
|||||||
package com.ruoyi.common.core.service;
|
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 通用 系统访问日志
|
|
||||||
*
|
|
||||||
* @author Lion Li
|
|
||||||
*/
|
|
||||||
public interface LogininforService {
|
|
||||||
|
|
||||||
void recordLogininfor(String username, String status, String message,
|
|
||||||
HttpServletRequest request, Object... args);
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
package com.ruoyi.common.core.service;
|
|
||||||
|
|
||||||
import com.ruoyi.common.core.domain.dto.OperLogDTO;
|
|
||||||
import org.springframework.scheduling.annotation.Async;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 通用 操作日志
|
|
||||||
*
|
|
||||||
* @author Lion Li
|
|
||||||
*/
|
|
||||||
public interface OperLogService {
|
|
||||||
|
|
||||||
@Async
|
|
||||||
void recordOper(OperLogDTO operLogDTO);
|
|
||||||
}
|
|
@ -0,0 +1,18 @@
|
|||||||
|
package com.ruoyi.common.core.service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通用 OSS服务
|
||||||
|
*
|
||||||
|
* @author Lion Li
|
||||||
|
*/
|
||||||
|
public interface OssService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过ossId查询对应的url
|
||||||
|
*
|
||||||
|
* @param ossIds ossId串逗号分隔
|
||||||
|
* @return url串逗号分隔
|
||||||
|
*/
|
||||||
|
String selectUrlByIds(String ossIds);
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
package com.ruoyi.common.core.service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通用 用户服务
|
||||||
|
*
|
||||||
|
* @author Lion Li
|
||||||
|
*/
|
||||||
|
public interface UserService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过用户ID查询用户账户
|
||||||
|
*
|
||||||
|
* @param userId 用户ID
|
||||||
|
* @return 用户账户
|
||||||
|
*/
|
||||||
|
String selectUserNameById(Long userId);
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
package com.ruoyi.common.encrypt;
|
||||||
|
|
||||||
|
import com.ruoyi.common.enums.AlgorithmType;
|
||||||
|
import com.ruoyi.common.enums.EncodeType;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加密上下文 用于encryptor传递必要的参数。
|
||||||
|
*
|
||||||
|
* @author 老马
|
||||||
|
* @version 4.6.0
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class EncryptContext {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认算法
|
||||||
|
*/
|
||||||
|
private AlgorithmType algorithm;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 安全秘钥
|
||||||
|
*/
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 公钥
|
||||||
|
*/
|
||||||
|
private String publicKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 私钥
|
||||||
|
*/
|
||||||
|
private String privateKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 编码方式,base64/hex
|
||||||
|
*/
|
||||||
|
private EncodeType encode;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
package com.ruoyi.common.encrypt;
|
||||||
|
|
||||||
|
import com.ruoyi.common.enums.AlgorithmType;
|
||||||
|
import com.ruoyi.common.enums.EncodeType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加解者
|
||||||
|
*
|
||||||
|
* @author 老马
|
||||||
|
* @version 4.6.0
|
||||||
|
*/
|
||||||
|
public interface IEncryptor {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得当前算法
|
||||||
|
*/
|
||||||
|
AlgorithmType algorithm();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加密
|
||||||
|
*
|
||||||
|
* @param value 待加密字符串
|
||||||
|
* @param encodeType 加密后的编码格式
|
||||||
|
* @return 加密后的字符串
|
||||||
|
*/
|
||||||
|
String encrypt(String value, EncodeType encodeType);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解密
|
||||||
|
*
|
||||||
|
* @param value 待加密字符串
|
||||||
|
* @return 解密后的字符串
|
||||||
|
*/
|
||||||
|
String decrypt(String value);
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
package com.ruoyi.common.encrypt.encryptor;
|
||||||
|
|
||||||
|
import com.ruoyi.common.encrypt.EncryptContext;
|
||||||
|
import com.ruoyi.common.encrypt.IEncryptor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 所有加密执行者的基类
|
||||||
|
*
|
||||||
|
* @author 老马
|
||||||
|
* @version 4.6.0
|
||||||
|
*/
|
||||||
|
public abstract class AbstractEncryptor implements IEncryptor {
|
||||||
|
|
||||||
|
public AbstractEncryptor(EncryptContext context) {
|
||||||
|
// 用户配置校验与配置注入
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,69 @@
|
|||||||
|
package com.ruoyi.common.encrypt.encryptor;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.ArrayUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import cn.hutool.crypto.SecureUtil;
|
||||||
|
import cn.hutool.crypto.symmetric.AES;
|
||||||
|
import com.ruoyi.common.encrypt.EncryptContext;
|
||||||
|
import com.ruoyi.common.enums.AlgorithmType;
|
||||||
|
import com.ruoyi.common.enums.EncodeType;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AES算法实现
|
||||||
|
*
|
||||||
|
* @author 老马
|
||||||
|
* @version 4.6.0
|
||||||
|
*/
|
||||||
|
public class AesEncryptor extends AbstractEncryptor {
|
||||||
|
|
||||||
|
private final AES aes;
|
||||||
|
|
||||||
|
public AesEncryptor(EncryptContext context) {
|
||||||
|
super(context);
|
||||||
|
String password = context.getPassword();
|
||||||
|
if (StrUtil.isBlank(password)) {
|
||||||
|
throw new IllegalArgumentException("AES没有获得秘钥信息");
|
||||||
|
}
|
||||||
|
// aes算法的秘钥要求是16位、24位、32位
|
||||||
|
int[] array = {16, 24, 32};
|
||||||
|
if (!ArrayUtil.contains(array, password.length())) {
|
||||||
|
throw new IllegalArgumentException("AES秘钥长度应该为16位、24位、32位,实际为" + password.length() + "位");
|
||||||
|
}
|
||||||
|
aes = SecureUtil.aes(context.getPassword().getBytes(StandardCharsets.UTF_8));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得当前算法
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public AlgorithmType algorithm() {
|
||||||
|
return AlgorithmType.AES;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加密
|
||||||
|
*
|
||||||
|
* @param value 待加密字符串
|
||||||
|
* @param encodeType 加密后的编码格式
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String encrypt(String value, EncodeType encodeType) {
|
||||||
|
if (encodeType == EncodeType.HEX) {
|
||||||
|
return aes.encryptHex(value);
|
||||||
|
} else {
|
||||||
|
return aes.encryptBase64(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解密
|
||||||
|
*
|
||||||
|
* @param value 待加密字符串
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String decrypt(String value) {
|
||||||
|
return this.aes.decryptStr(value);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
package com.ruoyi.common.encrypt.encryptor;
|
||||||
|
|
||||||
|
import cn.hutool.core.codec.Base64;
|
||||||
|
import com.ruoyi.common.encrypt.EncryptContext;
|
||||||
|
import com.ruoyi.common.enums.AlgorithmType;
|
||||||
|
import com.ruoyi.common.enums.EncodeType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base64算法实现
|
||||||
|
*
|
||||||
|
* @author 老马
|
||||||
|
* @version 4.6.0
|
||||||
|
*/
|
||||||
|
public class Base64Encryptor extends AbstractEncryptor {
|
||||||
|
|
||||||
|
public Base64Encryptor(EncryptContext context) {
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得当前算法
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public AlgorithmType algorithm() {
|
||||||
|
return AlgorithmType.BASE64;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加密
|
||||||
|
*
|
||||||
|
* @param value 待加密字符串
|
||||||
|
* @param encodeType 加密后的编码格式
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String encrypt(String value, EncodeType encodeType) {
|
||||||
|
return Base64.encode(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解密
|
||||||
|
*
|
||||||
|
* @param value 待加密字符串
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String decrypt(String value) {
|
||||||
|
return Base64.decodeStr(value);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,65 @@
|
|||||||
|
package com.ruoyi.common.encrypt.encryptor;
|
||||||
|
|
||||||
|
import cn.hutool.core.codec.Base64;
|
||||||
|
import cn.hutool.crypto.SecureUtil;
|
||||||
|
import cn.hutool.crypto.asymmetric.KeyType;
|
||||||
|
import cn.hutool.crypto.asymmetric.RSA;
|
||||||
|
import com.ruoyi.common.encrypt.EncryptContext;
|
||||||
|
import com.ruoyi.common.enums.AlgorithmType;
|
||||||
|
import com.ruoyi.common.enums.EncodeType;
|
||||||
|
import com.ruoyi.common.utils.StringUtils;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RSA算法实现
|
||||||
|
*
|
||||||
|
* @author 老马
|
||||||
|
* @version 4.6.0
|
||||||
|
*/
|
||||||
|
public class RsaEncryptor extends AbstractEncryptor {
|
||||||
|
|
||||||
|
private final RSA rsa;
|
||||||
|
|
||||||
|
public RsaEncryptor(EncryptContext context) {
|
||||||
|
super(context);
|
||||||
|
String privateKey = context.getPrivateKey();
|
||||||
|
String publicKey = context.getPublicKey();
|
||||||
|
if (StringUtils.isAnyEmpty(privateKey, publicKey)) {
|
||||||
|
throw new IllegalArgumentException("RSA公私钥均需要提供,公钥加密,私钥解密。");
|
||||||
|
}
|
||||||
|
this.rsa = SecureUtil.rsa(Base64.decode(privateKey), Base64.decode(publicKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得当前算法
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public AlgorithmType algorithm() {
|
||||||
|
return AlgorithmType.RSA;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加密
|
||||||
|
*
|
||||||
|
* @param value 待加密字符串
|
||||||
|
* @param encodeType 加密后的编码格式
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String encrypt(String value, EncodeType encodeType) {
|
||||||
|
if (encodeType == EncodeType.HEX) {
|
||||||
|
return rsa.encryptHex(value, KeyType.PublicKey);
|
||||||
|
} else {
|
||||||
|
return rsa.encryptBase64(value, KeyType.PublicKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解密
|
||||||
|
*
|
||||||
|
* @param value 待加密字符串
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String decrypt(String value) {
|
||||||
|
return this.rsa.decryptStr(value, KeyType.PrivateKey);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,65 @@
|
|||||||
|
package com.ruoyi.common.encrypt.encryptor;
|
||||||
|
|
||||||
|
|
||||||
|
import cn.hutool.core.codec.Base64;
|
||||||
|
import cn.hutool.crypto.SmUtil;
|
||||||
|
import cn.hutool.crypto.asymmetric.KeyType;
|
||||||
|
import cn.hutool.crypto.asymmetric.SM2;
|
||||||
|
import com.ruoyi.common.encrypt.EncryptContext;
|
||||||
|
import com.ruoyi.common.enums.AlgorithmType;
|
||||||
|
import com.ruoyi.common.enums.EncodeType;
|
||||||
|
import com.ruoyi.common.utils.StringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sm2算法实现
|
||||||
|
*
|
||||||
|
* @author 老马
|
||||||
|
* @version 4.6.0
|
||||||
|
*/
|
||||||
|
public class Sm2Encryptor extends AbstractEncryptor {
|
||||||
|
|
||||||
|
private final SM2 sm2;
|
||||||
|
|
||||||
|
public Sm2Encryptor(EncryptContext context) {
|
||||||
|
super(context);
|
||||||
|
String privateKey = context.getPrivateKey();
|
||||||
|
String publicKey = context.getPublicKey();
|
||||||
|
if (StringUtils.isAnyEmpty(privateKey, publicKey)) {
|
||||||
|
throw new IllegalArgumentException("SM2公私钥均需要提供,公钥加密,私钥解密。");
|
||||||
|
}
|
||||||
|
this.sm2 = SmUtil.sm2(Base64.decode(privateKey), Base64.decode(publicKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得当前算法
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public AlgorithmType algorithm() {
|
||||||
|
return AlgorithmType.SM2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加密
|
||||||
|
*
|
||||||
|
* @param value 待加密字符串
|
||||||
|
* @param encodeType 加密后的编码格式
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String encrypt(String value, EncodeType encodeType) {
|
||||||
|
if (encodeType == EncodeType.HEX) {
|
||||||
|
return sm2.encryptHex(value, KeyType.PublicKey);
|
||||||
|
} else {
|
||||||
|
return sm2.encryptBase64(value, KeyType.PublicKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解密
|
||||||
|
*
|
||||||
|
* @param value 待加密字符串
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String decrypt(String value) {
|
||||||
|
return this.sm2.decryptStr(value, KeyType.PrivateKey);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,67 @@
|
|||||||
|
package com.ruoyi.common.encrypt.encryptor;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import cn.hutool.crypto.SmUtil;
|
||||||
|
import cn.hutool.crypto.symmetric.SM4;
|
||||||
|
import com.ruoyi.common.encrypt.EncryptContext;
|
||||||
|
import com.ruoyi.common.enums.AlgorithmType;
|
||||||
|
import com.ruoyi.common.enums.EncodeType;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sm4算法实现
|
||||||
|
*
|
||||||
|
* @author 老马
|
||||||
|
* @version 4.6.0
|
||||||
|
*/
|
||||||
|
public class Sm4Encryptor extends AbstractEncryptor {
|
||||||
|
|
||||||
|
private final SM4 sm4;
|
||||||
|
|
||||||
|
public Sm4Encryptor(EncryptContext context) {
|
||||||
|
super(context);
|
||||||
|
String password = context.getPassword();
|
||||||
|
if (StrUtil.isBlank(password)) {
|
||||||
|
throw new IllegalArgumentException("SM4没有获得秘钥信息");
|
||||||
|
}
|
||||||
|
// sm4算法的秘钥要求是16位长度
|
||||||
|
if (16 != password.length()) {
|
||||||
|
throw new IllegalArgumentException("SM4秘钥长度应该为16位,实际为" + password.length() + "位");
|
||||||
|
}
|
||||||
|
this.sm4 = SmUtil.sm4(password.getBytes(StandardCharsets.UTF_8));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得当前算法
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public AlgorithmType algorithm() {
|
||||||
|
return AlgorithmType.SM4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加密
|
||||||
|
*
|
||||||
|
* @param value 待加密字符串
|
||||||
|
* @param encodeType 加密后的编码格式
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String encrypt(String value, EncodeType encodeType) {
|
||||||
|
if (encodeType == EncodeType.HEX) {
|
||||||
|
return sm4.encryptHex(value);
|
||||||
|
} else {
|
||||||
|
return sm4.encryptBase64(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解密
|
||||||
|
*
|
||||||
|
* @param value 待加密字符串
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String decrypt(String value) {
|
||||||
|
return this.sm4.decryptStr(value);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
package com.ruoyi.common.enums;
|
||||||
|
|
||||||
|
import com.ruoyi.common.encrypt.encryptor.*;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 算法名称
|
||||||
|
*
|
||||||
|
* @author 老马
|
||||||
|
* @version 4.6.0
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public enum AlgorithmType {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认走yml配置
|
||||||
|
*/
|
||||||
|
DEFAULT(null),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* base64
|
||||||
|
*/
|
||||||
|
BASE64(Base64Encryptor.class),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* aes
|
||||||
|
*/
|
||||||
|
AES(AesEncryptor.class),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* rsa
|
||||||
|
*/
|
||||||
|
RSA(RsaEncryptor.class),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sm2
|
||||||
|
*/
|
||||||
|
SM2(Sm2Encryptor.class),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sm4
|
||||||
|
*/
|
||||||
|
SM4(Sm4Encryptor.class);
|
||||||
|
|
||||||
|
private final Class<? extends AbstractEncryptor> clazz;
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package com.ruoyi.common.enums;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 编码类型
|
||||||
|
*
|
||||||
|
* @author 老马
|
||||||
|
* @version 4.6.0
|
||||||
|
*/
|
||||||
|
public enum EncodeType {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认使用yml配置
|
||||||
|
*/
|
||||||
|
DEFAULT,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* base64编码
|
||||||
|
*/
|
||||||
|
BASE64,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 16进制编码
|
||||||
|
*/
|
||||||
|
HEX;
|
||||||
|
|
||||||
|
}
|
@ -22,6 +22,11 @@ public enum LoginType {
|
|||||||
*/
|
*/
|
||||||
SMS("sms.code.retry.limit.exceed", "sms.code.retry.limit.count"),
|
SMS("sms.code.retry.limit.exceed", "sms.code.retry.limit.count"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 邮箱登录
|
||||||
|
*/
|
||||||
|
EMAIL("email.code.retry.limit.exceed", "email.code.retry.limit.count"),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 小程序登录
|
* 小程序登录
|
||||||
*/
|
*/
|
||||||
|
@ -86,7 +86,7 @@ public class CellMergeStrategy extends AbstractMergeStrategy {
|
|||||||
// 空值跳过不合并
|
// 空值跳过不合并
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (cellValue != val) {
|
if (!cellValue.equals(val)) {
|
||||||
if (i - repeatCell.getCurrent() > 1) {
|
if (i - repeatCell.getCurrent() > 1) {
|
||||||
cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex - 1, colNum, colNum));
|
cellList.add(new CellRangeAddress(repeatCell.getCurrent() + rowIndex, i + rowIndex - 1, colNum, colNum));
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,7 @@ public class DefaultExcelListener<T> extends AnalysisEventListener<T> implements
|
|||||||
private ExcelResult<T> excelResult;
|
private ExcelResult<T> excelResult;
|
||||||
|
|
||||||
public DefaultExcelListener(boolean isValidate) {
|
public DefaultExcelListener(boolean isValidate) {
|
||||||
this.excelResult = new DefautExcelResult<>();
|
this.excelResult = new DefaultExcelResult<>();
|
||||||
this.isValidate = isValidate;
|
this.isValidate = isValidate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ import java.util.List;
|
|||||||
* @author Yjoioooo
|
* @author Yjoioooo
|
||||||
* @author Lion Li
|
* @author Lion Li
|
||||||
*/
|
*/
|
||||||
public class DefautExcelResult<T> implements ExcelResult<T> {
|
public class DefaultExcelResult<T> implements ExcelResult<T> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 数据对象list
|
* 数据对象list
|
||||||
@ -26,17 +26,17 @@ public class DefautExcelResult<T> implements ExcelResult<T> {
|
|||||||
@Setter
|
@Setter
|
||||||
private List<String> errorList;
|
private List<String> errorList;
|
||||||
|
|
||||||
public DefautExcelResult() {
|
public DefaultExcelResult() {
|
||||||
this.list = new ArrayList<>();
|
this.list = new ArrayList<>();
|
||||||
this.errorList = new ArrayList<>();
|
this.errorList = new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public DefautExcelResult(List<T> list, List<String> errorList) {
|
public DefaultExcelResult(List<T> list, List<String> errorList) {
|
||||||
this.list = list;
|
this.list = list;
|
||||||
this.errorList = errorList;
|
this.errorList = errorList;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DefautExcelResult(ExcelResult<T> excelResult) {
|
public DefaultExcelResult(ExcelResult<T> excelResult) {
|
||||||
this.list = excelResult.getList();
|
this.list = excelResult.getList();
|
||||||
this.errorList = excelResult.getErrorList();
|
this.errorList = excelResult.getErrorList();
|
||||||
}
|
}
|
@ -25,7 +25,7 @@ public class XssFilter implements Filter {
|
|||||||
public void init(FilterConfig filterConfig) throws ServletException {
|
public void init(FilterConfig filterConfig) throws ServletException {
|
||||||
String tempExcludes = filterConfig.getInitParameter("excludes");
|
String tempExcludes = filterConfig.getInitParameter("excludes");
|
||||||
if (StringUtils.isNotEmpty(tempExcludes)) {
|
if (StringUtils.isNotEmpty(tempExcludes)) {
|
||||||
String[] url = tempExcludes.split(",");
|
String[] url = tempExcludes.split(StringUtils.SEPARATOR);
|
||||||
for (int i = 0; url != null && i < url.length; i++) {
|
for (int i = 0; url != null && i < url.length; i++) {
|
||||||
excludes.add(url[i]);
|
excludes.add(url[i]);
|
||||||
}
|
}
|
||||||
|
@ -33,12 +33,12 @@ public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
|
|||||||
String[] values = super.getParameterValues(name);
|
String[] values = super.getParameterValues(name);
|
||||||
if (values != null) {
|
if (values != null) {
|
||||||
int length = values.length;
|
int length = values.length;
|
||||||
String[] escapseValues = new String[length];
|
String[] escapesValues = new String[length];
|
||||||
for (int i = 0; i < length; i++) {
|
for (int i = 0; i < length; i++) {
|
||||||
// 防xss攻击和过滤前后空格
|
// 防xss攻击和过滤前后空格
|
||||||
escapseValues[i] = HtmlUtil.cleanHtmlTag(values[i]).trim();
|
escapesValues[i] = HtmlUtil.cleanHtmlTag(values[i]).trim();
|
||||||
}
|
}
|
||||||
return escapseValues;
|
return escapesValues;
|
||||||
}
|
}
|
||||||
return super.getParameterValues(name);
|
return super.getParameterValues(name);
|
||||||
}
|
}
|
||||||
|
@ -66,7 +66,7 @@ public class DataBaseHelper {
|
|||||||
// instr(',0,100,101,' , ',100,') <> 0
|
// instr(',0,100,101,' , ',100,') <> 0
|
||||||
return "instr(','||" + var2 + "||',' , '," + var + ",') <> 0";
|
return "instr(','||" + var2 + "||',' , '," + var + ",') <> 0";
|
||||||
}
|
}
|
||||||
// find_in_set(100 , '0,100,101')
|
// find_in_set('100' , '0,100,101')
|
||||||
return "find_in_set(" + var + " , " + var2 + ") <> 0";
|
return "find_in_set('" + var + "' , " + var2 + ") <> 0";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,11 +3,14 @@ package com.ruoyi.common.helper;
|
|||||||
import cn.dev33.satoken.context.SaHolder;
|
import cn.dev33.satoken.context.SaHolder;
|
||||||
import cn.dev33.satoken.context.model.SaStorage;
|
import cn.dev33.satoken.context.model.SaStorage;
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
|
import com.baomidou.mybatisplus.core.plugins.IgnoreStrategy;
|
||||||
|
import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper;
|
||||||
import lombok.AccessLevel;
|
import lombok.AccessLevel;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 数据权限助手
|
* 数据权限助手
|
||||||
@ -44,4 +47,47 @@ public class DataPermissionHelper {
|
|||||||
}
|
}
|
||||||
throw new NullPointerException("data permission context type exception");
|
throw new NullPointerException("data permission context type exception");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 开启忽略数据权限(开启后需手动调用 {@link #disableIgnore()} 关闭)
|
||||||
|
*/
|
||||||
|
public static void enableIgnore() {
|
||||||
|
InterceptorIgnoreHelper.handle(IgnoreStrategy.builder().dataPermission(true).build());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关闭忽略数据权限
|
||||||
|
*/
|
||||||
|
public static void disableIgnore() {
|
||||||
|
InterceptorIgnoreHelper.clearIgnoreStrategy();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在忽略数据权限中执行
|
||||||
|
*
|
||||||
|
* @param handle 处理执行方法
|
||||||
|
*/
|
||||||
|
public static void ignore(Runnable handle) {
|
||||||
|
enableIgnore();
|
||||||
|
try {
|
||||||
|
handle.run();
|
||||||
|
} finally {
|
||||||
|
disableIgnore();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在忽略数据权限中执行
|
||||||
|
*
|
||||||
|
* @param handle 处理执行方法
|
||||||
|
*/
|
||||||
|
public static <T> T ignore(Supplier<T> handle) {
|
||||||
|
enableIgnore();
|
||||||
|
try {
|
||||||
|
return handle.get();
|
||||||
|
} finally {
|
||||||
|
disableIgnore();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,25 +1,25 @@
|
|||||||
package com.ruoyi.common.helper;
|
package com.ruoyi.common.helper;
|
||||||
|
|
||||||
import cn.dev33.satoken.context.SaHolder;
|
import cn.dev33.satoken.context.SaHolder;
|
||||||
|
import cn.dev33.satoken.context.model.SaStorage;
|
||||||
|
import cn.dev33.satoken.stp.SaLoginModel;
|
||||||
import cn.dev33.satoken.stp.StpUtil;
|
import cn.dev33.satoken.stp.StpUtil;
|
||||||
import cn.hutool.core.util.ArrayUtil;
|
import cn.hutool.core.convert.Convert;
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
import com.ruoyi.common.constant.UserConstants;
|
import com.ruoyi.common.constant.UserConstants;
|
||||||
import com.ruoyi.common.core.domain.model.LoginUser;
|
import com.ruoyi.common.core.domain.model.LoginUser;
|
||||||
import com.ruoyi.common.enums.DeviceType;
|
import com.ruoyi.common.enums.DeviceType;
|
||||||
import com.ruoyi.common.enums.UserType;
|
import com.ruoyi.common.enums.UserType;
|
||||||
import com.ruoyi.common.exception.UtilException;
|
|
||||||
import com.ruoyi.common.utils.StringUtils;
|
|
||||||
import lombok.AccessLevel;
|
import lombok.AccessLevel;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 登录鉴权助手
|
* 登录鉴权助手
|
||||||
*
|
* <p>
|
||||||
* user_type 为 用户类型 同一个用户表 可以有多种用户类型 例如 pc,app
|
* user_type 为 用户类型 同一个用户表 可以有多种用户类型 例如 pc,app
|
||||||
* deivce 为 设备类型 同一个用户类型 可以有 多种设备类型 例如 web,ios
|
* deivce 为 设备类型 同一个用户类型 可以有 多种设备类型 例如 web,ios
|
||||||
* 可以组成 用户类型与设备类型多对多的 权限灵活控制
|
* 可以组成 用户类型与设备类型多对多的 权限灵活控制
|
||||||
*
|
* <p>
|
||||||
* 多用户体系 针对 多种用户类型 但权限控制不一致
|
* 多用户体系 针对 多种用户类型 但权限控制不一致
|
||||||
* 可以组成 多用户类型表与多设备类型 分别控制权限
|
* 可以组成 多用户类型表与多设备类型 分别控制权限
|
||||||
*
|
*
|
||||||
@ -28,8 +28,8 @@ import lombok.NoArgsConstructor;
|
|||||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||||
public class LoginHelper {
|
public class LoginHelper {
|
||||||
|
|
||||||
public static final String JOIN_CODE = ":";
|
|
||||||
public static final String LOGIN_USER_KEY = "loginUser";
|
public static final String LOGIN_USER_KEY = "loginUser";
|
||||||
|
public static final String USER_KEY = "userId";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 登录系统
|
* 登录系统
|
||||||
@ -37,9 +37,7 @@ public class LoginHelper {
|
|||||||
* @param loginUser 登录用户信息
|
* @param loginUser 登录用户信息
|
||||||
*/
|
*/
|
||||||
public static void login(LoginUser loginUser) {
|
public static void login(LoginUser loginUser) {
|
||||||
SaHolder.getStorage().set(LOGIN_USER_KEY, loginUser);
|
loginByDevice(loginUser, null);
|
||||||
StpUtil.login(loginUser.getLoginId());
|
|
||||||
setLoginUser(loginUser);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -49,15 +47,14 @@ public class LoginHelper {
|
|||||||
* @param loginUser 登录用户信息
|
* @param loginUser 登录用户信息
|
||||||
*/
|
*/
|
||||||
public static void loginByDevice(LoginUser loginUser, DeviceType deviceType) {
|
public static void loginByDevice(LoginUser loginUser, DeviceType deviceType) {
|
||||||
SaHolder.getStorage().set(LOGIN_USER_KEY, loginUser);
|
SaStorage storage = SaHolder.getStorage();
|
||||||
StpUtil.login(loginUser.getLoginId(), deviceType.getDevice());
|
storage.set(LOGIN_USER_KEY, loginUser);
|
||||||
setLoginUser(loginUser);
|
storage.set(USER_KEY, loginUser.getUserId());
|
||||||
}
|
SaLoginModel model = new SaLoginModel();
|
||||||
|
if (ObjectUtil.isNotNull(deviceType)) {
|
||||||
/**
|
model.setDevice(deviceType.getDevice());
|
||||||
* 设置用户数据(多级缓存)
|
}
|
||||||
*/
|
StpUtil.login(loginUser.getLoginId(), model.setExtra(USER_KEY, loginUser.getUserId()));
|
||||||
public static void setLoginUser(LoginUser loginUser) {
|
|
||||||
StpUtil.getTokenSession().set(LOGIN_USER_KEY, loginUser);
|
StpUtil.getTokenSession().set(LOGIN_USER_KEY, loginUser);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,21 +71,28 @@ public class LoginHelper {
|
|||||||
return loginUser;
|
return loginUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用户基于token
|
||||||
|
*/
|
||||||
|
public static LoginUser getLoginUser(String token) {
|
||||||
|
return (LoginUser) StpUtil.getTokenSessionByToken(token).get(LOGIN_USER_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取用户id
|
* 获取用户id
|
||||||
*/
|
*/
|
||||||
public static Long getUserId() {
|
public static Long getUserId() {
|
||||||
LoginUser loginUser = getLoginUser();
|
Long userId;
|
||||||
if (ObjectUtil.isNull(loginUser)) {
|
try {
|
||||||
String loginId = StpUtil.getLoginIdAsString();
|
userId = Convert.toLong(SaHolder.getStorage().get(USER_KEY));
|
||||||
String[] strs = StringUtils.split(loginId, JOIN_CODE);
|
if (ObjectUtil.isNull(userId)) {
|
||||||
if (!ArrayUtil.containsAny(strs, UserType.values())) {
|
userId = Convert.toLong(StpUtil.getExtra(USER_KEY));
|
||||||
throw new UtilException("登录用户: LoginId异常 => " + loginId);
|
SaHolder.getStorage().set(USER_KEY, userId);
|
||||||
}
|
}
|
||||||
// 用户id在总是在最后
|
} catch (Exception e) {
|
||||||
return Long.parseLong(strs[strs.length - 1]);
|
return null;
|
||||||
}
|
}
|
||||||
return loginUser.getUserId();
|
return userId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -22,7 +22,9 @@ import java.util.Objects;
|
|||||||
* 字典数据json序列化工具
|
* 字典数据json序列化工具
|
||||||
*
|
*
|
||||||
* @author itino
|
* @author itino
|
||||||
|
* @deprecated 建议使用通用翻译注解
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class DictDataJsonSerializer extends JsonSerializer<String> implements ContextualSerializer {
|
public class DictDataJsonSerializer extends JsonSerializer<String> implements ContextualSerializer {
|
||||||
|
|
||||||
|
@ -0,0 +1,17 @@
|
|||||||
|
package com.ruoyi.common.translation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 翻译接口 (实现类需标注 {@link com.ruoyi.common.annotation.TranslationType} 注解标明翻译类型)
|
||||||
|
*
|
||||||
|
* @author Lion Li
|
||||||
|
*/
|
||||||
|
public interface TranslationInterface<T> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 翻译
|
||||||
|
*
|
||||||
|
* @param key 需要被翻译的键(不为空)
|
||||||
|
* @return 返回键对应的值
|
||||||
|
*/
|
||||||
|
T translation(Object key, String other);
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
package com.ruoyi.common.translation.handler;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.BeanDescription;
|
||||||
|
import com.fasterxml.jackson.databind.SerializationConfig;
|
||||||
|
import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
|
||||||
|
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bean 序列化修改器 解决 Null 被单独处理问题
|
||||||
|
*
|
||||||
|
* @author Lion Li
|
||||||
|
*/
|
||||||
|
public class TranslationBeanSerializerModifier extends BeanSerializerModifier {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc,
|
||||||
|
List<BeanPropertyWriter> beanProperties) {
|
||||||
|
for (BeanPropertyWriter writer : beanProperties) {
|
||||||
|
// 如果序列化器为 TranslationHandler 的话 将 Null 值也交给他处理
|
||||||
|
if (writer.getSerializer() instanceof TranslationHandler) {
|
||||||
|
writer.assignNullSerializer(writer.getSerializer());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return beanProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,65 @@
|
|||||||
|
package com.ruoyi.common.translation.handler;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
|
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.annotation.Translation;
|
||||||
|
import com.ruoyi.common.translation.TranslationInterface;
|
||||||
|
import com.ruoyi.common.utils.StringUtils;
|
||||||
|
import com.ruoyi.common.utils.reflect.ReflectUtils;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 翻译处理器
|
||||||
|
*
|
||||||
|
* @author Lion Li
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class TranslationHandler extends JsonSerializer<Object> implements ContextualSerializer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 全局翻译实现类映射器
|
||||||
|
*/
|
||||||
|
public static final Map<String, TranslationInterface<?>> TRANSLATION_MAPPER = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
private Translation translation;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
|
||||||
|
TranslationInterface<?> trans = TRANSLATION_MAPPER.get(translation.type());
|
||||||
|
if (ObjectUtil.isNotNull(trans)) {
|
||||||
|
// 如果映射字段不为空 则取映射字段的值
|
||||||
|
if (StringUtils.isNotBlank(translation.mapper())) {
|
||||||
|
value = ReflectUtils.invokeGetter(gen.getCurrentValue(), translation.mapper());
|
||||||
|
}
|
||||||
|
// 如果为 null 直接写出
|
||||||
|
if (ObjectUtil.isNull(value)) {
|
||||||
|
gen.writeNull();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Object result = trans.translation(value, translation.other());
|
||||||
|
gen.writeObject(result);
|
||||||
|
} else {
|
||||||
|
gen.writeObject(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException {
|
||||||
|
Translation translation = property.getAnnotation(Translation.class);
|
||||||
|
if (Objects.nonNull(translation)) {
|
||||||
|
this.translation = translation;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
return prov.findValueSerializer(property.getType(), property);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package com.ruoyi.common.translation.impl;
|
||||||
|
|
||||||
|
import com.ruoyi.common.annotation.TranslationType;
|
||||||
|
import com.ruoyi.common.constant.TransConstant;
|
||||||
|
import com.ruoyi.common.core.service.DeptService;
|
||||||
|
import com.ruoyi.common.translation.TranslationInterface;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 部门翻译实现
|
||||||
|
*
|
||||||
|
* @author Lion Li
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@AllArgsConstructor
|
||||||
|
@TranslationType(type = TransConstant.DEPT_ID_TO_NAME)
|
||||||
|
public class DeptNameTranslationImpl implements TranslationInterface<String> {
|
||||||
|
|
||||||
|
private final DeptService deptService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String translation(Object key, String other) {
|
||||||
|
return deptService.selectDeptNameByIds(key.toString());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
package com.ruoyi.common.translation.impl;
|
||||||
|
|
||||||
|
import com.ruoyi.common.annotation.TranslationType;
|
||||||
|
import com.ruoyi.common.constant.TransConstant;
|
||||||
|
import com.ruoyi.common.core.service.DictService;
|
||||||
|
import com.ruoyi.common.translation.TranslationInterface;
|
||||||
|
import com.ruoyi.common.utils.StringUtils;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字典翻译实现
|
||||||
|
*
|
||||||
|
* @author Lion Li
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@AllArgsConstructor
|
||||||
|
@TranslationType(type = TransConstant.DICT_TYPE_TO_LABEL)
|
||||||
|
public class DictTypeTranslationImpl implements TranslationInterface<String> {
|
||||||
|
|
||||||
|
private final DictService dictService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String translation(Object key, String other) {
|
||||||
|
if (key instanceof String && StringUtils.isNotBlank(other)) {
|
||||||
|
return dictService.getDictLabel(other, key.toString());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package com.ruoyi.common.translation.impl;
|
||||||
|
|
||||||
|
import com.ruoyi.common.annotation.TranslationType;
|
||||||
|
import com.ruoyi.common.constant.TransConstant;
|
||||||
|
import com.ruoyi.common.core.service.OssService;
|
||||||
|
import com.ruoyi.common.translation.TranslationInterface;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OSS翻译实现
|
||||||
|
*
|
||||||
|
* @author Lion Li
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@AllArgsConstructor
|
||||||
|
@TranslationType(type = TransConstant.OSS_ID_TO_URL)
|
||||||
|
public class OssUrlTranslationImpl implements TranslationInterface<String> {
|
||||||
|
|
||||||
|
private final OssService ossService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String translation(Object key, String other) {
|
||||||
|
return ossService.selectUrlByIds(key.toString());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
package com.ruoyi.common.translation.impl;
|
||||||
|
|
||||||
|
import com.ruoyi.common.annotation.TranslationType;
|
||||||
|
import com.ruoyi.common.constant.TransConstant;
|
||||||
|
import com.ruoyi.common.core.service.UserService;
|
||||||
|
import com.ruoyi.common.translation.TranslationInterface;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户名翻译实现
|
||||||
|
*
|
||||||
|
* @author Lion Li
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@AllArgsConstructor
|
||||||
|
@TranslationType(type = TransConstant.USER_ID_TO_NAME)
|
||||||
|
public class UserNameTranslationImpl implements TranslationInterface<String> {
|
||||||
|
|
||||||
|
private final UserService userService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String translation(Object key, String other) {
|
||||||
|
if (key instanceof Long) {
|
||||||
|
return userService.selectUserNameById((Long) key);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@ -12,6 +12,7 @@ import org.springframework.cglib.beans.BeanCopier;
|
|||||||
import org.springframework.cglib.beans.BeanMap;
|
import org.springframework.cglib.beans.BeanMap;
|
||||||
import org.springframework.cglib.core.Converter;
|
import org.springframework.cglib.core.Converter;
|
||||||
|
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@ -135,6 +136,25 @@ public class BeanCopyUtils {
|
|||||||
return bean;
|
return bean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* map拷贝到map
|
||||||
|
*
|
||||||
|
* @param map 数据来源
|
||||||
|
* @param clazz 返回的对象类型
|
||||||
|
* @return map对象
|
||||||
|
*/
|
||||||
|
public static <T, V> Map<String, V> mapToMap(Map<String, T> map, Class<V> clazz) {
|
||||||
|
if (MapUtil.isEmpty(map)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (ObjectUtil.isNull(clazz)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Map<String, V> copyMap = new LinkedHashMap<>(map.size());
|
||||||
|
map.forEach((k, v) -> copyMap.put(k, copy(v, clazz)));
|
||||||
|
return copyMap;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* BeanCopier属性缓存<br>
|
* BeanCopier属性缓存<br>
|
||||||
* 缓存用于防止多次反射造成的性能问题
|
* 缓存用于防止多次反射造成的性能问题
|
||||||
@ -174,7 +194,7 @@ public class BeanCopyUtils {
|
|||||||
private String genKey(Class<?> srcClass, Class<?> targetClass, Converter converter) {
|
private String genKey(Class<?> srcClass, Class<?> targetClass, Converter converter) {
|
||||||
final StringBuilder key = StrUtil.builder()
|
final StringBuilder key = StrUtil.builder()
|
||||||
.append(srcClass.getName()).append('#').append(targetClass.getName());
|
.append(srcClass.getName()).append('#').append(targetClass.getName());
|
||||||
if(null != converter){
|
if (null != converter) {
|
||||||
key.append('#').append(converter.getClass().getName());
|
key.append('#').append(converter.getClass().getName());
|
||||||
}
|
}
|
||||||
return key.toString();
|
return key.toString();
|
||||||
|
@ -0,0 +1,243 @@
|
|||||||
|
package com.ruoyi.common.utils;
|
||||||
|
|
||||||
|
import cn.hutool.core.codec.Base64;
|
||||||
|
import cn.hutool.core.util.ArrayUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import cn.hutool.crypto.SecureUtil;
|
||||||
|
import cn.hutool.crypto.SmUtil;
|
||||||
|
import cn.hutool.crypto.asymmetric.KeyType;
|
||||||
|
import cn.hutool.crypto.asymmetric.RSA;
|
||||||
|
import cn.hutool.crypto.asymmetric.SM2;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 安全相关工具类
|
||||||
|
*
|
||||||
|
* @author 老马
|
||||||
|
*/
|
||||||
|
public class EncryptUtils {
|
||||||
|
/**
|
||||||
|
* 公钥
|
||||||
|
*/
|
||||||
|
public static final String PUBLIC_KEY = "publicKey";
|
||||||
|
/**
|
||||||
|
* 私钥
|
||||||
|
*/
|
||||||
|
public static final String PRIVATE_KEY = "privateKey";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base64加密
|
||||||
|
*
|
||||||
|
* @param data 待加密数据
|
||||||
|
* @return 加密后字符串
|
||||||
|
*/
|
||||||
|
public static String encryptByBase64(String data) {
|
||||||
|
return Base64.encode(data, StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base64解密
|
||||||
|
*
|
||||||
|
* @param data 待解密数据
|
||||||
|
* @return 解密后字符串
|
||||||
|
*/
|
||||||
|
public static String decryptByBase64(String data) {
|
||||||
|
return Base64.decodeStr(data, StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AES加密
|
||||||
|
*
|
||||||
|
* @param data 待解密数据
|
||||||
|
* @param password 秘钥字符串
|
||||||
|
* @return 加密后字符串, 采用Base64编码
|
||||||
|
*/
|
||||||
|
public static String encryptByAes(String data, String password) {
|
||||||
|
if (StrUtil.isBlank(password)) {
|
||||||
|
throw new IllegalArgumentException("AES需要传入秘钥信息");
|
||||||
|
}
|
||||||
|
// aes算法的秘钥要求是16位、24位、32位
|
||||||
|
int[] array = {16, 24, 32};
|
||||||
|
if (!ArrayUtil.contains(array, password.length())) {
|
||||||
|
throw new IllegalArgumentException("AES秘钥长度要求为16位、24位、32位");
|
||||||
|
}
|
||||||
|
return SecureUtil.aes(password.getBytes(StandardCharsets.UTF_8)).encryptBase64(data, StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AES解密
|
||||||
|
*
|
||||||
|
* @param data 待解密数据
|
||||||
|
* @param password 秘钥字符串
|
||||||
|
* @return 解密后字符串
|
||||||
|
*/
|
||||||
|
public static String decryptByAes(String data, String password) {
|
||||||
|
if (StrUtil.isBlank(password)) {
|
||||||
|
throw new IllegalArgumentException("AES需要传入秘钥信息");
|
||||||
|
}
|
||||||
|
// aes算法的秘钥要求是16位、24位、32位
|
||||||
|
int[] array = {16, 24, 32};
|
||||||
|
if (!ArrayUtil.contains(array, password.length())) {
|
||||||
|
throw new IllegalArgumentException("AES秘钥长度要求为16位、24位、32位");
|
||||||
|
}
|
||||||
|
return SecureUtil.aes(password.getBytes(StandardCharsets.UTF_8)).decryptStr(data, StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sm4加密
|
||||||
|
*
|
||||||
|
* @param data 待加密数据
|
||||||
|
* @param password 秘钥字符串
|
||||||
|
* @return 加密后字符串, 采用Base64编码
|
||||||
|
*/
|
||||||
|
public static String encryptBySm4(String data, String password) {
|
||||||
|
if (StrUtil.isBlank(password)) {
|
||||||
|
throw new IllegalArgumentException("SM4需要传入秘钥信息");
|
||||||
|
}
|
||||||
|
// sm4算法的秘钥要求是16位长度
|
||||||
|
int sm4PasswordLength = 16;
|
||||||
|
if (sm4PasswordLength != password.length()) {
|
||||||
|
throw new IllegalArgumentException("SM4秘钥长度要求为16位");
|
||||||
|
}
|
||||||
|
return SmUtil.sm4(password.getBytes(StandardCharsets.UTF_8)).encryptBase64(data, StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sm4解密
|
||||||
|
*
|
||||||
|
* @param data 待解密数据
|
||||||
|
* @param password 秘钥字符串
|
||||||
|
* @return 解密后字符串
|
||||||
|
*/
|
||||||
|
public static String decryptBySm4(String data, String password) {
|
||||||
|
if (StrUtil.isBlank(password)) {
|
||||||
|
throw new IllegalArgumentException("SM4需要传入秘钥信息");
|
||||||
|
}
|
||||||
|
// sm4算法的秘钥要求是16位长度
|
||||||
|
int sm4PasswordLength = 16;
|
||||||
|
if (sm4PasswordLength != password.length()) {
|
||||||
|
throw new IllegalArgumentException("SM4秘钥长度要求为16位");
|
||||||
|
}
|
||||||
|
return SmUtil.sm4(password.getBytes(StandardCharsets.UTF_8)).decryptStr(data, StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 产生sm2加解密需要的公钥和私钥
|
||||||
|
*
|
||||||
|
* @return 公私钥Map
|
||||||
|
*/
|
||||||
|
public static Map<String, String> generateSm2Key() {
|
||||||
|
Map<String, String> keyMap = new HashMap<>(2);
|
||||||
|
SM2 sm2 = SmUtil.sm2();
|
||||||
|
keyMap.put(PRIVATE_KEY, sm2.getPrivateKeyBase64());
|
||||||
|
keyMap.put(PUBLIC_KEY, sm2.getPublicKeyBase64());
|
||||||
|
return keyMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sm2公钥加密
|
||||||
|
*
|
||||||
|
* @param data 待加密数据
|
||||||
|
* @param publicKey 公钥
|
||||||
|
* @return 加密后字符串, 采用Base64编码
|
||||||
|
*/
|
||||||
|
public static String encryptBySm2(String data, String publicKey) {
|
||||||
|
if (StrUtil.isBlank(publicKey)) {
|
||||||
|
throw new IllegalArgumentException("SM2需要传入公钥进行加密");
|
||||||
|
}
|
||||||
|
SM2 sm2 = SmUtil.sm2(null, publicKey);
|
||||||
|
return sm2.encryptBase64(data, StandardCharsets.UTF_8, KeyType.PublicKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sm2私钥解密
|
||||||
|
*
|
||||||
|
* @param data 待加密数据
|
||||||
|
* @param privateKey 私钥
|
||||||
|
* @return 解密后字符串
|
||||||
|
*/
|
||||||
|
public static String decryptBySm2(String data, String privateKey) {
|
||||||
|
if (StrUtil.isBlank(privateKey)) {
|
||||||
|
throw new IllegalArgumentException("SM2需要传入私钥进行解密");
|
||||||
|
}
|
||||||
|
SM2 sm2 = SmUtil.sm2(privateKey, null);
|
||||||
|
return sm2.decryptStr(data, KeyType.PrivateKey, StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 产生RSA加解密需要的公钥和私钥
|
||||||
|
*
|
||||||
|
* @return 公私钥Map
|
||||||
|
*/
|
||||||
|
public static Map<String, String> generateRsaKey() {
|
||||||
|
Map<String, String> keyMap = new HashMap<>(2);
|
||||||
|
RSA rsa = SecureUtil.rsa();
|
||||||
|
keyMap.put(PRIVATE_KEY, rsa.getPrivateKeyBase64());
|
||||||
|
keyMap.put(PUBLIC_KEY, rsa.getPublicKeyBase64());
|
||||||
|
return keyMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* rsa公钥加密
|
||||||
|
*
|
||||||
|
* @param data 待加密数据
|
||||||
|
* @param publicKey 公钥
|
||||||
|
* @return 加密后字符串, 采用Base64编码
|
||||||
|
*/
|
||||||
|
public static String encryptByRsa(String data, String publicKey) {
|
||||||
|
if (StrUtil.isBlank(publicKey)) {
|
||||||
|
throw new IllegalArgumentException("RSA需要传入公钥进行加密");
|
||||||
|
}
|
||||||
|
RSA rsa = SecureUtil.rsa(null, publicKey);
|
||||||
|
return rsa.encryptBase64(data, StandardCharsets.UTF_8, KeyType.PublicKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* rsa私钥解密
|
||||||
|
*
|
||||||
|
* @param data 待加密数据
|
||||||
|
* @param privateKey 私钥
|
||||||
|
* @return 解密后字符串
|
||||||
|
*/
|
||||||
|
public static String decryptByRsa(String data, String privateKey) {
|
||||||
|
if (StrUtil.isBlank(privateKey)) {
|
||||||
|
throw new IllegalArgumentException("RSA需要传入私钥进行解密");
|
||||||
|
}
|
||||||
|
RSA rsa = SecureUtil.rsa(privateKey, null);
|
||||||
|
return rsa.decryptStr(data, KeyType.PrivateKey, StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* md5加密
|
||||||
|
*
|
||||||
|
* @param data 待加密数据
|
||||||
|
* @return 加密后字符串, 采用Hex编码
|
||||||
|
*/
|
||||||
|
public static String encryptByMd5(String data) {
|
||||||
|
return SecureUtil.md5(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sha256加密
|
||||||
|
*
|
||||||
|
* @param data 待加密数据
|
||||||
|
* @return 加密后字符串, 采用Hex编码
|
||||||
|
*/
|
||||||
|
public static String encryptBySha256(String data) {
|
||||||
|
return SecureUtil.sha256(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sm3加密
|
||||||
|
*
|
||||||
|
* @param data 待加密数据
|
||||||
|
* @return 加密后字符串, 采用Hex编码
|
||||||
|
*/
|
||||||
|
public static String encryptBySm3(String data) {
|
||||||
|
return SmUtil.sm3(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -94,7 +94,7 @@ public class ServletUtils extends ServletUtil {
|
|||||||
public static Map<String, String> getParamMap(ServletRequest request) {
|
public static Map<String, String> getParamMap(ServletRequest request) {
|
||||||
Map<String, String> params = new HashMap<>();
|
Map<String, String> params = new HashMap<>();
|
||||||
for (Map.Entry<String, String[]> entry : getParams(request).entrySet()) {
|
for (Map.Entry<String, String[]> entry : getParams(request).entrySet()) {
|
||||||
params.put(entry.getKey(), StringUtils.join(entry.getValue(), ","));
|
params.put(entry.getKey(), StringUtils.join(entry.getValue(), StringUtils.SEPARATOR));
|
||||||
}
|
}
|
||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ public class StreamUtils {
|
|||||||
* @return 拼接后的list
|
* @return 拼接后的list
|
||||||
*/
|
*/
|
||||||
public static <E> String join(Collection<E> collection, Function<E, String> function) {
|
public static <E> String join(Collection<E> collection, Function<E, String> function) {
|
||||||
return join(collection, function, ",");
|
return join(collection, function, StringUtils.SEPARATOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
package com.ruoyi.common.utils;
|
package com.ruoyi.common.utils;
|
||||||
|
|
||||||
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import cn.hutool.core.convert.Convert;
|
||||||
import cn.hutool.core.lang.Validator;
|
import cn.hutool.core.lang.Validator;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import lombok.AccessLevel;
|
import lombok.AccessLevel;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import org.springframework.util.AntPathMatcher;
|
import org.springframework.util.AntPathMatcher;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.HashSet;
|
import java.util.function.Function;
|
||||||
import java.util.List;
|
import java.util.stream.Collectors;
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 字符串工具类
|
* 字符串工具类
|
||||||
@ -20,6 +20,8 @@ import java.util.Set;
|
|||||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||||
public class StringUtils extends org.apache.commons.lang3.StringUtils {
|
public class StringUtils extends org.apache.commons.lang3.StringUtils {
|
||||||
|
|
||||||
|
public static final String SEPARATOR = ",";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取参数不为空值
|
* 获取参数不为空值
|
||||||
*
|
*
|
||||||
@ -224,7 +226,6 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
|
|||||||
*
|
*
|
||||||
* @param pattern 匹配规则
|
* @param pattern 匹配规则
|
||||||
* @param url 需要匹配的url
|
* @param url 需要匹配的url
|
||||||
* @return
|
|
||||||
*/
|
*/
|
||||||
public static boolean isMatch(String pattern, String url) {
|
public static boolean isMatch(String pattern, String url) {
|
||||||
AntPathMatcher matcher = new AntPathMatcher();
|
AntPathMatcher matcher = new AntPathMatcher();
|
||||||
@ -234,23 +235,23 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
|
|||||||
/**
|
/**
|
||||||
* 数字左边补齐0,使之达到指定长度。注意,如果数字转换为字符串后,长度大于size,则只保留 最后size个字符。
|
* 数字左边补齐0,使之达到指定长度。注意,如果数字转换为字符串后,长度大于size,则只保留 最后size个字符。
|
||||||
*
|
*
|
||||||
* @param num 数字对象
|
* @param num 数字对象
|
||||||
* @param size 字符串指定长度
|
* @param size 字符串指定长度
|
||||||
* @return 返回数字的字符串格式,该字符串为指定长度。
|
* @return 返回数字的字符串格式,该字符串为指定长度。
|
||||||
*/
|
*/
|
||||||
public static final String padl(final Number num, final int size) {
|
public static String padl(final Number num, final int size) {
|
||||||
return padl(num.toString(), size, '0');
|
return padl(num.toString(), size, '0');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 字符串左补齐。如果原始字符串s长度大于size,则只保留最后size个字符。
|
* 字符串左补齐。如果原始字符串s长度大于size,则只保留最后size个字符。
|
||||||
*
|
*
|
||||||
* @param s 原始字符串
|
* @param s 原始字符串
|
||||||
* @param size 字符串指定长度
|
* @param size 字符串指定长度
|
||||||
* @param c 用于补齐的字符
|
* @param c 用于补齐的字符
|
||||||
* @return 返回指定长度的字符串,由原字符串左补齐或截取得到。
|
* @return 返回指定长度的字符串,由原字符串左补齐或截取得到。
|
||||||
*/
|
*/
|
||||||
public static final String padl(final String s, final int size, final char c) {
|
public static String padl(final String s, final int size, final char c) {
|
||||||
final StringBuilder sb = new StringBuilder(size);
|
final StringBuilder sb = new StringBuilder(size);
|
||||||
if (s != null) {
|
if (s != null) {
|
||||||
final int len = s.length();
|
final int len = s.length();
|
||||||
@ -270,4 +271,55 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
|
|||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 切分字符串(分隔符默认逗号)
|
||||||
|
*
|
||||||
|
* @param str 被切分的字符串
|
||||||
|
* @return 分割后的数据列表
|
||||||
|
*/
|
||||||
|
public static List<String> splitList(String str) {
|
||||||
|
return splitTo(str, Convert::toStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 切分字符串
|
||||||
|
*
|
||||||
|
* @param str 被切分的字符串
|
||||||
|
* @param separator 分隔符
|
||||||
|
* @return 分割后的数据列表
|
||||||
|
*/
|
||||||
|
public static List<String> splitList(String str, String separator) {
|
||||||
|
return splitTo(str, separator, Convert::toStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 切分字符串自定义转换(分隔符默认逗号)
|
||||||
|
*
|
||||||
|
* @param str 被切分的字符串
|
||||||
|
* @param mapper 自定义转换
|
||||||
|
* @return 分割后的数据列表
|
||||||
|
*/
|
||||||
|
public static <T> List<T> splitTo(String str, Function<? super Object, T> mapper) {
|
||||||
|
return splitTo(str, SEPARATOR, mapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 切分字符串自定义转换
|
||||||
|
*
|
||||||
|
* @param str 被切分的字符串
|
||||||
|
* @param separator 分隔符
|
||||||
|
* @param mapper 自定义转换
|
||||||
|
* @return 分割后的数据列表
|
||||||
|
*/
|
||||||
|
public static <T> List<T> splitTo(String str, String separator, Function<? super Object, T> mapper) {
|
||||||
|
if (isBlank(str)) {
|
||||||
|
return new ArrayList<>(0);
|
||||||
|
}
|
||||||
|
return StrUtil.split(str, separator)
|
||||||
|
.stream()
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.map(mapper)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,7 @@
|
|||||||
package com.ruoyi.common.utils.ip;
|
package com.ruoyi.common.utils.ip;
|
||||||
|
|
||||||
import cn.hutool.core.lang.Dict;
|
|
||||||
import cn.hutool.core.net.NetUtil;
|
import cn.hutool.core.net.NetUtil;
|
||||||
import cn.hutool.http.HtmlUtil;
|
import cn.hutool.http.HtmlUtil;
|
||||||
import cn.hutool.http.HttpUtil;
|
|
||||||
import com.ruoyi.common.config.RuoYiConfig;
|
|
||||||
import com.ruoyi.common.constant.Constants;
|
|
||||||
import com.ruoyi.common.utils.JsonUtils;
|
|
||||||
import com.ruoyi.common.utils.StringUtils;
|
import com.ruoyi.common.utils.StringUtils;
|
||||||
import lombok.AccessLevel;
|
import lombok.AccessLevel;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
@ -21,40 +16,18 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||||
public class AddressUtils {
|
public class AddressUtils {
|
||||||
|
|
||||||
// IP地址查询
|
|
||||||
public static final String IP_URL = "http://whois.pconline.com.cn/ipJson.jsp";
|
|
||||||
|
|
||||||
// 未知地址
|
// 未知地址
|
||||||
public static final String UNKNOWN = "XX XX";
|
public static final String UNKNOWN = "XX XX";
|
||||||
|
|
||||||
public static String getRealAddressByIP(String ip) {
|
public static String getRealAddressByIP(String ip) {
|
||||||
String address = UNKNOWN;
|
|
||||||
if (StringUtils.isBlank(ip)) {
|
if (StringUtils.isBlank(ip)) {
|
||||||
return address;
|
return UNKNOWN;
|
||||||
}
|
}
|
||||||
// 内网不查询
|
// 内网不查询
|
||||||
ip = "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : HtmlUtil.cleanHtmlTag(ip);
|
ip = "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : HtmlUtil.cleanHtmlTag(ip);
|
||||||
if (NetUtil.isInnerIP(ip)) {
|
if (NetUtil.isInnerIP(ip)) {
|
||||||
return "内网IP";
|
return "内网IP";
|
||||||
}
|
}
|
||||||
if (RuoYiConfig.isAddressEnabled()) {
|
return RegionUtils.getCityInfo(ip);
|
||||||
try {
|
|
||||||
String rspStr = HttpUtil.createGet(IP_URL)
|
|
||||||
.body("ip=" + ip + "&json=true", Constants.GBK)
|
|
||||||
.execute()
|
|
||||||
.body();
|
|
||||||
if (StringUtils.isEmpty(rspStr)) {
|
|
||||||
log.error("获取地理位置异常 {}", ip);
|
|
||||||
return UNKNOWN;
|
|
||||||
}
|
|
||||||
Dict obj = JsonUtils.parseMap(rspStr);
|
|
||||||
String region = obj.getStr("pro");
|
|
||||||
String city = obj.getStr("city");
|
|
||||||
return String.format("%s %s", region, city);
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("获取地理位置异常 {}", ip);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return UNKNOWN;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,66 @@
|
|||||||
|
package com.ruoyi.common.utils.ip;
|
||||||
|
|
||||||
|
import cn.hutool.core.io.FileUtil;
|
||||||
|
import cn.hutool.core.io.resource.ClassPathResource;
|
||||||
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
|
import com.ruoyi.common.exception.ServiceException;
|
||||||
|
import com.ruoyi.common.utils.file.FileUtils;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.lionsoul.ip2region.xdb.Searcher;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据ip地址定位工具类,离线方式
|
||||||
|
* 参考地址:<a href="https://gitee.com/lionsoul/ip2region/tree/master/binding/java">集成 ip2region 实现离线IP地址定位库</a>
|
||||||
|
*
|
||||||
|
* @author lishuyan
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class RegionUtils {
|
||||||
|
|
||||||
|
private static final Searcher SEARCHER;
|
||||||
|
|
||||||
|
static {
|
||||||
|
String fileName = "/ip2region.xdb";
|
||||||
|
File existFile = FileUtils.file(FileUtil.getTmpDir() + FileUtil.FILE_SEPARATOR + fileName);
|
||||||
|
if (!FileUtils.exist(existFile)) {
|
||||||
|
ClassPathResource fileStream = new ClassPathResource(fileName);
|
||||||
|
if (ObjectUtil.isEmpty(fileStream.getStream())) {
|
||||||
|
throw new ServiceException("RegionUtils初始化失败,原因:IP地址库数据不存在!");
|
||||||
|
}
|
||||||
|
FileUtils.writeFromStream(fileStream.getStream(), existFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
String dbPath = existFile.getPath();
|
||||||
|
|
||||||
|
// 1、从 dbPath 加载整个 xdb 到内存。
|
||||||
|
byte[] cBuff;
|
||||||
|
try {
|
||||||
|
cBuff = Searcher.loadContentFromFile(dbPath);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new ServiceException("RegionUtils初始化失败,原因:从ip2region.xdb文件加载内容失败!" + e.getMessage());
|
||||||
|
}
|
||||||
|
// 2、使用上述的 cBuff 创建一个完全基于内存的查询对象。
|
||||||
|
try {
|
||||||
|
SEARCHER = Searcher.newWithBuffer(cBuff);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new ServiceException("RegionUtils初始化失败,原因:" + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据IP地址离线获取城市
|
||||||
|
*/
|
||||||
|
public static String getCityInfo(String ip) {
|
||||||
|
try {
|
||||||
|
ip = ip.trim();
|
||||||
|
// 3、执行查询
|
||||||
|
String region = SEARCHER.search(ip);
|
||||||
|
return region.replace("0|", "").replace("|0", "");
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("IP地址离线获取城市异常 {}", ip);
|
||||||
|
return "未知";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -24,6 +24,7 @@ import javax.servlet.ServletOutputStream;
|
|||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -84,7 +85,13 @@ public class ExcelUtil {
|
|||||||
* @param response 响应体
|
* @param response 响应体
|
||||||
*/
|
*/
|
||||||
public static <T> void exportExcel(List<T> list, String sheetName, Class<T> clazz, HttpServletResponse response) {
|
public static <T> void exportExcel(List<T> list, String sheetName, Class<T> clazz, HttpServletResponse response) {
|
||||||
exportExcel(list, sheetName, clazz, false, response);
|
try {
|
||||||
|
resetResponse(sheetName, response);
|
||||||
|
ServletOutputStream os = response.getOutputStream();
|
||||||
|
exportExcel(list, sheetName, clazz, false, os);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException("导出Excel异常");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -100,23 +107,48 @@ public class ExcelUtil {
|
|||||||
try {
|
try {
|
||||||
resetResponse(sheetName, response);
|
resetResponse(sheetName, response);
|
||||||
ServletOutputStream os = response.getOutputStream();
|
ServletOutputStream os = response.getOutputStream();
|
||||||
ExcelWriterSheetBuilder builder = EasyExcel.write(os, clazz)
|
exportExcel(list, sheetName, clazz, merge, os);
|
||||||
.autoCloseStream(false)
|
|
||||||
// 自动适配
|
|
||||||
.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
|
|
||||||
// 大数值自动转换 防止失真
|
|
||||||
.registerConverter(new ExcelBigNumberConvert())
|
|
||||||
.sheet(sheetName);
|
|
||||||
if (merge) {
|
|
||||||
// 合并处理器
|
|
||||||
builder.registerWriteHandler(new CellMergeStrategy(list, true));
|
|
||||||
}
|
|
||||||
builder.doWrite(list);
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RuntimeException("导出Excel异常");
|
throw new RuntimeException("导出Excel异常");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出excel
|
||||||
|
*
|
||||||
|
* @param list 导出数据集合
|
||||||
|
* @param sheetName 工作表的名称
|
||||||
|
* @param clazz 实体类
|
||||||
|
* @param os 输出流
|
||||||
|
*/
|
||||||
|
public static <T> void exportExcel(List<T> list, String sheetName, Class<T> clazz, OutputStream os) {
|
||||||
|
exportExcel(list, sheetName, clazz, false, os);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出excel
|
||||||
|
*
|
||||||
|
* @param list 导出数据集合
|
||||||
|
* @param sheetName 工作表的名称
|
||||||
|
* @param clazz 实体类
|
||||||
|
* @param merge 是否合并单元格
|
||||||
|
* @param os 输出流
|
||||||
|
*/
|
||||||
|
public static <T> void exportExcel(List<T> list, String sheetName, Class<T> clazz, boolean merge, OutputStream os) {
|
||||||
|
ExcelWriterSheetBuilder builder = EasyExcel.write(os, clazz)
|
||||||
|
.autoCloseStream(false)
|
||||||
|
// 自动适配
|
||||||
|
.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
|
||||||
|
// 大数值自动转换 防止失真
|
||||||
|
.registerConverter(new ExcelBigNumberConvert())
|
||||||
|
.sheet(sheetName);
|
||||||
|
if (merge) {
|
||||||
|
// 合并处理器
|
||||||
|
builder.registerWriteHandler(new CellMergeStrategy(list, true));
|
||||||
|
}
|
||||||
|
builder.doWrite(list);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 单表多数据模板导出 模板格式为 {.属性}
|
* 单表多数据模板导出 模板格式为 {.属性}
|
||||||
*
|
*
|
||||||
@ -125,31 +157,46 @@ public class ExcelUtil {
|
|||||||
* 例如: excel/temp.xlsx
|
* 例如: excel/temp.xlsx
|
||||||
* 重点: 模板文件必须放置到启动类对应的 resource 目录下
|
* 重点: 模板文件必须放置到启动类对应的 resource 目录下
|
||||||
* @param data 模板需要的数据
|
* @param data 模板需要的数据
|
||||||
|
* @param response 响应体
|
||||||
*/
|
*/
|
||||||
public static void exportTemplate(List<Object> data, String filename, String templatePath, HttpServletResponse response) {
|
public static void exportTemplate(List<Object> data, String filename, String templatePath, HttpServletResponse response) {
|
||||||
try {
|
try {
|
||||||
resetResponse(filename, response);
|
resetResponse(filename, response);
|
||||||
ClassPathResource templateResource = new ClassPathResource(templatePath);
|
ServletOutputStream os = response.getOutputStream();
|
||||||
ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream())
|
exportTemplate(data, templatePath, os);
|
||||||
.withTemplate(templateResource.getStream())
|
|
||||||
.autoCloseStream(false)
|
|
||||||
// 大数值自动转换 防止失真
|
|
||||||
.registerConverter(new ExcelBigNumberConvert())
|
|
||||||
.build();
|
|
||||||
WriteSheet writeSheet = EasyExcel.writerSheet().build();
|
|
||||||
if (CollUtil.isEmpty(data)) {
|
|
||||||
throw new IllegalArgumentException("数据为空");
|
|
||||||
}
|
|
||||||
// 单表多数据导出 模板格式为 {.属性}
|
|
||||||
for (Object d : data) {
|
|
||||||
excelWriter.fill(d, writeSheet);
|
|
||||||
}
|
|
||||||
excelWriter.finish();
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RuntimeException("导出Excel异常");
|
throw new RuntimeException("导出Excel异常");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 单表多数据模板导出 模板格式为 {.属性}
|
||||||
|
*
|
||||||
|
* @param templatePath 模板路径 resource 目录下的路径包括模板文件名
|
||||||
|
* 例如: excel/temp.xlsx
|
||||||
|
* 重点: 模板文件必须放置到启动类对应的 resource 目录下
|
||||||
|
* @param data 模板需要的数据
|
||||||
|
* @param os 输出流
|
||||||
|
*/
|
||||||
|
public static void exportTemplate(List<Object> data, String templatePath, OutputStream os) {
|
||||||
|
ClassPathResource templateResource = new ClassPathResource(templatePath);
|
||||||
|
ExcelWriter excelWriter = EasyExcel.write(os)
|
||||||
|
.withTemplate(templateResource.getStream())
|
||||||
|
.autoCloseStream(false)
|
||||||
|
// 大数值自动转换 防止失真
|
||||||
|
.registerConverter(new ExcelBigNumberConvert())
|
||||||
|
.build();
|
||||||
|
WriteSheet writeSheet = EasyExcel.writerSheet().build();
|
||||||
|
if (CollUtil.isEmpty(data)) {
|
||||||
|
throw new IllegalArgumentException("数据为空");
|
||||||
|
}
|
||||||
|
// 单表多数据导出 模板格式为 {.属性}
|
||||||
|
for (Object d : data) {
|
||||||
|
excelWriter.fill(d, writeSheet);
|
||||||
|
}
|
||||||
|
excelWriter.finish();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 多表多数据模板导出 模板格式为 {key.属性}
|
* 多表多数据模板导出 模板格式为 {key.属性}
|
||||||
*
|
*
|
||||||
@ -158,37 +205,52 @@ public class ExcelUtil {
|
|||||||
* 例如: excel/temp.xlsx
|
* 例如: excel/temp.xlsx
|
||||||
* 重点: 模板文件必须放置到启动类对应的 resource 目录下
|
* 重点: 模板文件必须放置到启动类对应的 resource 目录下
|
||||||
* @param data 模板需要的数据
|
* @param data 模板需要的数据
|
||||||
|
* @param response 响应体
|
||||||
*/
|
*/
|
||||||
public static void exportTemplateMultiList(Map<String, Object> data, String filename, String templatePath, HttpServletResponse response) {
|
public static void exportTemplateMultiList(Map<String, Object> data, String filename, String templatePath, HttpServletResponse response) {
|
||||||
try {
|
try {
|
||||||
resetResponse(filename, response);
|
resetResponse(filename, response);
|
||||||
ClassPathResource templateResource = new ClassPathResource(templatePath);
|
ServletOutputStream os = response.getOutputStream();
|
||||||
ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream())
|
exportTemplateMultiList(data, templatePath, os);
|
||||||
.withTemplate(templateResource.getStream())
|
|
||||||
.autoCloseStream(false)
|
|
||||||
// 大数值自动转换 防止失真
|
|
||||||
.registerConverter(new ExcelBigNumberConvert())
|
|
||||||
.build();
|
|
||||||
WriteSheet writeSheet = EasyExcel.writerSheet().build();
|
|
||||||
if (CollUtil.isEmpty(data)) {
|
|
||||||
throw new IllegalArgumentException("数据为空");
|
|
||||||
}
|
|
||||||
for (Map.Entry<String, Object> map : data.entrySet()) {
|
|
||||||
// 设置列表后续还有数据
|
|
||||||
FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
|
|
||||||
if (map.getValue() instanceof Collection) {
|
|
||||||
// 多表导出必须使用 FillWrapper
|
|
||||||
excelWriter.fill(new FillWrapper(map.getKey(), (Collection<?>) map.getValue()), fillConfig, writeSheet);
|
|
||||||
} else {
|
|
||||||
excelWriter.fill(map.getValue(), writeSheet);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
excelWriter.finish();
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RuntimeException("导出Excel异常");
|
throw new RuntimeException("导出Excel异常");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 多表多数据模板导出 模板格式为 {key.属性}
|
||||||
|
*
|
||||||
|
* @param templatePath 模板路径 resource 目录下的路径包括模板文件名
|
||||||
|
* 例如: excel/temp.xlsx
|
||||||
|
* 重点: 模板文件必须放置到启动类对应的 resource 目录下
|
||||||
|
* @param data 模板需要的数据
|
||||||
|
* @param os 输出流
|
||||||
|
*/
|
||||||
|
public static void exportTemplateMultiList(Map<String, Object> data, String templatePath, OutputStream os) {
|
||||||
|
ClassPathResource templateResource = new ClassPathResource(templatePath);
|
||||||
|
ExcelWriter excelWriter = EasyExcel.write(os)
|
||||||
|
.withTemplate(templateResource.getStream())
|
||||||
|
.autoCloseStream(false)
|
||||||
|
// 大数值自动转换 防止失真
|
||||||
|
.registerConverter(new ExcelBigNumberConvert())
|
||||||
|
.build();
|
||||||
|
WriteSheet writeSheet = EasyExcel.writerSheet().build();
|
||||||
|
if (CollUtil.isEmpty(data)) {
|
||||||
|
throw new IllegalArgumentException("数据为空");
|
||||||
|
}
|
||||||
|
for (Map.Entry<String, Object> map : data.entrySet()) {
|
||||||
|
// 设置列表后续还有数据
|
||||||
|
FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
|
||||||
|
if (map.getValue() instanceof Collection) {
|
||||||
|
// 多表导出必须使用 FillWrapper
|
||||||
|
excelWriter.fill(new FillWrapper(map.getKey(), (Collection<?>) map.getValue()), fillConfig, writeSheet);
|
||||||
|
} else {
|
||||||
|
excelWriter.fill(map.getValue(), writeSheet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
excelWriter.finish();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 重置响应体
|
* 重置响应体
|
||||||
*/
|
*/
|
||||||
@ -208,7 +270,7 @@ public class ExcelUtil {
|
|||||||
*/
|
*/
|
||||||
public static String convertByExp(String propertyValue, String converterExp, String separator) {
|
public static String convertByExp(String propertyValue, String converterExp, String separator) {
|
||||||
StringBuilder propertyString = new StringBuilder();
|
StringBuilder propertyString = new StringBuilder();
|
||||||
String[] convertSource = converterExp.split(",");
|
String[] convertSource = converterExp.split(StringUtils.SEPARATOR);
|
||||||
for (String item : convertSource) {
|
for (String item : convertSource) {
|
||||||
String[] itemArray = item.split("=");
|
String[] itemArray = item.split("=");
|
||||||
if (StringUtils.containsAny(propertyValue, separator)) {
|
if (StringUtils.containsAny(propertyValue, separator)) {
|
||||||
@ -237,7 +299,7 @@ public class ExcelUtil {
|
|||||||
*/
|
*/
|
||||||
public static String reverseByExp(String propertyValue, String converterExp, String separator) {
|
public static String reverseByExp(String propertyValue, String converterExp, String separator) {
|
||||||
StringBuilder propertyString = new StringBuilder();
|
StringBuilder propertyString = new StringBuilder();
|
||||||
String[] convertSource = converterExp.split(",");
|
String[] convertSource = converterExp.split(StringUtils.SEPARATOR);
|
||||||
for (String item : convertSource) {
|
for (String item : convertSource) {
|
||||||
String[] itemArray = item.split("=");
|
String[] itemArray = item.split("=");
|
||||||
if (StringUtils.containsAny(propertyValue, separator)) {
|
if (StringUtils.containsAny(propertyValue, separator)) {
|
||||||
|
@ -4,7 +4,6 @@ import com.ruoyi.common.utils.spring.SpringUtils;
|
|||||||
import lombok.AccessLevel;
|
import lombok.AccessLevel;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import org.redisson.api.*;
|
import org.redisson.api.*;
|
||||||
import org.redisson.config.Config;
|
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@ -27,14 +26,6 @@ public class RedisUtils {
|
|||||||
|
|
||||||
private static final RedissonClient CLIENT = SpringUtils.getBean(RedissonClient.class);
|
private static final RedissonClient CLIENT = SpringUtils.getBean(RedissonClient.class);
|
||||||
|
|
||||||
public static NameMapper getNameMapper() {
|
|
||||||
Config config = CLIENT.getConfig();
|
|
||||||
if (config.isClusterConfig()) {
|
|
||||||
return config.useClusterServers().getNameMapper();
|
|
||||||
}
|
|
||||||
return config.useSingleServer().getNameMapper();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 限流
|
* 限流
|
||||||
*
|
*
|
||||||
@ -218,6 +209,15 @@ public class RedisUtils {
|
|||||||
batch.execute();
|
batch.execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查缓存对象是否存在
|
||||||
|
*
|
||||||
|
* @param key 缓存的键值
|
||||||
|
*/
|
||||||
|
public static boolean isExistsObject(final String key) {
|
||||||
|
return CLIENT.getBucket(key).isExists();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 缓存List数据
|
* 缓存List数据
|
||||||
*
|
*
|
||||||
@ -437,8 +437,8 @@ public class RedisUtils {
|
|||||||
* @return 对象列表
|
* @return 对象列表
|
||||||
*/
|
*/
|
||||||
public static Collection<String> keys(final String pattern) {
|
public static Collection<String> keys(final String pattern) {
|
||||||
Stream<String> stream = CLIENT.getKeys().getKeysStreamByPattern(getNameMapper().map(pattern));
|
Stream<String> stream = CLIENT.getKeys().getKeysStreamByPattern(pattern);
|
||||||
return stream.map(key -> getNameMapper().unmap(key)).collect(Collectors.toList());
|
return stream.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -447,7 +447,7 @@ public class RedisUtils {
|
|||||||
* @param pattern 字符串前缀
|
* @param pattern 字符串前缀
|
||||||
*/
|
*/
|
||||||
public static void deleteKeys(final String pattern) {
|
public static void deleteKeys(final String pattern) {
|
||||||
CLIENT.getKeys().deleteByPattern(getNameMapper().map(pattern));
|
CLIENT.getKeys().deleteByPattern(pattern);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -457,6 +457,6 @@ public class RedisUtils {
|
|||||||
*/
|
*/
|
||||||
public static Boolean hasKey(String key) {
|
public static Boolean hasKey(String key) {
|
||||||
RKeys rKeys = CLIENT.getKeys();
|
RKeys rKeys = CLIENT.getKeys();
|
||||||
return rKeys.countExists(getNameMapper().map(key)) > 0;
|
return rKeys.countExists(key) > 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package com.ruoyi.common.utils.spring;
|
|||||||
import cn.hutool.extra.spring.SpringUtil;
|
import cn.hutool.extra.spring.SpringUtil;
|
||||||
import org.springframework.aop.framework.AopContext;
|
import org.springframework.aop.framework.AopContext;
|
||||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -62,4 +63,12 @@ public final class SpringUtils extends SpringUtil {
|
|||||||
return (T) AopContext.currentProxy();
|
return (T) AopContext.currentProxy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取spring上下文
|
||||||
|
*/
|
||||||
|
public static ApplicationContext context() {
|
||||||
|
return getApplicationContext();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>ruoyi-vue-plus</artifactId>
|
<artifactId>ruoyi-vue-plus</artifactId>
|
||||||
<groupId>com.ruoyi</groupId>
|
<groupId>com.ruoyi</groupId>
|
||||||
<version>4.4.0</version>
|
<version>4.7.0</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user