mirror of
https://github.com/dromara/RuoYi-Vue-Plus.git
synced 2025-09-23 23:09:47 +08:00
Compare commits
161 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 | |||
cedb174ffd | |||
1a70cf658c | |||
1496220a74 | |||
d6894c89ed | |||
ebf780617a | |||
0208fa14af | |||
8f34644692 | |||
d2774d3706 | |||
0788bcfcb0 | |||
e4fff795b8 | |||
fd7dc204de | |||
4e807455e0 | |||
67e20711d4 | |||
138842e9ba | |||
1a8e2b3e63 | |||
6f83ed3285 | |||
369e4be31c | |||
5c9fe22780 | |||
b2a927cc5e | |||
23cbed3aac | |||
8acce4a7fc | |||
bf362b9aba | |||
9b57c67d3e | |||
0700c60636 | |||
95a2c74462 | |||
ca085b9aaa | |||
329c070e05 | |||
f3a4104fd0 | |||
82e8ab2385 | |||
c9260c4966 |
@ -2,7 +2,7 @@
|
||||
<configuration default="false" name="ruoyi-monitor-admin" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
|
||||
<deployment type="dockerfile">
|
||||
<settings>
|
||||
<option name="imageTag" value="ruoyi/ruoyi-monitor-admin:4.5.0" />
|
||||
<option name="imageTag" value="ruoyi/ruoyi-monitor-admin:4.7.0" />
|
||||
<option name="buildOnly" value="true" />
|
||||
<option name="sourceFilePath" value="ruoyi-extend/ruoyi-monitor-admin/Dockerfile" />
|
||||
</settings>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<configuration default="false" name="ruoyi-server" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
|
||||
<deployment type="dockerfile">
|
||||
<settings>
|
||||
<option name="imageTag" value="ruoyi/ruoyi-server:4.5.0" />
|
||||
<option name="imageTag" value="ruoyi/ruoyi-server:4.7.0" />
|
||||
<option name="buildOnly" value="true" />
|
||||
<option name="sourceFilePath" value="ruoyi-admin/Dockerfile" />
|
||||
</settings>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<configuration default="false" name="ruoyi-xxl-job-admin" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
|
||||
<deployment type="dockerfile">
|
||||
<settings>
|
||||
<option name="imageTag" value="ruoyi/ruoyi-xxl-job-admin:4.5.0" />
|
||||
<option name="imageTag" value="ruoyi/ruoyi-xxl-job-admin:4.7.0" />
|
||||
<option name="buildOnly" value="true" />
|
||||
<option name="sourceFilePath" value="ruoyi-extend/ruoyi-xxl-job-admin/Dockerfile" />
|
||||
</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/JavaLionLi/RuoYi-Vue-Plus/blob/master/LICENSE)
|
||||
|
||||
[](https://gitee.com/dromara/RuoYi-Vue-Plus)
|
||||
[](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)
|
||||
<br>
|
||||
[](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus)
|
||||
[](https://gitee.com/dromara/RuoYi-Vue-Plus)
|
||||
[]()
|
||||
[]()
|
||||
[]()
|
||||
@ -14,62 +20,95 @@
|
||||
> 项目代码、文档 均开源免费可商用 遵循开源协议在项目中保留开源协议文件即可<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-Vue-Plus | [RuoYi-Vue-Plus文档](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/pages) | 重写RuoYi-Vue全方位升级(不兼容原框架) |
|
||||
| 微服务分支 | 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分支 | RuoYi-Vue-Plus-UI | [UI地址](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus-UI) | 由于组件还未完善 仅供学习 |
|
||||
| 原框架 | RuoYi-Vue | [RuoYi-Vue官网](http://ruoyi.vip/) | 定期同步需要的功能 |
|
||||
| 前端开发框架 | Vue、Element UI | [Element UI官网](https://element.eleme.cn/#/zh-CN) | |
|
||||
| 后端开发框架 | SpringBoot | [SpringBoot官网](https://spring.io/projects/spring-boot/#learn) | |
|
||||
| 容器框架 | Undertow | [Undertow官网](https://undertow.io/) | 基于 XNIO 的高性能容器 |
|
||||
| 权限认证框架 | Sa-Token、Jwt | [Sa-Token官网](https://sa-token.dev33.cn/) | 强解耦、强扩展 |
|
||||
| 关系数据库 | MySQL | [MySQL官网](https://dev.mysql.com/) | 适配 8.X 最低 5.7 |
|
||||
| 关系数据库 | Oracle | [Oracle官网](https://www.oracle.com/cn/database/) | 适配 11g 12c |
|
||||
| 关系数据库 | PostgreSQL | [PostgreSQL官网](https://www.postgresql.org/) | 适配 13 14 |
|
||||
| 关系数据库 | SQLServer | [SQLServer官网](https://docs.microsoft.com/zh-cn/sql/sql-server) | 适配 2017 2019 |
|
||||
| 缓存数据库 | Redis | [Redis官网](https://redis.io/) | 适配 6.X 最低 4.X |
|
||||
| 数据库框架 | Mybatis-Plus | [Mybatis-Plus文档](https://baomidou.com/guide/) | 快速 CRUD 增加开发效率 |
|
||||
| 数据库框架 | p6spy | [p6spy官网](https://p6spy.readthedocs.io/) | 更强劲的 SQL 分析 |
|
||||
| 多数据源框架 | dynamic-datasource | [dynamic-ds文档](https://www.kancloud.cn/tracy5546/dynamic-datasource/content) | 支持主从与多种类数据库异构 |
|
||||
| 序列化框架 | Jackson | [Jackson官网](https://github.com/FasterXML/jackson) | 统一使用 jackson 高效可靠 |
|
||||
| Redis客户端 | Redisson | [Redisson文档](https://github.com/redisson/redisson/wiki/%E7%9B%AE%E5%BD%95) | 支持单机、集群配置 |
|
||||
| 分布式限流 | Redisson | [Redisson文档](https://github.com/redisson/redisson/wiki/%E7%9B%AE%E5%BD%95) | 全局、请求IP、集群ID 多种限流 |
|
||||
| 分布式队列 | Redisson | [Redisson文档](https://github.com/redisson/redisson/wiki/%E7%9B%AE%E5%BD%95) | 普通队列、延迟队列、优先队列 等 |
|
||||
| 分布式锁 | Lock4j | [Lock4j官网](https://gitee.com/baomidou/lock4j) | 注解锁、工具锁 多种多样 |
|
||||
| 分布式幂等 | Redisson | [Lock4j文档](https://gitee.com/baomidou/lock4j) | 拦截重复提交 |
|
||||
| 分布式链路追踪 | Apache SkyWalking | [Apache SkyWalking文档](https://skywalking.apache.org/docs/) | 链路追踪、网格分析、度量聚合、可视化 |
|
||||
| 分布式任务调度 | Xxl-Job | [Xxl-Job官网](https://www.xuxueli.com/xxl-job/) | 高性能 高可靠 易扩展 |
|
||||
| 文件存储 | Minio | [Minio文档](https://docs.min.io/) | 本地存储 |
|
||||
| 文件存储 | 七牛、阿里、腾讯 | [OSS使用文档](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/pages?sort_id=4359146&doc_id=1469725) | 云存储 |
|
||||
| 短信模块 | 阿里、腾讯 | [短信使用文档](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/pages?sort_id=5578491&doc_id=1469725) | 短信发送 |
|
||||
| 监控框架 | SpringBoot-Admin | [SpringBoot-Admin文档](https://codecentric.github.io/spring-boot-admin/current/) | 全方位服务监控 |
|
||||
| 校验框架 | Validation | [Validation文档](https://docs.jboss.org/hibernate/stable/validator/reference/en-US/html_single/) | 增强接口安全性、严谨性 支持国际化 |
|
||||
| Excel框架 | Alibaba EasyExcel | [EasyExcel文档](https://www.yuque.com/easyexcel/doc/easyexcel) | 性能优异 扩展性强 |
|
||||
| 文档框架 | SpringDoc、javadoc | [接口文档](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/pages?sort_id=5805266&doc_id=1469725) | 无注解零入侵基于java注释 |
|
||||
| 工具类框架 | Hutool、Lombok | [Hutool文档](https://www.hutool.cn/docs/) | 减少代码冗余 增加安全性 |
|
||||
| 代码生成器 | 适配MP、SpringDoc规范化代码 | [代码生成文档](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/pages?sort_id=5522329&doc_id=1469725) | 一键生成前后端代码 |
|
||||
| 部署方式 | Docker | [Docker文档](https://docs.docker.com/) | 容器编排 一键部署业务集群 |
|
||||
| 国际化 | SpringMessage | [SpringMVC文档](https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc) | Spring标准国际化方案 |
|
||||
# 本框架与RuoYi的功能差异
|
||||
|
||||
| 功能 | 本框架 | RuoYi |
|
||||
|-------------|-------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------|
|
||||
| 前端项目 | 基于vue3-element-admin开源项目重写<br/>Vue3 + TS + ElementPlus | 基于Vue2/Vue3 + JS |
|
||||
| 后端项目结构 | 采用插件化 + 扩展包形式 结构解耦 易于扩展 | 模块相互注入耦合严重难以扩展 |
|
||||
| 后端代码风格 | 严格遵守Alibaba规范与项目统一配置的代码格式化 | 代码书写与常规结构不同阅读障碍大 |
|
||||
| Web容器 | 采用 Undertow 基于 XNIO 的高性能容器 | 采用 Tomcat |
|
||||
| 权限认证 | 采用 Sa-Token、Jwt 静态使用功能齐全 低耦合 高扩展 | Spring Security 配置繁琐扩展性极差 |
|
||||
| 权限注解 | 采用 Sa-Token 支持注解 登录校验、角色校验、权限校验、二级认证校验、HttpBasic校验、忽略校验<br/>角色与权限校验支持多种条件 如 `AND` `OR` 或 `权限 OR 角色` 等复杂表达式 | 只支持是否存在匹配 |
|
||||
| 关系数据库支持 | 原生支持 MySQL、Oracle、PostgreSQL、SQLServer<br/>可同时使用异构切换 | 支持 Mysql、Oracle 不支持同时使用、不支持异构切换 |
|
||||
| 缓存数据库 | 支持 Redis 5-7 支持大部分新功能特性 如 分布式限流、分布式队列 | Redis 简单 get set 支持 |
|
||||
| Redis客户端 | 采用 Redisson Redis官方推荐 基于Netty的客户端工具<br/>支持Redis 90%以上的命令 底层优化规避很多不正确的用法 例如: keys被转换为scan<br/>支持单机、哨兵、单主集群、多主集群等模式 | Lettuce + RedisTemplate 支持模式少 工具使用繁琐<br/>连接池采用 common-pool Bug多经常性出问题 |
|
||||
| 缓存注解 | 采用 Spring-Cache 注解 对其扩展了实现支持了更多功能<br/>例如 过期时间 最大空闲时间 组最大长度等 只需一个注解即可完成数据自动缓存 | 需手动编写Redis代码逻辑 |
|
||||
| ORM框架 | 采用 Mybatis-Plus 基于对象几乎不用写SQL全java操作 功能强大插件众多<br/>例如多租户插件 分页插件 乐观锁插件等等 | 采用 Mybatis 基于XML需要手写SQL |
|
||||
| SQL监控 | 采用 p6spy 可输出完整SQL与执行时间监控 | log输出 需手动拼接sql与参数无法快速查看调试问题 |
|
||||
| 数据分页 | 采用 Mybatis-Plus 分页插件<br/>框架对其进行了扩展 对象化分页对象 支持多种方式传参 支持前端多排序 复杂排序 | 采用 PageHelper 仅支持单查询分页 参数只能从param传 只能单排序 功能扩展性差 体验不好 |
|
||||
| 数据权限 | 采用 Mybatis-Plus 插件 自行分析拼接SQL 无感式过滤<br/>只需为Mapper设置好注解条件 支持多种自定义 不限于部门角色 | 采用 注解+aop 实现 基于部门角色 生成的sql兼容性差 不支持其他业务扩展<br/>生成sql后需手动拼接到具体业务sql上 对于多个Mapper查询不起作用 |
|
||||
| 数据脱敏 | 采用 注解 + jackson 序列化期间脱敏 支持不同模块不同的脱敏条件<br/>支持多种策略 如身份证、手机号、地址、邮箱、银行卡等 可自行扩展 | 无 |
|
||||
| 数据加解密 | 采用 注解 + mybatis 拦截器 对存取数据期间自动加解密<br/>支持多种策略 如BASE64、AES、RSA、SM2、SM4等 | 无 |
|
||||
| 数据翻译 | 采用 注解 + jackson 序列化期间动态修改数据 数据进行翻译<br/>支持多种模式: `映射翻译` `直接翻译` `其他扩展条件翻译` 接口化两步即可完成自定义扩展 内置多种翻译实现 | 无 |
|
||||
| 多数据源框架 | 采用 dynamic-datasource 支持世面大部分数据库<br/>通过yml配置即可动态管理异构不同种类的数据库 也可通过前端页面添加数据源<br/>支持spel表达式从请求头参数等条件切换数据源 | 基于 druid 手动编写代码配置数据源 配置繁琐 支持性差 |
|
||||
| 多数据源事务 | 采用 dynamic-datasource 支持多数据源不同种类的数据库事务回滚 | 不支持 |
|
||||
| 数据库连接池 | 采用 HikariCP Spring官方内置连接池 配置简单 以性能与稳定性闻名天下 | 采用 druid bug众多 社区维护差 活跃度低 配置众多繁琐性能一般 |
|
||||
| 数据库主键 | 采用 雪花ID 基于时间戳的 有序增长 唯一ID 再也不用为分库分表 数据合并主键冲突重复而发愁 | 采用 数据库自增ID 支持数据量有限 不支持多数据源主键唯一 |
|
||||
| WebSocket协议 | 基于 Spring 封装的 WebSocket 协议 扩展了Token鉴权与分布式会话同步 不再只是基于单机的废物 | 无 |
|
||||
| 序列化 | 采用 Jackson Spring官方内置序列化 靠谱!!! | 采用 fastjson bugjson 远近闻名 |
|
||||
| 分布式幂等 | 参考美团GTIS防重系统简化实现(细节可看文档) | 手动编写注解基于aop实现 |
|
||||
| 分布式任务调度 | 采用 Xxl-Job 天生支持分布式 统一的管理中心 | 采用 Quartz 基于数据库锁性能差 集群需要做很多配置与改造 |
|
||||
| 文件存储 | 采用 Minio 分布式文件存储 天生支持多机、多硬盘、多分片、多副本存储<br/>支持权限管理 安全可靠 文件可加密存储 | 采用 本机文件存储 文件裸漏 易丢失泄漏 不支持集群有单点效应 |
|
||||
| 云存储 | 采用 AWS S3 协议客户端 支持 七牛、阿里、腾讯 等一切支持S3协议的厂家 | 不支持 |
|
||||
| 短信 | 支持 阿里、腾讯 只需在yml配置好厂家密钥即可使用 接口化支持扩展其他厂家 | 不支持 |
|
||||
| 邮件 | 采用 mail-api 通用协议支持大部分邮件厂商 | 不支持 |
|
||||
| 接口文档 | 采用 SpringDoc、javadoc 无注解零入侵基于java注释<br/>只需把注释写好 无需再写一大堆的文档注解了 | 采用 Springfox 已停止维护 需要编写大量的注解来支持文档生成 |
|
||||
| 校验框架 | 采用 Validation 支持注解与工具类校验 注解支持国际化 | 仅支持注解 且注解不支持国际化 |
|
||||
| Excel框架 | 采用 Alibaba EasyExcel 基于插件化<br/>框架对其增加了很多功能 例如 自动合并相同内容 自动排列布局 字典翻译等 | 基于 POI 手写实现 功能有限 复杂 扩展性差 |
|
||||
| 工具类框架 | 采用 Hutool、Lombok 上百种工具覆盖90%的使用需求 基于注解自动生成 get set 等简化框架大量代码 | 手写工具稳定性差易出问题 工具数量有限 代码臃肿需自己手写 get set 等 |
|
||||
| 监控框架 | 采用 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>
|
||||
>[初始化项目 必看](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/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/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/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/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/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](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)
|
||||
>>[https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/pages](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/pages)
|
||||
>[参考文档 Wiki](https://gitee.com/dromara/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
|
||||
* GitHub 地址 [RuoYi-Vue-Plus-github](https://github.com/JavaLionLi/RuoYi-Vue-Plus)
|
||||
* 单模块 分支 [RuoYi-Vue-Plus-fast](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/tree/fast/)
|
||||
* GitHub 地址 [RuoYi-Vue-Plus-github](https://github.com/dromara/RuoYi-Vue-Plus)
|
||||
* 单模块 分支 [RuoYi-Vue-Plus-fast](https://gitee.com/dromara/RuoYi-Vue-Plus/tree/fast/)
|
||||
* 微服务 分支 [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/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](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/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>
|
||||
| | |
|
||||
|--------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------|
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|
||||
|
||||
|
41
pom.xml
41
pom.xml
@ -6,45 +6,48 @@
|
||||
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<artifactId>ruoyi-vue-plus</artifactId>
|
||||
<version>4.5.0</version>
|
||||
<version>4.7.0</version>
|
||||
|
||||
<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>
|
||||
|
||||
<properties>
|
||||
<ruoyi-vue-plus.version>4.5.0</ruoyi-vue-plus.version>
|
||||
<spring-boot.version>2.7.7</spring-boot.version>
|
||||
<ruoyi-vue-plus.version>4.7.0</ruoyi-vue-plus.version>
|
||||
<spring-boot.version>2.7.11</spring-boot.version>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
<java.version>1.8</java.version>
|
||||
<maven-jar-plugin.version>3.2.2</maven-jar-plugin.version>
|
||||
<spring-boot.mybatis>2.2.2</spring-boot.mybatis>
|
||||
<springdoc.version>1.6.14</springdoc.version>
|
||||
<springdoc.version>1.6.15</springdoc.version>
|
||||
<poi.version>5.2.3</poi.version>
|
||||
<easyexcel.version>3.1.5</easyexcel.version>
|
||||
<easyexcel.version>3.2.1</easyexcel.version>
|
||||
<velocity.version>2.3</velocity.version>
|
||||
<satoken.version>1.34.0</satoken.version>
|
||||
<mybatis-plus.version>3.5.3.1</mybatis-plus.version>
|
||||
<p6spy.version>3.9.1</p6spy.version>
|
||||
<hutool.version>5.8.11</hutool.version>
|
||||
<hutool.version>5.8.18</hutool.version>
|
||||
<okhttp.version>4.10.0</okhttp.version>
|
||||
<spring-boot-admin.version>2.7.10</spring-boot-admin.version>
|
||||
<redisson.version>3.19.1</redisson.version>
|
||||
<redisson.version>3.20.1</redisson.version>
|
||||
<lock4j.version>2.2.3</lock4j.version>
|
||||
<dynamic-ds.version>3.5.2</dynamic-ds.version>
|
||||
<alibaba-ttl.version>2.14.2</alibaba-ttl.version>
|
||||
<xxl-job.version>2.3.1</xxl-job.version>
|
||||
<lombok.version>1.18.24</lombok.version>
|
||||
<xxl-job.version>2.4.0</xxl-job.version>
|
||||
<lombok.version>1.18.26</lombok.version>
|
||||
<bouncycastle.version>1.72</bouncycastle.version>
|
||||
<!-- 离线IP地址定位库 -->
|
||||
<ip2region.version>2.7.0</ip2region.version>
|
||||
|
||||
<!-- 临时修复 snakeyaml 漏洞 -->
|
||||
<snakeyaml.version>1.33</snakeyaml.version>
|
||||
|
||||
<!-- OSS 配置 -->
|
||||
<aws-java-sdk-s3.version>1.12.373</aws-java-sdk-s3.version>
|
||||
<aws-java-sdk-s3.version>1.12.400</aws-java-sdk-s3.version>
|
||||
<!-- SMS 配置 -->
|
||||
<aliyun.sms.version>2.0.23</aliyun.sms.version>
|
||||
<tencent.sms.version>3.1.660</tencent.sms.version>
|
||||
<tencent.sms.version>3.1.687</tencent.sms.version>
|
||||
</properties>
|
||||
|
||||
<profiles>
|
||||
@ -257,6 +260,13 @@
|
||||
<version>${alibaba-ttl.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 离线IP地址定位库 ip2region -->
|
||||
<dependency>
|
||||
<groupId>org.lionsoul</groupId>
|
||||
<artifactId>ip2region</artifactId>
|
||||
<version>${ip2region.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 临时修复 snakeyaml 漏洞 -->
|
||||
<dependency>
|
||||
<groupId>org.yaml</groupId>
|
||||
@ -264,6 +274,13 @@
|
||||
<version>${snakeyaml.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 加密包引入 -->
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk15to18</artifactId>
|
||||
<version>${bouncycastle.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 定时任务 -->
|
||||
<dependency>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
|
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>ruoyi-vue-plus</artifactId>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<version>4.5.0</version>
|
||||
<version>4.7.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<packaging>jar</packaging>
|
||||
|
@ -10,10 +10,12 @@ import com.ruoyi.common.constant.Constants;
|
||||
import com.ruoyi.common.core.domain.R;
|
||||
import com.ruoyi.common.enums.CaptchaType;
|
||||
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.reflect.ReflectUtils;
|
||||
import com.ruoyi.common.utils.spring.SpringUtils;
|
||||
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.core.SmsTemplate;
|
||||
import com.ruoyi.sms.entity.SmsResult;
|
||||
@ -47,6 +49,7 @@ public class CaptchaController {
|
||||
private final CaptchaProperties captchaProperties;
|
||||
private final SmsProperties smsProperties;
|
||||
private final ISysConfigService configService;
|
||||
private final MailProperties mailProperties;
|
||||
|
||||
/**
|
||||
* 短信验证码
|
||||
@ -54,8 +57,7 @@ public class CaptchaController {
|
||||
* @param phonenumber 用户手机号
|
||||
*/
|
||||
@GetMapping("/captchaSms")
|
||||
public R<Void> smsCaptcha(@NotBlank(message = "{user.phonenumber.not.blank}")
|
||||
String phonenumber) {
|
||||
public R<Void> smsCaptcha(@NotBlank(message = "{user.phonenumber.not.blank}") String phonenumber) {
|
||||
if (!smsProperties.getEnabled()) {
|
||||
return R.fail("当前系统没有开启短信功能!");
|
||||
}
|
||||
@ -75,6 +77,28 @@ public class CaptchaController {
|
||||
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();
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成验证码
|
||||
*/
|
||||
|
@ -33,7 +33,6 @@ public class CacheController {
|
||||
private final static List<SysCache> CACHES = new ArrayList<>();
|
||||
|
||||
static {
|
||||
CACHES.add(new SysCache(CacheConstants.LOGIN_TOKEN_KEY, "用户信息"));
|
||||
CACHES.add(new SysCache(CacheConstants.ONLINE_TOKEN_KEY, "在线用户"));
|
||||
CACHES.add(new SysCache(CacheNames.SYS_CONFIG, "配置信息"));
|
||||
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<UserOnlineDTO> userOnlineDTOList = new ArrayList<>();
|
||||
for (String key : keys) {
|
||||
String token = key.replace(CacheConstants.LOGIN_TOKEN_KEY, "");
|
||||
String token = StringUtils.substringAfterLast(key, ":");
|
||||
// 如果已经过期则跳过
|
||||
if (StpUtil.stpLogic.getTokenActivityTimeoutByToken(token) < -1) {
|
||||
continue;
|
||||
|
@ -79,7 +79,7 @@ public class SysConfigController extends BaseController {
|
||||
@Log(title = "参数管理", businessType = BusinessType.INSERT)
|
||||
@PostMapping
|
||||
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() + "'失败,参数键名已存在");
|
||||
}
|
||||
configService.insertConfig(config);
|
||||
@ -93,7 +93,7 @@ public class SysConfigController extends BaseController {
|
||||
@Log(title = "参数管理", businessType = BusinessType.UPDATE)
|
||||
@PutMapping
|
||||
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() + "'失败,参数键名已存在");
|
||||
}
|
||||
configService.updateConfig(config);
|
||||
|
@ -1,7 +1,7 @@
|
||||
package com.ruoyi.web.controller.system;
|
||||
|
||||
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.constant.UserConstants;
|
||||
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) {
|
||||
List<SysDept> depts = deptService.selectDeptList(new SysDept());
|
||||
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);
|
||||
}
|
||||
|
||||
@ -72,7 +72,7 @@ public class SysDeptController extends BaseController {
|
||||
@Log(title = "部门管理", businessType = BusinessType.INSERT)
|
||||
@PostMapping
|
||||
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 toAjax(deptService.insertDept(dept));
|
||||
@ -87,7 +87,7 @@ public class SysDeptController extends BaseController {
|
||||
public R<Void> edit(@Validated @RequestBody SysDept dept) {
|
||||
Long deptId = dept.getDeptId();
|
||||
deptService.checkDeptDataScope(deptId);
|
||||
if (UserConstants.NOT_UNIQUE.equals(deptService.checkDeptNameUnique(dept))) {
|
||||
if (!deptService.checkDeptNameUnique(dept)) {
|
||||
return R.fail("修改部门'" + dept.getDeptName() + "'失败,部门名称已存在");
|
||||
} else if (dept.getParentId().equals(deptId)) {
|
||||
return R.fail("修改部门'" + dept.getDeptName() + "'失败,上级部门不能是自己");
|
||||
|
@ -69,7 +69,7 @@ public class SysDictTypeController extends BaseController {
|
||||
@Log(title = "字典类型", businessType = BusinessType.INSERT)
|
||||
@PostMapping
|
||||
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() + "'失败,字典类型已存在");
|
||||
}
|
||||
dictTypeService.insertDictType(dict);
|
||||
@ -83,7 +83,7 @@ public class SysDictTypeController extends BaseController {
|
||||
@Log(title = "字典类型", businessType = BusinessType.UPDATE)
|
||||
@PutMapping
|
||||
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() + "'失败,字典类型已存在");
|
||||
}
|
||||
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.entity.SysMenu;
|
||||
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.LoginUser;
|
||||
import com.ruoyi.common.core.domain.model.SmsLoginBody;
|
||||
@ -57,7 +58,7 @@ public class SysLoginController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 短信登录(示例)
|
||||
* 短信登录
|
||||
*
|
||||
* @param smsLoginBody 登录信息
|
||||
* @return 结果
|
||||
@ -72,6 +73,21 @@ public class SysLoginController {
|
||||
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)
|
||||
@PostMapping
|
||||
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() + "'失败,菜单名称已存在");
|
||||
} else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath())) {
|
||||
return R.fail("新增菜单'" + menu.getMenuName() + "'失败,地址必须以http(s)://开头");
|
||||
@ -97,7 +97,7 @@ public class SysMenuController extends BaseController {
|
||||
@Log(title = "菜单管理", businessType = BusinessType.UPDATE)
|
||||
@PutMapping
|
||||
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() + "'失败,菜单名称已存在");
|
||||
} else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath())) {
|
||||
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.hutool.core.convert.Convert;
|
||||
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.core.controller.BaseController;
|
||||
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.validate.QueryGroup;
|
||||
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.vo.SysOssVo;
|
||||
import com.ruoyi.system.service.ISysOssService;
|
||||
@ -80,7 +72,7 @@ public class SysOssController extends BaseController {
|
||||
@PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
|
||||
public R<Map<String, String>> upload(@RequestPart("file") MultipartFile file) {
|
||||
if (ObjectUtil.isNull(file)) {
|
||||
throw new ServiceException("上传文件不能为空");
|
||||
return R.fail("上传文件不能为空");
|
||||
}
|
||||
SysOssVo oss = iSysOssService.upload(file);
|
||||
Map<String, String> map = new HashMap<>(2);
|
||||
|
@ -69,9 +69,9 @@ public class SysPostController extends BaseController {
|
||||
@Log(title = "岗位管理", businessType = BusinessType.INSERT)
|
||||
@PostMapping
|
||||
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() + "'失败,岗位名称已存在");
|
||||
} else if (UserConstants.NOT_UNIQUE.equals(postService.checkPostCodeUnique(post))) {
|
||||
} else if (!postService.checkPostCodeUnique(post)) {
|
||||
return R.fail("新增岗位'" + post.getPostName() + "'失败,岗位编码已存在");
|
||||
}
|
||||
return toAjax(postService.insertPost(post));
|
||||
@ -84,9 +84,9 @@ public class SysPostController extends BaseController {
|
||||
@Log(title = "岗位管理", businessType = BusinessType.UPDATE)
|
||||
@PutMapping
|
||||
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() + "'失败,岗位名称已存在");
|
||||
} else if (UserConstants.NOT_UNIQUE.equals(postService.checkPostCodeUnique(post))) {
|
||||
} else if (!postService.checkPostCodeUnique(post)) {
|
||||
return R.fail("修改岗位'" + post.getPostName() + "'失败,岗位编码已存在");
|
||||
}
|
||||
return toAjax(postService.updatePost(post));
|
||||
|
@ -58,12 +58,10 @@ public class SysProfileController extends BaseController {
|
||||
@Log(title = "个人信息", businessType = BusinessType.UPDATE)
|
||||
@PutMapping
|
||||
public R<Void> updateProfile(@RequestBody SysUser user) {
|
||||
if (StringUtils.isNotEmpty(user.getPhonenumber())
|
||||
&& UserConstants.NOT_UNIQUE.equals(userService.checkPhoneUnique(user))) {
|
||||
if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) {
|
||||
return R.fail("修改用户'" + user.getUserName() + "'失败,手机号码已存在");
|
||||
}
|
||||
if (StringUtils.isNotEmpty(user.getEmail())
|
||||
&& UserConstants.NOT_UNIQUE.equals(userService.checkEmailUnique(user))) {
|
||||
if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user)) {
|
||||
return R.fail("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在");
|
||||
}
|
||||
user.setUserId(getUserId());
|
||||
|
@ -1,19 +1,15 @@
|
||||
package com.ruoyi.web.controller.system;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.ruoyi.common.annotation.Log;
|
||||
import com.ruoyi.common.constant.UserConstants;
|
||||
import com.ruoyi.common.core.controller.BaseController;
|
||||
import com.ruoyi.common.core.domain.PageQuery;
|
||||
import com.ruoyi.common.core.domain.R;
|
||||
import com.ruoyi.common.core.domain.entity.SysDept;
|
||||
import com.ruoyi.common.core.domain.entity.SysRole;
|
||||
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.enums.BusinessType;
|
||||
import com.ruoyi.common.helper.LoginHelper;
|
||||
import com.ruoyi.common.utils.poi.ExcelUtil;
|
||||
import com.ruoyi.system.domain.SysUserRole;
|
||||
import com.ruoyi.system.service.ISysDeptService;
|
||||
@ -84,9 +80,9 @@ public class SysRoleController extends BaseController {
|
||||
@Log(title = "角色管理", businessType = BusinessType.INSERT)
|
||||
@PostMapping
|
||||
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() + "'失败,角色名称已存在");
|
||||
} else if (UserConstants.NOT_UNIQUE.equals(roleService.checkRoleKeyUnique(role))) {
|
||||
} else if (!roleService.checkRoleKeyUnique(role)) {
|
||||
return R.fail("新增角色'" + role.getRoleName() + "'失败,角色权限已存在");
|
||||
}
|
||||
return toAjax(roleService.insertRole(role));
|
||||
@ -102,20 +98,14 @@ public class SysRoleController extends BaseController {
|
||||
public R<Void> edit(@Validated @RequestBody SysRole role) {
|
||||
roleService.checkRoleAllowed(role);
|
||||
roleService.checkRoleDataScope(role.getRoleId());
|
||||
if (UserConstants.NOT_UNIQUE.equals(roleService.checkRoleNameUnique(role))) {
|
||||
if (!roleService.checkRoleNameUnique(role)) {
|
||||
return R.fail("修改角色'" + role.getRoleName() + "'失败,角色名称已存在");
|
||||
} else if (UserConstants.NOT_UNIQUE.equals(roleService.checkRoleKeyUnique(role))) {
|
||||
} else if (!roleService.checkRoleKeyUnique(role)) {
|
||||
return R.fail("修改角色'" + role.getRoleName() + "'失败,角色权限已存在");
|
||||
}
|
||||
|
||||
if (roleService.updateRole(role) > 0) {
|
||||
// 更新缓存用户权限
|
||||
LoginUser loginUser = getLoginUser();
|
||||
SysUser sysUser = userService.selectUserById(loginUser.getUserId());
|
||||
if (ObjectUtil.isNotNull(sysUser) && !sysUser.isAdmin()) {
|
||||
loginUser.setMenuPermission(permissionService.getMenuPermission(sysUser));
|
||||
LoginHelper.setLoginUser(loginUser);
|
||||
}
|
||||
roleService.cleanOnlineUserByRole(role.getRoleId());
|
||||
return R.ok();
|
||||
}
|
||||
return R.fail("修改角色'" + role.getRoleName() + "'失败,请联系管理员");
|
||||
|
@ -136,13 +136,11 @@ public class SysUserController extends BaseController {
|
||||
@Log(title = "用户管理", businessType = BusinessType.INSERT)
|
||||
@PostMapping
|
||||
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() + "'失败,登录账号已存在");
|
||||
} else if (StringUtils.isNotEmpty(user.getPhonenumber())
|
||||
&& UserConstants.NOT_UNIQUE.equals(userService.checkPhoneUnique(user))) {
|
||||
} else if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) {
|
||||
return R.fail("新增用户'" + user.getUserName() + "'失败,手机号码已存在");
|
||||
} else if (StringUtils.isNotEmpty(user.getEmail())
|
||||
&& UserConstants.NOT_UNIQUE.equals(userService.checkEmailUnique(user))) {
|
||||
} else if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user)) {
|
||||
return R.fail("新增用户'" + user.getUserName() + "'失败,邮箱账号已存在");
|
||||
}
|
||||
user.setPassword(BCrypt.hashpw(user.getPassword()));
|
||||
@ -158,13 +156,11 @@ public class SysUserController extends BaseController {
|
||||
public R<Void> edit(@Validated @RequestBody SysUser user) {
|
||||
userService.checkUserAllowed(user);
|
||||
userService.checkUserDataScope(user.getUserId());
|
||||
if (UserConstants.NOT_UNIQUE.equals(userService.checkUserNameUnique(user))) {
|
||||
if (!userService.checkUserNameUnique(user)) {
|
||||
return R.fail("修改用户'" + user.getUserName() + "'失败,登录账号已存在");
|
||||
} else if (StringUtils.isNotEmpty(user.getPhonenumber())
|
||||
&& UserConstants.NOT_UNIQUE.equals(userService.checkPhoneUnique(user))) {
|
||||
} else if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) {
|
||||
return R.fail("修改用户'" + user.getUserName() + "'失败,手机号码已存在");
|
||||
} else if (StringUtils.isNotEmpty(user.getEmail())
|
||||
&& UserConstants.NOT_UNIQUE.equals(userService.checkEmailUnique(user))) {
|
||||
} else if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user)) {
|
||||
return R.fail("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在");
|
||||
}
|
||||
return toAjax(userService.updateUser(user));
|
||||
|
@ -86,15 +86,17 @@ spring:
|
||||
# 最小空闲线程数量
|
||||
minIdle: 10
|
||||
# 配置获取连接等待超时的时间
|
||||
connectionTimeout: 10000
|
||||
connectionTimeout: 30000
|
||||
# 校验超时时间
|
||||
validationTimeout: 5000
|
||||
# 空闲连接存活最大时间,默认10分钟
|
||||
idleTimeout: 60000
|
||||
idleTimeout: 600000
|
||||
# 此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认30分钟
|
||||
maxLifetime: 900000
|
||||
maxLifetime: 1800000
|
||||
# 连接测试query(配置检测连接是否有效)
|
||||
connectionTestQuery: SELECT 1
|
||||
# 多久检查一次连接的活性
|
||||
keepaliveTime: 30000
|
||||
|
||||
--- # redis 单机配置(单机与集群只能开启一个另一个需要注释掉)
|
||||
spring:
|
||||
|
@ -89,15 +89,17 @@ spring:
|
||||
# 最小空闲线程数量
|
||||
minIdle: 10
|
||||
# 配置获取连接等待超时的时间
|
||||
connectionTimeout: 10000
|
||||
connectionTimeout: 30000
|
||||
# 校验超时时间
|
||||
validationTimeout: 5000
|
||||
# 空闲连接存活最大时间,默认10分钟
|
||||
idleTimeout: 60000
|
||||
idleTimeout: 600000
|
||||
# 此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认30分钟
|
||||
maxLifetime: 900000
|
||||
maxLifetime: 1800000
|
||||
# 连接测试query(配置检测连接是否有效)
|
||||
connectionTestQuery: SELECT 1
|
||||
# 多久检查一次连接的活性
|
||||
keepaliveTime: 30000
|
||||
|
||||
--- # redis 单机配置(单机与集群只能开启一个另一个需要注释掉)
|
||||
spring:
|
||||
|
@ -51,7 +51,7 @@ logging:
|
||||
level:
|
||||
com.ruoyi: @logging.level@
|
||||
org.springframework: warn
|
||||
config: classpath:logback.xml
|
||||
config: classpath:logback-plus.xml
|
||||
|
||||
# 用户配置
|
||||
user:
|
||||
@ -128,8 +128,10 @@ security:
|
||||
- /**/*.html
|
||||
- /**/*.css
|
||||
- /**/*.js
|
||||
# swagger 文档配置
|
||||
# 公共路径
|
||||
- /favicon.ico
|
||||
- /error
|
||||
# swagger 文档配置
|
||||
- /*/api-docs
|
||||
- /*/api-docs/**
|
||||
# actuator 监控配置
|
||||
@ -180,10 +182,22 @@ mybatis-plus:
|
||||
# 字段验证策略之 select,在 select 的时候的字段验证策略既 wrapper 根据内部 entity 生成的 where 条件
|
||||
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
|
||||
enabled: true
|
||||
info:
|
||||
# 标题
|
||||
title: '标题:${ruoyi.name}后台管理系统_接口文档'
|
||||
@ -195,7 +209,7 @@ swagger:
|
||||
contact:
|
||||
name: Lion Li
|
||||
email: crazylionli@163.com
|
||||
url: https://gitee.com/JavaLionLi/RuoYi-Vue-Plus
|
||||
url: https://gitee.com/dromara/RuoYi-Vue-Plus
|
||||
components:
|
||||
# 鉴权方式配置
|
||||
security-schemes:
|
||||
@ -205,6 +219,9 @@ swagger:
|
||||
name: ${sa-token.token-name}
|
||||
|
||||
springdoc:
|
||||
api-docs:
|
||||
# 是否开启接口文档
|
||||
enabled: true
|
||||
swagger-ui:
|
||||
# 持久化认证数据
|
||||
persistAuthorization: true
|
||||
|
@ -18,6 +18,7 @@ user.password.not.blank=用户密码不能为空
|
||||
user.password.length.valid=用户密码长度必须在{min}到{max}个字符之间
|
||||
user.password.not.valid=* 5-50个字符
|
||||
user.email.not.valid=邮箱格式错误
|
||||
user.email.not.blank=邮箱不能为空
|
||||
user.phonenumber.not.blank=用户手机号不能为空
|
||||
user.mobile.phone.number.not.valid=手机号格式错误
|
||||
user.login.success=登录成功
|
||||
@ -42,4 +43,7 @@ rate.limiter.message=访问过于频繁,请稍候再试
|
||||
sms.code.not.blank=短信验证码不能为空
|
||||
sms.code.retry.limit.count=短信验证码输入错误{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不能为空
|
||||
|
@ -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.not.valid=* 5-50 characters
|
||||
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.mobile.phone.number.not.valid=Phone number format error
|
||||
user.login.success=Login successful
|
||||
@ -42,4 +43,7 @@ rate.limiter.message=Visit too frequently, please try again later
|
||||
sms.code.not.blank=Sms code cannot be blank
|
||||
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 {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
|
||||
|
@ -18,6 +18,7 @@ user.password.not.blank=用户密码不能为空
|
||||
user.password.length.valid=用户密码长度必须在{min}到{max}个字符之间
|
||||
user.password.not.valid=* 5-50个字符
|
||||
user.email.not.valid=邮箱格式错误
|
||||
user.email.not.blank=邮箱不能为空
|
||||
user.phonenumber.not.blank=用户手机号不能为空
|
||||
user.mobile.phone.number.not.valid=手机号格式错误
|
||||
user.login.success=登录成功
|
||||
@ -42,4 +43,7 @@ rate.limiter.message=访问过于频繁,请稍候再试
|
||||
sms.code.not.blank=短信验证码不能为空
|
||||
sms.code.retry.limit.count=短信验证码输入错误{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不能为空
|
||||
|
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>
|
||||
<artifactId>ruoyi-vue-plus</artifactId>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<version>4.5.0</version>
|
||||
<version>4.7.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@ -153,6 +153,18 @@
|
||||
<artifactId>lock4j-redisson-spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 加密包引入 -->
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk15to18</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 离线IP地址定位库 -->
|
||||
<dependency>
|
||||
<groupId>org.lionsoul</groupId>
|
||||
<artifactId>ip2region</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
@ -13,7 +13,9 @@ import java.lang.annotation.Target;
|
||||
* 字典数据映射注解
|
||||
*
|
||||
* @author itino
|
||||
* @deprecated 建议使用通用翻译注解
|
||||
*/
|
||||
@Deprecated
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.FIELD, ElementType.METHOD})
|
||||
@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;
|
||||
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* 排除指定的请求参数
|
||||
*/
|
||||
String[] excludeParamNames() default {};
|
||||
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
package com.ruoyi.common.annotation;
|
||||
|
||||
import com.ruoyi.common.constant.CacheConstants;
|
||||
import com.ruoyi.common.enums.LimitType;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
@ -15,9 +14,10 @@ import java.lang.annotation.*;
|
||||
@Documented
|
||||
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;
|
||||
|
||||
/**
|
||||
* 提示消息 支持国际化 格式为 {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 {
|
||||
|
||||
/**
|
||||
* 登录用户 redis key
|
||||
*/
|
||||
String LOGIN_TOKEN_KEY = "Authorization:login:token:";
|
||||
|
||||
/**
|
||||
* 在线用户 redis key
|
||||
*/
|
||||
|
@ -30,6 +30,16 @@ public interface CacheNames {
|
||||
*/
|
||||
String SYS_DICT = "sys_dict";
|
||||
|
||||
/**
|
||||
* 用户账户
|
||||
*/
|
||||
String SYS_USER_NAME = "sys_user_name#30d";
|
||||
|
||||
/**
|
||||
* 部门
|
||||
*/
|
||||
String SYS_DEPT = "sys_dept#30d";
|
||||
|
||||
/**
|
||||
* 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 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"});
|
||||
|
||||
String[] orderByArr = orderBy.split(",");
|
||||
String[] isAscArr = isAsc.split(",");
|
||||
String[] orderByArr = orderBy.split(StringUtils.SEPARATOR);
|
||||
String[] isAscArr = isAsc.split(StringUtils.SEPARATOR);
|
||||
if (isAscArr.length != 1 && isAscArr.length != orderByArr.length) {
|
||||
throw new ServiceException("排序参数有误");
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ public class SysDept extends TreeEntity<SysDept> {
|
||||
* 部门名称
|
||||
*/
|
||||
@NotBlank(message = "部门名称不能为空")
|
||||
@Size(min = 0, max = 30, message = "部门名称长度不能超过30个字符")
|
||||
@Size(min = 0, max = 30, message = "部门名称长度不能超过{max}个字符")
|
||||
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;
|
||||
|
||||
/**
|
||||
* 邮箱
|
||||
*/
|
||||
@Email(message = "邮箱格式不正确")
|
||||
@Size(min = 0, max = 50, message = "邮箱长度不能超过50个字符")
|
||||
@Size(min = 0, max = 50, message = "邮箱长度不能超过{max}个字符")
|
||||
private String email;
|
||||
|
||||
/**
|
||||
|
@ -44,7 +44,7 @@ public class SysDictData extends BaseEntity {
|
||||
*/
|
||||
@ExcelProperty(value = "字典标签")
|
||||
@NotBlank(message = "字典标签不能为空")
|
||||
@Size(min = 0, max = 100, message = "字典标签长度不能超过100个字符")
|
||||
@Size(min = 0, max = 100, message = "字典标签长度不能超过{max}个字符")
|
||||
private String dictLabel;
|
||||
|
||||
/**
|
||||
@ -52,7 +52,7 @@ public class SysDictData extends BaseEntity {
|
||||
*/
|
||||
@ExcelProperty(value = "字典键值")
|
||||
@NotBlank(message = "字典键值不能为空")
|
||||
@Size(min = 0, max = 100, message = "字典键值长度不能超过100个字符")
|
||||
@Size(min = 0, max = 100, message = "字典键值长度不能超过{max}个字符")
|
||||
private String dictValue;
|
||||
|
||||
/**
|
||||
@ -60,13 +60,13 @@ public class SysDictData extends BaseEntity {
|
||||
*/
|
||||
@ExcelProperty(value = "字典类型")
|
||||
@NotBlank(message = "字典类型不能为空")
|
||||
@Size(min = 0, max = 100, message = "字典类型长度不能超过100个字符")
|
||||
@Size(min = 0, max = 100, message = "字典类型长度不能超过{max}个字符")
|
||||
private String dictType;
|
||||
|
||||
/**
|
||||
* 样式属性(其他样式扩展)
|
||||
*/
|
||||
@Size(min = 0, max = 100, message = "样式属性长度不能超过100个字符")
|
||||
@Size(min = 0, max = 100, message = "样式属性长度不能超过{max}个字符")
|
||||
private String cssClass;
|
||||
|
||||
/**
|
||||
|
@ -38,7 +38,7 @@ public class SysDictType extends BaseEntity {
|
||||
*/
|
||||
@ExcelProperty(value = "字典名称")
|
||||
@NotBlank(message = "字典名称不能为空")
|
||||
@Size(min = 0, max = 100, message = "字典类型名称长度不能超过100个字符")
|
||||
@Size(min = 0, max = 100, message = "字典类型名称长度不能超过{max}个字符")
|
||||
private String dictName;
|
||||
|
||||
/**
|
||||
@ -46,7 +46,7 @@ public class SysDictType extends BaseEntity {
|
||||
*/
|
||||
@ExcelProperty(value = "字典类型")
|
||||
@NotBlank(message = "字典类型不能为空")
|
||||
@Size(min = 0, max = 100, message = "字典类型类型长度不能超过100个字符")
|
||||
@Size(min = 0, max = 100, message = "字典类型类型长度不能超过{max}个字符")
|
||||
@Pattern(regexp = "^[a-z][a-z0-9_]*$", message = "字典类型必须以字母开头,且只能为(小写字母,数字,下滑线)")
|
||||
private String dictType;
|
||||
|
||||
|
@ -32,7 +32,7 @@ public class SysMenu extends TreeEntity<SysMenu> {
|
||||
* 菜单名称
|
||||
*/
|
||||
@NotBlank(message = "菜单名称不能为空")
|
||||
@Size(min = 0, max = 50, message = "菜单名称长度不能超过50个字符")
|
||||
@Size(min = 0, max = 50, message = "菜单名称长度不能超过{max}个字符")
|
||||
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;
|
||||
|
||||
/**
|
||||
* 组件路径
|
||||
*/
|
||||
@Size(min = 0, max = 200, message = "组件路径不能超过255个字符")
|
||||
@Size(min = 0, max = 200, message = "组件路径不能超过{max}个字符")
|
||||
private String component;
|
||||
|
||||
/**
|
||||
@ -88,7 +88,7 @@ public class SysMenu extends TreeEntity<SysMenu> {
|
||||
* 权限字符串
|
||||
*/
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
@Size(min = 0, max = 100, message = "权限标识长度不能超过100个字符")
|
||||
@Size(min = 0, max = 100, message = "权限标识长度不能超过{max}个字符")
|
||||
private String perms;
|
||||
|
||||
/**
|
||||
|
@ -17,7 +17,6 @@ import lombok.NoArgsConstructor;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Size;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 角色表 sys_role
|
||||
@ -44,7 +43,7 @@ public class SysRole extends BaseEntity {
|
||||
*/
|
||||
@ExcelProperty(value = "角色名称")
|
||||
@NotBlank(message = "角色名称不能为空")
|
||||
@Size(min = 0, max = 30, message = "角色名称长度不能超过30个字符")
|
||||
@Size(min = 0, max = 30, message = "角色名称长度不能超过{max}个字符")
|
||||
private String roleName;
|
||||
|
||||
/**
|
||||
@ -52,7 +51,7 @@ public class SysRole extends BaseEntity {
|
||||
*/
|
||||
@ExcelProperty(value = "角色权限")
|
||||
@NotBlank(message = "权限字符不能为空")
|
||||
@Size(min = 0, max = 100, message = "权限字符长度不能超过100个字符")
|
||||
@Size(min = 0, max = 100, message = "权限字符长度不能超过{max}个字符")
|
||||
private String roleKey;
|
||||
|
||||
/**
|
||||
@ -115,12 +114,6 @@ public class SysRole extends BaseEntity {
|
||||
@TableField(exist = false)
|
||||
private Long[] deptIds;
|
||||
|
||||
/**
|
||||
* 角色菜单权限
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
private Set<String> permissions;
|
||||
|
||||
public SysRole(Long roleId) {
|
||||
this.roleId = roleId;
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
package com.ruoyi.common.core.domain.entity;
|
||||
|
||||
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.constant.UserConstants;
|
||||
import com.ruoyi.common.core.domain.BaseEntity;
|
||||
@ -44,14 +46,14 @@ public class SysUser extends BaseEntity {
|
||||
*/
|
||||
@Xss(message = "用户账号不能包含脚本字符")
|
||||
@NotBlank(message = "用户账号不能为空")
|
||||
@Size(min = 0, max = 30, message = "用户账号长度不能超过30个字符")
|
||||
@Size(min = 0, max = 30, message = "用户账号长度不能超过{max}个字符")
|
||||
private String userName;
|
||||
|
||||
/**
|
||||
* 用户昵称
|
||||
*/
|
||||
@Xss(message = "用户昵称不能包含脚本字符")
|
||||
@Size(min = 0, max = 30, message = "用户昵称长度不能超过30个字符")
|
||||
@Size(min = 0, max = 30, message = "用户昵称长度不能超过{max}个字符")
|
||||
private String nickName;
|
||||
|
||||
/**
|
||||
@ -64,7 +66,7 @@ public class SysUser extends BaseEntity {
|
||||
*/
|
||||
@Sensitive(strategy = SensitiveStrategy.EMAIL)
|
||||
@Email(message = "邮箱格式不正确")
|
||||
@Size(min = 0, max = 50, message = "邮箱长度不能超过50个字符")
|
||||
@Size(min = 0, max = 50, message = "邮箱长度不能超过{max}个字符")
|
||||
private String email;
|
||||
|
||||
/**
|
||||
@ -93,6 +95,12 @@ public class SysUser extends BaseEntity {
|
||||
)
|
||||
private String password;
|
||||
|
||||
@JsonIgnore
|
||||
@JsonProperty
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
/**
|
||||
* 帐号状态(0正常 1停用)
|
||||
*/
|
||||
|
@ -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;
|
||||
|
||||
import com.ruoyi.common.core.domain.dto.RoleDTO;
|
||||
import com.ruoyi.common.helper.LoginHelper;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@ -111,7 +110,7 @@ public class LoginUser implements Serializable {
|
||||
if (userId == null) {
|
||||
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 {
|
||||
|
||||
/**
|
||||
* 用户名
|
||||
* 手机号
|
||||
*/
|
||||
@NotBlank(message = "{user.phonenumber.not.blank}")
|
||||
private String phonenumber;
|
||||
|
||||
/**
|
||||
* 用户密码
|
||||
* 短信code
|
||||
*/
|
||||
@NotBlank(message = "{sms.code.not.blank}")
|
||||
private String smsCode;
|
||||
|
@ -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);
|
||||
|
||||
}
|
@ -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"),
|
||||
|
||||
/**
|
||||
* 邮箱登录
|
||||
*/
|
||||
EMAIL("email.code.retry.limit.exceed", "email.code.retry.limit.count"),
|
||||
|
||||
/**
|
||||
* 小程序登录
|
||||
*/
|
||||
|
@ -42,7 +42,7 @@ public class DefaultExcelListener<T> extends AnalysisEventListener<T> implements
|
||||
private ExcelResult<T> excelResult;
|
||||
|
||||
public DefaultExcelListener(boolean isValidate) {
|
||||
this.excelResult = new DefautExcelResult<>();
|
||||
this.excelResult = new DefaultExcelResult<>();
|
||||
this.isValidate = isValidate;
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,7 @@ import java.util.List;
|
||||
* @author Yjoioooo
|
||||
* @author Lion Li
|
||||
*/
|
||||
public class DefautExcelResult<T> implements ExcelResult<T> {
|
||||
public class DefaultExcelResult<T> implements ExcelResult<T> {
|
||||
|
||||
/**
|
||||
* 数据对象list
|
||||
@ -26,17 +26,17 @@ public class DefautExcelResult<T> implements ExcelResult<T> {
|
||||
@Setter
|
||||
private List<String> errorList;
|
||||
|
||||
public DefautExcelResult() {
|
||||
public DefaultExcelResult() {
|
||||
this.list = 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.errorList = errorList;
|
||||
}
|
||||
|
||||
public DefautExcelResult(ExcelResult<T> excelResult) {
|
||||
public DefaultExcelResult(ExcelResult<T> excelResult) {
|
||||
this.list = excelResult.getList();
|
||||
this.errorList = excelResult.getErrorList();
|
||||
}
|
@ -25,7 +25,7 @@ public class XssFilter implements Filter {
|
||||
public void init(FilterConfig filterConfig) throws ServletException {
|
||||
String tempExcludes = filterConfig.getInitParameter("excludes");
|
||||
if (StringUtils.isNotEmpty(tempExcludes)) {
|
||||
String[] url = tempExcludes.split(",");
|
||||
String[] url = tempExcludes.split(StringUtils.SEPARATOR);
|
||||
for (int i = 0; url != null && i < url.length; i++) {
|
||||
excludes.add(url[i]);
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ public class DataBaseHelper {
|
||||
// instr(',0,100,101,' , ',100,') <> 0
|
||||
return "instr(','||" + var2 + "||',' , '," + var + ",') <> 0";
|
||||
}
|
||||
// find_in_set(100 , '0,100,101')
|
||||
return "find_in_set(" + var + " , " + var2 + ") <> 0";
|
||||
// find_in_set('100' , '0,100,101')
|
||||
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.model.SaStorage;
|
||||
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.NoArgsConstructor;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* 数据权限助手
|
||||
@ -44,4 +47,47 @@ public class DataPermissionHelper {
|
||||
}
|
||||
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;
|
||||
|
||||
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.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.ruoyi.common.constant.UserConstants;
|
||||
import com.ruoyi.common.core.domain.model.LoginUser;
|
||||
import com.ruoyi.common.enums.DeviceType;
|
||||
import com.ruoyi.common.enums.UserType;
|
||||
import com.ruoyi.common.exception.UtilException;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 登录鉴权助手
|
||||
*
|
||||
* <p>
|
||||
* user_type 为 用户类型 同一个用户表 可以有多种用户类型 例如 pc,app
|
||||
* deivce 为 设备类型 同一个用户类型 可以有 多种设备类型 例如 web,ios
|
||||
* 可以组成 用户类型与设备类型多对多的 权限灵活控制
|
||||
*
|
||||
* <p>
|
||||
* 多用户体系 针对 多种用户类型 但权限控制不一致
|
||||
* 可以组成 多用户类型表与多设备类型 分别控制权限
|
||||
*
|
||||
@ -28,8 +28,8 @@ import lombok.NoArgsConstructor;
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class LoginHelper {
|
||||
|
||||
public static final String JOIN_CODE = ":";
|
||||
public static final String LOGIN_USER_KEY = "loginUser";
|
||||
public static final String USER_KEY = "userId";
|
||||
|
||||
/**
|
||||
* 登录系统
|
||||
@ -37,9 +37,7 @@ public class LoginHelper {
|
||||
* @param loginUser 登录用户信息
|
||||
*/
|
||||
public static void login(LoginUser loginUser) {
|
||||
SaHolder.getStorage().set(LOGIN_USER_KEY, loginUser);
|
||||
StpUtil.login(loginUser.getLoginId());
|
||||
setLoginUser(loginUser);
|
||||
loginByDevice(loginUser, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -49,15 +47,14 @@ public class LoginHelper {
|
||||
* @param loginUser 登录用户信息
|
||||
*/
|
||||
public static void loginByDevice(LoginUser loginUser, DeviceType deviceType) {
|
||||
SaHolder.getStorage().set(LOGIN_USER_KEY, loginUser);
|
||||
StpUtil.login(loginUser.getLoginId(), deviceType.getDevice());
|
||||
setLoginUser(loginUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置用户数据(多级缓存)
|
||||
*/
|
||||
public static void setLoginUser(LoginUser loginUser) {
|
||||
SaStorage storage = SaHolder.getStorage();
|
||||
storage.set(LOGIN_USER_KEY, 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()));
|
||||
StpUtil.getTokenSession().set(LOGIN_USER_KEY, loginUser);
|
||||
}
|
||||
|
||||
@ -74,27 +71,28 @@ public class LoginHelper {
|
||||
return loginUser;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户基于token
|
||||
*/
|
||||
public static LoginUser getLoginUser(String token) {
|
||||
return (LoginUser) StpUtil.getTokenSessionByToken(token).get(LOGIN_USER_KEY);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户id
|
||||
*/
|
||||
public static Long getUserId() {
|
||||
LoginUser loginUser = getLoginUser();
|
||||
if (ObjectUtil.isNull(loginUser)) {
|
||||
String loginId = StpUtil.getLoginIdAsString();
|
||||
String userId = null;
|
||||
for (UserType value : UserType.values()) {
|
||||
if (StringUtils.contains(loginId, value.getUserType())) {
|
||||
String[] strs = StringUtils.split(loginId, JOIN_CODE);
|
||||
// 用户id在总是在最后
|
||||
userId = strs[strs.length - 1];
|
||||
}
|
||||
Long userId;
|
||||
try {
|
||||
userId = Convert.toLong(SaHolder.getStorage().get(USER_KEY));
|
||||
if (ObjectUtil.isNull(userId)) {
|
||||
userId = Convert.toLong(StpUtil.getExtra(USER_KEY));
|
||||
SaHolder.getStorage().set(USER_KEY, userId);
|
||||
}
|
||||
if (StringUtils.isBlank(userId)) {
|
||||
throw new UtilException("登录用户: LoginId异常 => " + loginId);
|
||||
}
|
||||
return Long.parseLong(userId);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
return loginUser.getUserId();
|
||||
return userId;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -22,7 +22,9 @@ import java.util.Objects;
|
||||
* 字典数据json序列化工具
|
||||
*
|
||||
* @author itino
|
||||
* @deprecated 建议使用通用翻译注解
|
||||
*/
|
||||
@Deprecated
|
||||
@Slf4j
|
||||
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;
|
||||
}
|
||||
}
|
@ -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) {
|
||||
Map<String, String> params = new HashMap<>();
|
||||
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;
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ public class StreamUtils {
|
||||
* @return 拼接后的list
|
||||
*/
|
||||
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;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.lang.Validator;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 字符串工具类
|
||||
@ -20,6 +20,8 @@ import java.util.Set;
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
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 url 需要匹配的url
|
||||
* @return
|
||||
*/
|
||||
public static boolean isMatch(String pattern, String url) {
|
||||
AntPathMatcher matcher = new AntPathMatcher();
|
||||
@ -234,23 +235,23 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
|
||||
/**
|
||||
* 数字左边补齐0,使之达到指定长度。注意,如果数字转换为字符串后,长度大于size,则只保留 最后size个字符。
|
||||
*
|
||||
* @param num 数字对象
|
||||
* @param num 数字对象
|
||||
* @param size 字符串指定长度
|
||||
* @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');
|
||||
}
|
||||
|
||||
/**
|
||||
* 字符串左补齐。如果原始字符串s长度大于size,则只保留最后size个字符。
|
||||
*
|
||||
* @param s 原始字符串
|
||||
* @param s 原始字符串
|
||||
* @param size 字符串指定长度
|
||||
* @param c 用于补齐的字符
|
||||
* @param c 用于补齐的字符
|
||||
* @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);
|
||||
if (s != null) {
|
||||
final int len = s.length();
|
||||
@ -270,4 +271,55 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
|
||||
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;
|
||||
|
||||
import cn.hutool.core.lang.Dict;
|
||||
import cn.hutool.core.net.NetUtil;
|
||||
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 lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
@ -21,40 +16,18 @@ import lombok.extern.slf4j.Slf4j;
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
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 String getRealAddressByIP(String ip) {
|
||||
String address = UNKNOWN;
|
||||
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);
|
||||
if (NetUtil.isInnerIP(ip)) {
|
||||
return "内网IP";
|
||||
}
|
||||
if (RuoYiConfig.isAddressEnabled()) {
|
||||
try {
|
||||
String rspStr = HttpUtil.createGet(IP_URL)
|
||||
.body("ip=" + ip + "&json=true", Constants.GBK)
|
||||
.execute()
|
||||
.body();
|
||||
if (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;
|
||||
return RegionUtils.getCityInfo(ip);
|
||||
}
|
||||
}
|
||||
|
@ -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 "未知";
|
||||
}
|
||||
}
|
||||
}
|
@ -270,7 +270,7 @@ public class ExcelUtil {
|
||||
*/
|
||||
public static String convertByExp(String propertyValue, String converterExp, String separator) {
|
||||
StringBuilder propertyString = new StringBuilder();
|
||||
String[] convertSource = converterExp.split(",");
|
||||
String[] convertSource = converterExp.split(StringUtils.SEPARATOR);
|
||||
for (String item : convertSource) {
|
||||
String[] itemArray = item.split("=");
|
||||
if (StringUtils.containsAny(propertyValue, separator)) {
|
||||
@ -299,7 +299,7 @@ public class ExcelUtil {
|
||||
*/
|
||||
public static String reverseByExp(String propertyValue, String converterExp, String separator) {
|
||||
StringBuilder propertyString = new StringBuilder();
|
||||
String[] convertSource = converterExp.split(",");
|
||||
String[] convertSource = converterExp.split(StringUtils.SEPARATOR);
|
||||
for (String item : convertSource) {
|
||||
String[] itemArray = item.split("=");
|
||||
if (StringUtils.containsAny(propertyValue, separator)) {
|
||||
|
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>ruoyi-vue-plus</artifactId>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<version>4.5.0</version>
|
||||
<version>4.7.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
@ -49,4 +49,16 @@ public class RedisRateLimiterController {
|
||||
return R.ok("操作成功", value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试请求IP限流(key基于参数获取)
|
||||
* 同一IP请求受影响
|
||||
*
|
||||
* 简单变量获取 #变量 复杂表达式 #{#变量 != 1 ? 1 : 0}
|
||||
*/
|
||||
@RateLimiter(count = 2, time = 10, limitType = LimitType.IP, key = "#value")
|
||||
@GetMapping("/testObj")
|
||||
public R<String> testObj(String value) {
|
||||
return R.ok("操作成功", value);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,55 @@
|
||||
package com.ruoyi.demo.controller;
|
||||
|
||||
import com.ruoyi.common.core.domain.R;
|
||||
import com.ruoyi.demo.domain.TestDemoEncrypt;
|
||||
import com.ruoyi.demo.mapper.TestDemoEncryptMapper;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* 测试数据库加解密功能
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@Validated
|
||||
@RestController
|
||||
@RequestMapping("/demo/encrypt")
|
||||
public class TestEncryptController {
|
||||
|
||||
@Autowired
|
||||
private TestDemoEncryptMapper mapper;
|
||||
@Value("${mybatis-encryptor.enable}")
|
||||
private Boolean encryptEnable;
|
||||
|
||||
/**
|
||||
* 测试数据库加解密
|
||||
*
|
||||
* @param key 测试key
|
||||
* @param value 测试value
|
||||
*/
|
||||
@GetMapping()
|
||||
public R<Map<String, TestDemoEncrypt>> test(String key, String value) {
|
||||
if (!encryptEnable) {
|
||||
throw new RuntimeException("加密功能未开启!");
|
||||
}
|
||||
Map<String, TestDemoEncrypt> map = new HashMap<>(2);
|
||||
TestDemoEncrypt demo = new TestDemoEncrypt();
|
||||
demo.setTestKey(key);
|
||||
demo.setValue(value);
|
||||
mapper.insert(demo);
|
||||
map.put("加密", demo);
|
||||
TestDemoEncrypt testDemo = mapper.selectById(demo.getId());
|
||||
map.put("解密", testDemo);
|
||||
return R.ok(map);
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package com.ruoyi.demo.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.ruoyi.common.annotation.EncryptField;
|
||||
import com.ruoyi.common.enums.AlgorithmType;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("test_demo")
|
||||
public class TestDemoEncrypt extends TestDemo {
|
||||
|
||||
/**
|
||||
* key键
|
||||
*/
|
||||
// @EncryptField(algorithm=AlgorithmType.SM2, privateKey = "MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQgZSlOvw8FBiH+aFJWLYZP/VRjg9wjfRarTkGBZd/T3N+gCgYIKoEcz1UBgi2hRANCAAR5DGuQwJqkxnbCsP+iPSDoHWIF4RwcR5EsSvT8QPxO1wRkR2IhCkzvRb32x2CUgJFdvoqVqfApFDPZzShqzBwX", publicKey = "MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEeQxrkMCapMZ2wrD/oj0g6B1iBeEcHEeRLEr0/ED8TtcEZEdiIQpM70W99sdglICRXb6KlanwKRQz2c0oaswcFw==")
|
||||
@EncryptField(algorithm = AlgorithmType.RSA, privateKey = "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBANBBEeueWlXlkkj2+WY5l+IWe42d8b5K28g+G/CFKC/yYAEHtqGlCsBOrb+YBkG9mPzmuYA/n9k0NFIc8E8yY5vZQaroyFBrTTWEzG9RY2f7Y3svVyybs6jpXSUs4xff8abo7wL1Y/wUaeatTViamxYnyTvdTmLm3d+JjRij68rxAgMBAAECgYAB0TnhXraSopwIVRfmboea1b0upl+BUdTJcmci412UjrKr5aE695ZLPkXbFXijVu7HJlyyv94NVUdaMACV7Ku/S2RuNB70M7YJm8rAjHFC3/i2ZeIM60h1Ziy4QKv0XM3pRATlDCDNhC1WUrtQCQSgU8kcp6eUUppruOqDzcY04QJBAPm9+sBP9CwDRgy3e5+V8aZtJkwDstb0lVVV/KY890cydVxiCwvX3fqVnxKMlb+x0YtH0sb9v+71xvK2lGobaRECQQDVePU6r/cCEfpc+nkWF6osAH1f8Mux3rYv2DoBGvaPzV2BGfsLed4neRfCwWNCKvGPCdW+L0xMJg8+RwaoBUPhAkAT5kViqXxFPYWJYd1h2+rDXhMdH3ZSlm6HvDBDdrwlWinr0Iwcx3iSjPV93uHXwm118aUj4fg3LDJMCKxOwBxhAkByrQXfvwOMYygBprRBf/j0plazoWFrbd6lGR0f1uI5IfNnFRPdeFw1DEINZ2Hw+6zEUF44SqRMC+4IYJNc02dBAkBCgy7RvfyV/A7N6kKXxTHauY0v6XwSSvpeKtRJkbIcRWOdIYvaHO9L7cklj3vIEdwjSUp9K4VTBYYlmAz1xh03", publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDQQRHrnlpV5ZJI9vlmOZfiFnuNnfG+StvIPhvwhSgv8mABB7ahpQrATq2/mAZBvZj85rmAP5/ZNDRSHPBPMmOb2UGq6MhQa001hMxvUWNn+2N7L1csm7Oo6V0lLOMX3/Gm6O8C9WP8FGnmrU1YmpsWJ8k73U5i5t3fiY0Yo+vK8QIDAQAB")
|
||||
private String testKey;
|
||||
|
||||
/**
|
||||
* 值
|
||||
*/
|
||||
// @EncryptField // 什么也不写走默认yml配置
|
||||
// @EncryptField(algorithm = AlgorithmType.SM4, password = "10rfylhtccpuyke5")
|
||||
@EncryptField(algorithm = AlgorithmType.AES, password = "10rfylhtccpuyke5")
|
||||
private String value;
|
||||
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package com.ruoyi.demo.mapper;
|
||||
|
||||
import com.ruoyi.common.core.mapper.BaseMapperPlus;
|
||||
import com.ruoyi.demo.domain.TestDemoEncrypt;
|
||||
|
||||
/**
|
||||
* 测试加密功能
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
public interface TestDemoEncryptMapper extends BaseMapperPlus<TestDemoEncryptMapper, TestDemoEncrypt, TestDemoEncrypt> {
|
||||
|
||||
}
|
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>ruoyi-vue-plus</artifactId>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<version>4.5.0</version>
|
||||
<version>4.7.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>ruoyi-extend</artifactId>
|
||||
|
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>ruoyi-extend</artifactId>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<version>4.5.0</version>
|
||||
<version>4.7.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<packaging>jar</packaging>
|
||||
|
@ -6,6 +6,9 @@ spring:
|
||||
profiles:
|
||||
active: @profiles.active@
|
||||
|
||||
logging:
|
||||
config: classpath:logback-plus.xml
|
||||
|
||||
--- # 监控中心服务端配置
|
||||
spring:
|
||||
security:
|
||||
|
@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>ruoyi-extend</artifactId>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<version>4.5.0</version>
|
||||
<version>4.7.0</version>
|
||||
</parent>
|
||||
<artifactId>ruoyi-xxl-job-admin</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.xxl.job.admin.controller;
|
||||
|
||||
import com.xxl.job.admin.controller.annotation.PermissionLimit;
|
||||
import com.xxl.job.admin.core.model.XxlJobGroup;
|
||||
import com.xxl.job.admin.core.model.XxlJobRegistry;
|
||||
import com.xxl.job.admin.core.util.I18nUtil;
|
||||
@ -35,12 +36,14 @@ public class JobGroupController {
|
||||
private XxlJobRegistryDao xxlJobRegistryDao;
|
||||
|
||||
@RequestMapping
|
||||
@PermissionLimit(adminuser = true)
|
||||
public String index(Model model) {
|
||||
return "jobgroup/jobgroup.index";
|
||||
}
|
||||
|
||||
@RequestMapping("/pageList")
|
||||
@ResponseBody
|
||||
@PermissionLimit(adminuser = true)
|
||||
public Map<String, Object> pageList(HttpServletRequest request,
|
||||
@RequestParam(required = false, defaultValue = "0") int start,
|
||||
@RequestParam(required = false, defaultValue = "10") int length,
|
||||
@ -60,6 +63,7 @@ public class JobGroupController {
|
||||
|
||||
@RequestMapping("/save")
|
||||
@ResponseBody
|
||||
@PermissionLimit(adminuser = true)
|
||||
public ReturnT<String> save(XxlJobGroup xxlJobGroup) {
|
||||
|
||||
// valid
|
||||
@ -103,6 +107,7 @@ public class JobGroupController {
|
||||
|
||||
@RequestMapping("/update")
|
||||
@ResponseBody
|
||||
@PermissionLimit(adminuser = true)
|
||||
public ReturnT<String> update(XxlJobGroup xxlJobGroup) {
|
||||
// valid
|
||||
if (xxlJobGroup.getAppname() == null || xxlJobGroup.getAppname().trim().length() == 0) {
|
||||
@ -171,6 +176,7 @@ public class JobGroupController {
|
||||
|
||||
@RequestMapping("/remove")
|
||||
@ResponseBody
|
||||
@PermissionLimit(adminuser = true)
|
||||
public ReturnT<String> remove(int id) {
|
||||
|
||||
// valid
|
||||
@ -190,6 +196,7 @@ public class JobGroupController {
|
||||
|
||||
@RequestMapping("/loadById")
|
||||
@ResponseBody
|
||||
@PermissionLimit(adminuser = true)
|
||||
public ReturnT<XxlJobGroup> loadById(int id) {
|
||||
XxlJobGroup jobGroup = xxlJobGroupDao.load(id);
|
||||
return jobGroup != null ? new ReturnT<XxlJobGroup>(jobGroup) : new ReturnT<XxlJobGroup>(ReturnT.FAIL_CODE, null);
|
||||
|
@ -130,22 +130,26 @@ public class JobLogController {
|
||||
|
||||
model.addAttribute("triggerCode", jobLog.getTriggerCode());
|
||||
model.addAttribute("handleCode", jobLog.getHandleCode());
|
||||
model.addAttribute("executorAddress", jobLog.getExecutorAddress());
|
||||
model.addAttribute("triggerTime", jobLog.getTriggerTime().getTime());
|
||||
model.addAttribute("logId", jobLog.getId());
|
||||
return "joblog/joblog.detail";
|
||||
}
|
||||
|
||||
@RequestMapping("/logDetailCat")
|
||||
@ResponseBody
|
||||
public ReturnT<LogResult> logDetailCat(String executorAddress, long triggerTime, long logId, int fromLineNum) {
|
||||
public ReturnT<LogResult> logDetailCat(long logId, int fromLineNum) {
|
||||
try {
|
||||
ExecutorBiz executorBiz = XxlJobScheduler.getExecutorBiz(executorAddress);
|
||||
ReturnT<LogResult> logResult = executorBiz.log(new LogParam(triggerTime, logId, fromLineNum));
|
||||
// valid
|
||||
XxlJobLog jobLog = xxlJobLogDao.load(logId); // todo, need to improve performance
|
||||
if (jobLog == null) {
|
||||
return new ReturnT<LogResult>(ReturnT.FAIL_CODE, I18nUtil.getString("joblog_logid_unvalid"));
|
||||
}
|
||||
|
||||
// log cat
|
||||
ExecutorBiz executorBiz = XxlJobScheduler.getExecutorBiz(jobLog.getExecutorAddress());
|
||||
ReturnT<LogResult> logResult = executorBiz.log(new LogParam(jobLog.getTriggerTime().getTime(), logId, fromLineNum));
|
||||
|
||||
// is end
|
||||
if (logResult.getContent() != null && logResult.getContent().getFromLineNum() > logResult.getContent().getToLineNum()) {
|
||||
XxlJobLog jobLog = xxlJobLogDao.load(logId);
|
||||
if (jobLog.getHandleCode() > 0) {
|
||||
logResult.getContent().setEnd(true);
|
||||
}
|
||||
|
@ -16,6 +16,9 @@ spring:
|
||||
resources:
|
||||
static-locations: classpath:/static/
|
||||
|
||||
logging:
|
||||
config: classpath:logback-plus.xml
|
||||
|
||||
--- # mybatis 配置
|
||||
mybatis:
|
||||
mapper-locations: classpath:/mybatis-mapper/*Mapper.xml
|
||||
|
@ -1,6 +1,6 @@
|
||||
admin_name=Scheduling Center
|
||||
admin_name_full=Distributed Task Scheduling Platform XXL-JOB
|
||||
admin_version=2.3.1
|
||||
admin_version=2.4.0
|
||||
admin_i18n=en
|
||||
|
||||
## system
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user