master
管理员 3 years ago
commit befb80ab8a

@ -0,0 +1,18 @@
# http://editorconfig.org
root = true
# 空格替代Tab缩进在各种编辑工具下效果一致
[*]
indent_style = space
indent_size = 4
charset = utf-8
end_of_line = lf
trim_trailing_whitespace = true
insert_final_newline = true
[*.{json,yml,yaml}]
indent_size = 2
[*.md]
insert_final_newline = false
trim_trailing_whitespace = false

52
.gitignore vendored

@ -0,0 +1,52 @@
######################################################################
# 本地环境配置
*-local.*
######################################################################
# Build Tools
.gradle
/build/
!gradle/wrapper/gradle-wrapper.jar
target/
!.mvn/wrapper/maven-wrapper.jar
######################################################################
# IDE
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### JRebel ###
rebel.xml
### NetBeans ###
nbproject/private/
build/*
nbbuild/
nbdist/
.nb-gradle/
######################################################################
# Others
*.log
*.xml.versionsBackup
*.swp
!*/build/*.java
!*/build/*.html
!*/build/*.xml

@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2019 RuoYi-Vue-Plus
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

@ -0,0 +1,164 @@
<img src="https://foruda.gitee.com/images/1679673773341074847/178e8451_1766278.png" width="50%" height="50%">
<div style="height: 10px; clear: both;"></div>
- - -
## 平台简介
[![码云Gitee](https://gitee.com/dromara/RuoYi-Vue-Plus/badge/star.svg?theme=blue)](https://gitee.com/dromara/RuoYi-Vue-Plus)
[![GitHub](https://img.shields.io/github/stars/JavaLionLi/RuoYi-Vue-Plus.svg?style=social&label=Stars)](https://github.com/dromara/RuoYi-Vue-Plus)
[![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://gitee.com/dromara/RuoYi-Vue-Plus/blob/master/LICENSE)
[![使用IntelliJ IDEA开发维护](https://img.shields.io/badge/IntelliJ%20IDEA-提供支持-blue.svg)](https://www.jetbrains.com/?from=RuoYi-Vue-Plus)
<br>
[![RuoYi-Vue-Plus](https://img.shields.io/badge/RuoYi_Vue_Plus-4.6.0-success.svg)](https://gitee.com/dromara/RuoYi-Vue-Plus)
[![Spring Boot](https://img.shields.io/badge/Spring%20Boot-2.7-blue.svg)]()
[![JDK-8+](https://img.shields.io/badge/JDK-8-green.svg)]()
[![JDK-11](https://img.shields.io/badge/JDK-11-green.svg)]()
> RuoYi-Vue-Plus 是重写 RuoYi-Vue 针对 `分布式集群` 场景全方位升级(不兼容原框架)
> 项目代码、文档 均开源免费可商用 遵循开源协议在项目中保留开源协议文件即可<br>
活到老写到老 为兴趣而开源 为学习而开源 为让大家真正可以学到技术而开源
> 系统演示: [传送门](https://gitee.com/dromara/RuoYi-Vue-Plus/wikis/pages?sort_id=4836388&doc_id=1469725)
# 本框架与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/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/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/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/dromara/RuoYi-Vue-Plus/wikis/pages)
>>[https://gitee.com/dromara/RuoYi-Vue-Plus/wikis/pages](https://gitee.com/dromara/RuoYi-Vue-Plus/wikis/pages)
## 软件架构图
![Plus部署架构图](https://images.gitee.com/uploads/images/2021/1112/202137_673ac5d2_1766278.png "Plus部署架构图.png")
## 贡献代码
欢迎各路英雄豪杰 `PR` 代码 请提交到 `dev` 开发分支 统一测试发版
框架定位为 `通用后台管理系统(分布式集群强化)` 原则上不接受业务 `PR`
### 其他
* 同步升级 RuoYi-Vue
* 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)
* 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/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" />
## 演示图例
| | |
|--------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------|
| ![输入图片说明](https://foruda.gitee.com/images/1680077524361362822/270bb429_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680077619939771291/989bf9b6_1766278.png "屏幕截图") |
| ![输入图片说明](https://foruda.gitee.com/images/1680077681751513929/1c27c5bd_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680077721559267315/74d63e23_1766278.png "屏幕截图") |
| ![输入图片说明](https://foruda.gitee.com/images/1680077765638904515/1b75d4a6_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078026375951297/eded7a4b_1766278.png "屏幕截图") |
| ![输入图片说明](https://foruda.gitee.com/images/1680078237104531207/0eb1b6a7_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078254306078709/5931e22f_1766278.png "屏幕截图") |
| ![输入图片说明](https://foruda.gitee.com/images/1680078287971528493/0b9af60a_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078308138770249/8d3b6696_1766278.png "屏幕截图") |
| ![输入图片说明](https://foruda.gitee.com/images/1680078352553634393/db5ef880_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078378238393374/601e4357_1766278.png "屏幕截图") |
| ![输入图片说明](https://foruda.gitee.com/images/1680078414983206024/2aae27c1_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078446738419874/ecce7d59_1766278.png "屏幕截图") |
| ![输入图片说明](https://foruda.gitee.com/images/1680078475971341775/149e8634_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078491666717143/3fadece7_1766278.png "屏幕截图") |
| ![输入图片说明](https://foruda.gitee.com/images/1680078558863188826/fb8ced2a_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078574561685461/ae68a0b2_1766278.png "屏幕截图") |
| ![输入图片说明](https://foruda.gitee.com/images/1680078594932772013/9d8bfec6_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078626493093532/fcfe4ff6_1766278.png "屏幕截图") |
| ![输入图片说明](https://foruda.gitee.com/images/1680078643608812515/0295bd4f_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078685196286463/d7612c81_1766278.png "屏幕截图") |
| ![输入图片说明](https://foruda.gitee.com/images/1680078703877318597/56fce0bc_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078716586545643/b6dbd68f_1766278.png "屏幕截图") |
| ![输入图片说明](https://foruda.gitee.com/images/1680078734103217688/eb1e6aa6_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078759131415480/73c525d8_1766278.png "屏幕截图") |
| ![输入图片说明](https://foruda.gitee.com/images/1680078779416197879/75e3ed02_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078802329118061/77e10915_1766278.png "屏幕截图") |
| ![输入图片说明](https://foruda.gitee.com/images/1680078893627848351/34a1c342_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078928175016986/f126ec4a_1766278.png "屏幕截图") |
| ![输入图片说明](https://foruda.gitee.com/images/1680078941718318363/b68a0f72_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680078963175518631/3bb769a1_1766278.png "屏幕截图") |
| ![输入图片说明](https://foruda.gitee.com/images/1680078982294090567/b31c343d_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680079000642440444/77ca82a9_1766278.png "屏幕截图") |
| ![输入图片说明](https://foruda.gitee.com/images/1680079020995074177/03b7d52e_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680079039367822173/76811806_1766278.png "屏幕截图") |
| ![输入图片说明](https://foruda.gitee.com/images/1680079274333484664/4dfdc7c0_1766278.png "屏幕截图") | ![输入图片说明](https://foruda.gitee.com/images/1680079290467458224/d6715fcf_1766278.png "屏幕截图") |

@ -0,0 +1,438 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-vue-plus</artifactId>
<version>4.6.0</version>
<name>RuoYi-Vue-Plus</name>
<url>https://gitee.com/dromara/RuoYi-Vue-Plus</url>
<description>RuoYi-Vue-Plus后台管理系统</description>
<properties>
<ruoyi-vue-plus.version>4.6.0</ruoyi-vue-plus.version>
<spring-boot.version>2.7.9</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.15</springdoc.version>
<poi.version>5.2.3</poi.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.15</hutool.version>
<okhttp.version>4.10.0</okhttp.version>
<spring-boot-admin.version>2.7.10</spring-boot-admin.version>
<redisson.version>3.20.0</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.26</lombok.version>
<bouncycastle.version>1.72</bouncycastle.version>
<!-- 临时修复 snakeyaml 漏洞 -->
<snakeyaml.version>1.33</snakeyaml.version>
<!-- OSS 配置 -->
<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.687</tencent.sms.version>
</properties>
<profiles>
<profile>
<id>local</id>
<properties>
<!-- 环境标识,需要与配置文件的名称相对应 -->
<profiles.active>local</profiles.active>
<logging.level>debug</logging.level>
</properties>
</profile>
<profile>
<id>dev</id>
<properties>
<!-- 环境标识,需要与配置文件的名称相对应 -->
<profiles.active>dev</profiles.active>
<logging.level>debug</logging.level>
</properties>
<activation>
<!-- 默认环境 -->
<activeByDefault>true</activeByDefault>
</activation>
</profile>
<profile>
<id>prod</id>
<properties>
<profiles.active>prod</profiles.active>
<logging.level>warn</logging.level>
</properties>
</profile>
</profiles>
<!-- 依赖声明 -->
<dependencyManagement>
<dependencies>
<!-- SpringBoot的依赖配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- hutool 的依赖配置-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-bom</artifactId>
<version>${hutool.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-webmvc-core</artifactId>
<version>${springdoc.version}</version>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-javadoc</artifactId>
<version>${springdoc.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>${poi.version}</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>${poi.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>${easyexcel.version}</version>
<exclusions>
<exclusion>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml-schemas</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- velocity代码生成使用模板 -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>${velocity.version}</version>
</dependency>
<!-- Sa-Token 权限认证, 在线文档http://sa-token.dev33.cn/ -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-starter</artifactId>
<version>${satoken.version}</version>
</dependency>
<!-- Sa-Token 整合 jwt -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-jwt</artifactId>
<version>${satoken.version}</version>
<exclusions>
<exclusion>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- dynamic-datasource 多数据源-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>${dynamic-ds.version}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<!-- sql性能分析插件 -->
<dependency>
<groupId>p6spy</groupId>
<artifactId>p6spy</artifactId>
<version>${p6spy.version}</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>${okhttp.version}</version>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-s3</artifactId>
<version>${aws-java-sdk-s3.version}</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>dysmsapi20170525</artifactId>
<version>${aliyun.sms.version}</version>
</dependency>
<dependency>
<groupId>com.tencentcloudapi</groupId>
<artifactId>tencentcloud-sdk-java-sms</artifactId>
<version>${tencent.sms.version}</version>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-server</artifactId>
<version>${spring-boot-admin.version}</version>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
<version>${spring-boot-admin.version}</version>
</dependency>
<!--redisson-->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>${redisson.version}</version>
<exclusions>
<exclusion>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-data-30</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-data-27</artifactId>
<version>${redisson.version}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>lock4j-redisson-spring-boot-starter</artifactId>
<version>${lock4j.version}</version>
</dependency>
<!-- xxl-job-core -->
<dependency>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-core</artifactId>
<version>${xxl-job.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>transmittable-thread-local</artifactId>
<version>${alibaba-ttl.version}</version>
</dependency>
<!-- 临时修复 snakeyaml 漏洞 -->
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>${snakeyaml.version}</version>
</dependency>
<!-- 加密包引入 -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15to18</artifactId>
<version>${bouncycastle.version}</version>
</dependency>
<!-- 定时任务 -->
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-job</artifactId>
<version>${ruoyi-vue-plus.version}</version>
</dependency>
<!-- 代码生成-->
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-generator</artifactId>
<version>${ruoyi-vue-plus.version}</version>
</dependency>
<!-- 核心模块-->
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-framework</artifactId>
<version>${ruoyi-vue-plus.version}</version>
</dependency>
<!-- 系统模块-->
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-system</artifactId>
<version>${ruoyi-vue-plus.version}</version>
</dependency>
<!-- 通用工具-->
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-common</artifactId>
<version>${ruoyi-vue-plus.version}</version>
</dependency>
<!-- OSS对象存储模块 -->
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-oss</artifactId>
<version>${ruoyi-vue-plus.version}</version>
</dependency>
<!-- SMS短信模块 -->
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-sms</artifactId>
<version>${ruoyi-vue-plus.version}</version>
</dependency>
<!-- demo模块 -->
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-demo</artifactId>
<version>${ruoyi-vue-plus.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<modules>
<module>ruoyi-admin</module>
<module>ruoyi-framework</module>
<module>ruoyi-system</module>
<module>ruoyi-job</module>
<module>ruoyi-generator</module>
<module>ruoyi-common</module>
<module>ruoyi-demo</module>
<module>ruoyi-extend</module>
<module>ruoyi-oss</module>
<module>ruoyi-sms</module>
</modules>
<packaging>pom</packaging>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.9.0</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<encoding>${project.build.sourceEncoding}</encoding>
<annotationProcessorPaths>
<path>
<groupId>com.github.therapi</groupId>
<artifactId>therapi-runtime-javadoc-scribe</artifactId>
<version>0.15.0</version>
</path>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
<path>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>${spring-boot.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
<!-- 单元测试使用 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
<configuration>
<argLine>-Dfile.encoding=UTF-8</argLine>
<!-- 根据打包环境执行对应的@Tag测试方法 -->
<groups>${profiles.active}</groups>
<!-- 排除标签 -->
<excludedGroups>exclude</excludedGroups>
</configuration>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/resources</directory>
<!-- 关闭过滤 -->
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<!-- 引入所有 匹配文件进行过滤 -->
<includes>
<include>application*</include>
<include>bootstrap*</include>
<include>banner*</include>
</includes>
<!-- 启用过滤 即该资源中的变量将会被过滤器中的值替换 -->
<filtering>true</filtering>
</resource>
</resources>
</build>
<repositories>
<repository>
<id>public</id>
<name>aliyun nexus</name>
<url>https://maven.aliyun.com/repository/public/</url>
<releases>
<enabled>true</enabled>
</releases>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>public</id>
<name>aliyun nexus</name>
<url>https://maven.aliyun.com/repository/public/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
</project>

@ -0,0 +1,23 @@
FROM anapsix/alpine-java:8_server-jre_unlimited
MAINTAINER Lion Li
RUN mkdir -p /ruoyi/server/logs \
/ruoyi/server/temp \
/ruoyi/skywalking/agent
WORKDIR /ruoyi/server
ENV SERVER_PORT=8080
EXPOSE ${SERVER_PORT}
ADD ./target/ruoyi-admin.jar ./app.jar
ENTRYPOINT ["java", \
"-Djava.security.egd=file:/dev/./urandom", \
"-Dserver.port=${SERVER_PORT}", \
# 应用名称 如果想区分集群节点监控 改成不同的名称即可
# "-Dskywalking.agent.service_name=ruoyi-server", \
# "-javaagent:/ruoyi/skywalking/agent/skywalking-agent.jar", \
"-jar", "app.jar"]

@ -0,0 +1,131 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>ruoyi-vue-plus</artifactId>
<groupId>com.ruoyi</groupId>
<version>4.6.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>
<artifactId>ruoyi-admin</artifactId>
<description>
web服务入口
</description>
<dependencies>
<!-- spring-boot-devtools -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional> <!-- 表示依赖不会传递 -->
</dependency>
<!-- Mysql驱动包 -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>
<!-- Oracle -->
<dependency>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc8</artifactId>
</dependency>
<!-- PostgreSql -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
</dependency>
<!-- SqlServer -->
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
</dependency>
<!-- 核心模块-->
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-framework</artifactId>
</dependency>
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-system</artifactId>
</dependency>
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-job</artifactId>
</dependency>
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-oss</artifactId>
</dependency>
<!-- 代码生成-->
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-generator</artifactId>
</dependency>
<!-- demo模块 -->
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-demo</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- skywalking 整合 logback -->
<!-- <dependency>-->
<!-- <groupId>org.apache.skywalking</groupId>-->
<!-- <artifactId>apm-toolkit-logback-1.x</artifactId>-->
<!-- <version>${与你的agent探针版本保持一致}</version>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>org.apache.skywalking</groupId>-->
<!-- <artifactId>apm-toolkit-trace</artifactId>-->
<!-- <version>${与你的agent探针版本保持一致}</version>-->
<!-- </dependency>-->
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
<configuration>
<fork>true</fork> <!-- 如果没有该配置devtools不会生效 -->
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.2</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
<warName>${project.artifactId}</warName>
</configuration>
</plugin>
</plugins>
</build>
</project>

@ -0,0 +1,24 @@
package com.ruoyi;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup;
/**
*
*
* @author ruoyi
*/
@SpringBootApplication
public class RuoYiApplication {
public static void main(String[] args) {
System.setProperty("spring.devtools.restart.enabled", "false");
SpringApplication application = new SpringApplication(RuoYiApplication.class);
application.setApplicationStartup(new BufferingApplicationStartup(2048));
application.run(args);
System.out.println("(♥◠‿◠)ノ゙ RuoYi-Vue-Plus启动成功 ლ(´ڡ`ლ)゙");
}
}

@ -0,0 +1,18 @@
package com.ruoyi;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
/**
* web
*
* @author ruoyi
*/
public class RuoYiServletInitializer extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(RuoYiApplication.class);
}
}

@ -0,0 +1,112 @@
package com.ruoyi.web.controller.common;
import cn.dev33.satoken.annotation.SaIgnore;
import cn.hutool.captcha.AbstractCaptcha;
import cn.hutool.captcha.generator.CodeGenerator;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil;
import com.ruoyi.common.constant.CacheConstants;
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.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.sms.config.properties.SmsProperties;
import com.ruoyi.sms.core.SmsTemplate;
import com.ruoyi.sms.entity.SmsResult;
import com.ruoyi.system.service.ISysConfigService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.constraints.NotBlank;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
/**
*
*
* @author Lion Li
*/
@SaIgnore
@Slf4j
@Validated
@RequiredArgsConstructor
@RestController
public class CaptchaController {
private final CaptchaProperties captchaProperties;
private final SmsProperties smsProperties;
private final ISysConfigService configService;
/**
*
*
* @param phonenumber
*/
@GetMapping("/captchaSms")
public R<Void> smsCaptcha(@NotBlank(message = "{user.phonenumber.not.blank}")
String phonenumber) {
if (!smsProperties.getEnabled()) {
return R.fail("当前系统没有开启短信功能!");
}
String key = CacheConstants.CAPTCHA_CODE_KEY + phonenumber;
String code = RandomUtil.randomNumbers(4);
RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
// 验证码模板id 自行处理 (查数据库或写死均可)
String templateId = "";
Map<String, String> map = new HashMap<>(1);
map.put("code", code);
SmsTemplate smsTemplate = SpringUtils.getBean(SmsTemplate.class);
SmsResult result = smsTemplate.send(phonenumber, templateId, map);
if (!result.isSuccess()) {
log.error("验证码短信发送异常 => {}", result);
return R.fail(result.getMessage());
}
return R.ok();
}
/**
*
*/
@GetMapping("/captchaImage")
public R<Map<String, Object>> getCode() {
Map<String, Object> ajax = new HashMap<>();
boolean captchaEnabled = configService.selectCaptchaEnabled();
ajax.put("captchaEnabled", captchaEnabled);
if (!captchaEnabled) {
return R.ok(ajax);
}
// 保存验证码信息
String uuid = IdUtil.simpleUUID();
String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + uuid;
// 生成验证码
CaptchaType captchaType = captchaProperties.getType();
boolean isMath = CaptchaType.MATH == captchaType;
Integer length = isMath ? captchaProperties.getNumberLength() : captchaProperties.getCharLength();
CodeGenerator codeGenerator = ReflectUtils.newInstance(captchaType.getClazz(), length);
AbstractCaptcha captcha = SpringUtils.getBean(captchaProperties.getCategory().getClazz());
captcha.setGenerator(codeGenerator);
captcha.createCode();
String code = captcha.getCode();
if (isMath) {
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression(StringUtils.remove(code, "="));
code = exp.getValue(String.class);
}
RedisUtils.setCacheObject(verifyKey, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
ajax.put("uuid", uuid);
ajax.put("img", captcha.getImageBase64());
return R.ok(ajax);
}
}

@ -0,0 +1,169 @@
package com.ruoyi.web.controller.monitor;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.collection.CollUtil;
import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.constant.CacheNames;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.utils.JsonUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.redis.CacheUtils;
import com.ruoyi.common.utils.redis.RedisUtils;
import com.ruoyi.system.domain.SysCache;
import lombok.RequiredArgsConstructor;
import org.redisson.spring.data.connection.RedissonConnectionFactory;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.web.bind.annotation.*;
import java.util.*;
import java.util.stream.Collectors;
/**
*
*
* @author Lion Li
*/
@RequiredArgsConstructor
@RestController
@RequestMapping("/monitor/cache")
public class CacheController {
private final RedissonConnectionFactory connectionFactory;
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, "数据字典"));
CACHES.add(new SysCache(CacheConstants.CAPTCHA_CODE_KEY, "验证码"));
CACHES.add(new SysCache(CacheConstants.REPEAT_SUBMIT_KEY, "防重提交"));
CACHES.add(new SysCache(CacheConstants.RATE_LIMIT_KEY, "限流处理"));
CACHES.add(new SysCache(CacheNames.SYS_OSS_CONFIG, "OSS配置"));
CACHES.add(new SysCache(CacheConstants.PWD_ERR_CNT_KEY, "密码错误次数"));
}
/**
*
*/
@SaCheckPermission("monitor:cache:list")
@GetMapping()
public R<Map<String, Object>> getInfo() throws Exception {
RedisConnection connection = connectionFactory.getConnection();
Properties info = connection.info();
Properties commandStats = connection.info("commandstats");
Long dbSize = connection.dbSize();
Map<String, Object> result = new HashMap<>(3);
result.put("info", info);
result.put("dbSize", dbSize);
List<Map<String, String>> pieList = new ArrayList<>();
if (commandStats != null) {
commandStats.stringPropertyNames().forEach(key -> {
Map<String, String> data = new HashMap<>(2);
String property = commandStats.getProperty(key);
data.put("name", StringUtils.removeStart(key, "cmdstat_"));
data.put("value", StringUtils.substringBetween(property, "calls=", ",usec"));
pieList.add(data);
});
}
result.put("commandStats", pieList);
return R.ok(result);
}
/**
*
*/
@SaCheckPermission("monitor:cache:list")
@GetMapping("/getNames")
public R<List<SysCache>> cache() {
return R.ok(CACHES);
}
/**
* Key
*
* @param cacheName
*/
@SaCheckPermission("monitor:cache:list")
@GetMapping("/getKeys/{cacheName}")
public R<Collection<String>> getCacheKeys(@PathVariable String cacheName) {
Collection<String> cacheKeys = new HashSet<>(0);
if (isCacheNames(cacheName)) {
Set<Object> keys = CacheUtils.keys(cacheName);
if (CollUtil.isNotEmpty(keys)) {
cacheKeys = keys.stream().map(Object::toString).collect(Collectors.toList());
}
} else {
cacheKeys = RedisUtils.keys(cacheName + "*");
}
return R.ok(cacheKeys);
}
/**
*
*
* @param cacheName
* @param cacheKey key
*/
@SaCheckPermission("monitor:cache:list")
@GetMapping("/getValue/{cacheName}/{cacheKey}")
public R<SysCache> getCacheValue(@PathVariable String cacheName, @PathVariable String cacheKey) {
Object cacheValue;
if (isCacheNames(cacheName)) {
cacheValue = CacheUtils.get(cacheName, cacheKey);
} else {
cacheValue = RedisUtils.getCacheObject(cacheKey);
}
SysCache sysCache = new SysCache(cacheName, cacheKey, JsonUtils.toJsonString(cacheValue));
return R.ok(sysCache);
}
/**
*
*
* @param cacheName
*/
@SaCheckPermission("monitor:cache:list")
@DeleteMapping("/clearCacheName/{cacheName}")
public R<Void> clearCacheName(@PathVariable String cacheName) {
if (isCacheNames(cacheName)) {
CacheUtils.clear(cacheName);
} else {
RedisUtils.deleteKeys(cacheName + "*");
}
return R.ok();
}
/**
* Key
*
* @param cacheKey key
*/
@SaCheckPermission("monitor:cache:list")
@DeleteMapping("/clearCacheKey/{cacheName}/{cacheKey}")
public R<Void> clearCacheKey(@PathVariable String cacheName, @PathVariable String cacheKey) {
if (isCacheNames(cacheName)) {
CacheUtils.evict(cacheName, cacheKey);
} else {
RedisUtils.deleteObject(cacheKey);
}
return R.ok();
}
/**
*
*/
@SaCheckPermission("monitor:cache:list")
@DeleteMapping("/clearCacheAll")
public R<Void> clearCacheAll() {
RedisUtils.deleteKeys("*");
return R.ok();
}
private boolean isCacheNames(String cacheName) {
return !StringUtils.contains(cacheName, ":");
}
}

@ -0,0 +1,88 @@
package com.ruoyi.web.controller.monitor;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.constant.CacheConstants;
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.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.common.utils.redis.RedisUtils;
import com.ruoyi.system.domain.SysLogininfor;
import com.ruoyi.system.service.ISysLogininforService;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
/**
* 访
*
* @author Lion Li
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/monitor/logininfor")
public class SysLogininforController extends BaseController {
private final ISysLogininforService logininforService;
/**
* 访
*/
@SaCheckPermission("monitor:logininfor:list")
@GetMapping("/list")
public TableDataInfo<SysLogininfor> list(SysLogininfor logininfor, PageQuery pageQuery) {
return logininforService.selectPageLogininforList(logininfor, pageQuery);
}
/**
* 访
*/
@Log(title = "登录日志", businessType = BusinessType.EXPORT)
@SaCheckPermission("monitor:logininfor:export")
@PostMapping("/export")
public void export(SysLogininfor logininfor, HttpServletResponse response) {
List<SysLogininfor> list = logininforService.selectLogininforList(logininfor);
ExcelUtil.exportExcel(list, "登录日志", SysLogininfor.class, response);
}
/**
*
* @param infoIds ids
*/
@SaCheckPermission("monitor:logininfor:remove")
@Log(title = "登录日志", businessType = BusinessType.DELETE)
@DeleteMapping("/{infoIds}")
public R<Void> remove(@PathVariable Long[] infoIds) {
return toAjax(logininforService.deleteLogininforByIds(infoIds));
}
/**
* 访
*/
@SaCheckPermission("monitor:logininfor:remove")
@Log(title = "登录日志", businessType = BusinessType.CLEAN)
@DeleteMapping("/clean")
public R<Void> clean() {
logininforService.cleanLogininfor();
return R.ok();
}
@SaCheckPermission("monitor:logininfor:unlock")
@Log(title = "账户解锁", businessType = BusinessType.OTHER)
@GetMapping("/unlock/{userName}")
public R<Void> unlock(@PathVariable("userName") String userName) {
String loginName = CacheConstants.PWD_ERR_CNT_KEY + userName;
if (RedisUtils.hasKey(loginName)) {
RedisUtils.deleteObject(loginName);
}
return R.ok();
}
}

@ -0,0 +1,74 @@
package com.ruoyi.web.controller.monitor;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.PageQuery;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.system.domain.SysOperLog;
import com.ruoyi.system.service.ISysOperLogService;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
/**
*
*
* @author Lion Li
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/monitor/operlog")
public class SysOperlogController extends BaseController {
private final ISysOperLogService operLogService;
/**
*
*/
@SaCheckPermission("monitor:operlog:list")
@GetMapping("/list")
public TableDataInfo<SysOperLog> list(SysOperLog operLog, PageQuery pageQuery) {
return operLogService.selectPageOperLogList(operLog, pageQuery);
}
/**
*
*/
@Log(title = "操作日志", businessType = BusinessType.EXPORT)
@SaCheckPermission("monitor:operlog:export")
@PostMapping("/export")
public void export(SysOperLog operLog, HttpServletResponse response) {
List<SysOperLog> list = operLogService.selectOperLogList(operLog);
ExcelUtil.exportExcel(list, "操作日志", SysOperLog.class, response);
}
/**
*
* @param operIds ids
*/
@Log(title = "操作日志", businessType = BusinessType.DELETE)
@SaCheckPermission("monitor:operlog:remove")
@DeleteMapping("/{operIds}")
public R<Void> remove(@PathVariable Long[] operIds) {
return toAjax(operLogService.deleteOperLogByIds(operIds));
}
/**
*
*/
@Log(title = "操作日志", businessType = BusinessType.CLEAN)
@SaCheckPermission("monitor:operlog:remove")
@DeleteMapping("/clean")
public R<Void> clean() {
operLogService.cleanOperLog();
return R.ok();
}
}

@ -0,0 +1,90 @@
package com.ruoyi.web.controller.monitor;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.domain.dto.UserOnlineDTO;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.StreamUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.redis.RedisUtils;
import com.ruoyi.system.domain.SysUserOnline;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* 线
*
* @author Lion Li
*/
@RequiredArgsConstructor
@RestController
@RequestMapping("/monitor/online")
public class SysUserOnlineController extends BaseController {
/**
* 线
*
* @param ipaddr IP
* @param userName
*/
@SaCheckPermission("monitor:online:list")
@GetMapping("/list")
public TableDataInfo<SysUserOnline> list(String ipaddr, String userName) {
// 获取所有未过期的 token
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, "");
// 如果已经过期则跳过
if (StpUtil.stpLogic.getTokenActivityTimeoutByToken(token) < -1) {
continue;
}
userOnlineDTOList.add(RedisUtils.getCacheObject(CacheConstants.ONLINE_TOKEN_KEY + token));
}
if (StringUtils.isNotEmpty(ipaddr) && StringUtils.isNotEmpty(userName)) {
userOnlineDTOList = StreamUtils.filter(userOnlineDTOList, userOnline ->
StringUtils.equals(ipaddr, userOnline.getIpaddr()) &&
StringUtils.equals(userName, userOnline.getUserName())
);
} else if (StringUtils.isNotEmpty(ipaddr)) {
userOnlineDTOList = StreamUtils.filter(userOnlineDTOList, userOnline ->
StringUtils.equals(ipaddr, userOnline.getIpaddr())
);
} else if (StringUtils.isNotEmpty(userName)) {
userOnlineDTOList = StreamUtils.filter(userOnlineDTOList, userOnline ->
StringUtils.equals(userName, userOnline.getUserName())
);
}
Collections.reverse(userOnlineDTOList);
userOnlineDTOList.removeAll(Collections.singleton(null));
List<SysUserOnline> userOnlineList = BeanUtil.copyToList(userOnlineDTOList, SysUserOnline.class);
return TableDataInfo.build(userOnlineList);
}
/**
* 退
*
* @param tokenId token
*/
@SaCheckPermission("monitor:online:forceLogout")
@Log(title = "在线用户", businessType = BusinessType.FORCE)
@DeleteMapping("/{tokenId}")
public R<Void> forceLogout(@PathVariable String tokenId) {
try {
StpUtil.kickoutByTokenValue(tokenId);
} catch (NotLoginException ignored) {
}
return R.ok();
}
}

@ -0,0 +1,137 @@
package com.ruoyi.web.controller.system;
import cn.dev33.satoken.annotation.SaCheckPermission;
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.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.system.domain.SysConfig;
import com.ruoyi.system.service.ISysConfigService;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
/**
*
*
* @author Lion Li
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/system/config")
public class SysConfigController extends BaseController {
private final ISysConfigService configService;
/**
*
*/
@SaCheckPermission("system:config:list")
@GetMapping("/list")
public TableDataInfo<SysConfig> list(SysConfig config, PageQuery pageQuery) {
return configService.selectPageConfigList(config, pageQuery);
}
/**
*
*/
@Log(title = "参数管理", businessType = BusinessType.EXPORT)
@SaCheckPermission("system:config:export")
@PostMapping("/export")
public void export(SysConfig config, HttpServletResponse response) {
List<SysConfig> list = configService.selectConfigList(config);
ExcelUtil.exportExcel(list, "参数数据", SysConfig.class, response);
}
/**
*
*
* @param configId ID
*/
@SaCheckPermission("system:config:query")
@GetMapping(value = "/{configId}")
public R<SysConfig> getInfo(@PathVariable Long configId) {
return R.ok(configService.selectConfigById(configId));
}
/**
*
*
* @param configKey Key
*/
@GetMapping(value = "/configKey/{configKey}")
public R<Void> getConfigKey(@PathVariable String configKey) {
return R.ok(configService.selectConfigByKey(configKey));
}
/**
*
*/
@SaCheckPermission("system:config:add")
@Log(title = "参数管理", businessType = BusinessType.INSERT)
@PostMapping
public R<Void> add(@Validated @RequestBody SysConfig config) {
if (!configService.checkConfigKeyUnique(config)) {
return R.fail("新增参数'" + config.getConfigName() + "'失败,参数键名已存在");
}
configService.insertConfig(config);
return R.ok();
}
/**
*
*/
@SaCheckPermission("system:config:edit")
@Log(title = "参数管理", businessType = BusinessType.UPDATE)
@PutMapping
public R<Void> edit(@Validated @RequestBody SysConfig config) {
if (!configService.checkConfigKeyUnique(config)) {
return R.fail("修改参数'" + config.getConfigName() + "'失败,参数键名已存在");
}
configService.updateConfig(config);
return R.ok();
}
/**
*
*/
@SaCheckPermission("system:config:edit")
@Log(title = "参数管理", businessType = BusinessType.UPDATE)
@PutMapping("/updateByKey")
public R<Void> updateByKey(@RequestBody SysConfig config) {
configService.updateConfig(config);
return R.ok();
}
/**
*
*
* @param configIds ID
*/
@SaCheckPermission("system:config:remove")
@Log(title = "参数管理", businessType = BusinessType.DELETE)
@DeleteMapping("/{configIds}")
public R<Void> remove(@PathVariable Long[] configIds) {
configService.deleteConfigByIds(configIds);
return R.ok();
}
/**
*
*/
@SaCheckPermission("system:config:remove")
@Log(title = "参数管理", businessType = BusinessType.CLEAN)
@DeleteMapping("/refreshCache")
public R<Void> refreshCache() {
configService.resetConfigCache();
return R.ok();
}
}

@ -0,0 +1,119 @@
package com.ruoyi.web.controller.system;
import cn.dev33.satoken.annotation.SaCheckPermission;
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;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.service.ISysDeptService;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
*
*
* @author Lion Li
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/system/dept")
public class SysDeptController extends BaseController {
private final ISysDeptService deptService;
/**
*
*/
@SaCheckPermission("system:dept:list")
@GetMapping("/list")
public R<List<SysDept>> list(SysDept dept) {
List<SysDept> depts = deptService.selectDeptList(dept);
return R.ok(depts);
}
/**
*
*
* @param deptId ID
*/
@SaCheckPermission("system:dept:list")
@GetMapping("/list/exclude/{deptId}")
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)
|| StringUtils.splitList(d.getAncestors()).contains(Convert.toStr(deptId)));
return R.ok(depts);
}
/**
*
*
* @param deptId ID
*/
@SaCheckPermission("system:dept:query")
@GetMapping(value = "/{deptId}")
public R<SysDept> getInfo(@PathVariable Long deptId) {
deptService.checkDeptDataScope(deptId);
return R.ok(deptService.selectDeptById(deptId));
}
/**
*
*/
@SaCheckPermission("system:dept:add")
@Log(title = "部门管理", businessType = BusinessType.INSERT)
@PostMapping
public R<Void> add(@Validated @RequestBody SysDept dept) {
if (!deptService.checkDeptNameUnique(dept)) {
return R.fail("新增部门'" + dept.getDeptName() + "'失败,部门名称已存在");
}
return toAjax(deptService.insertDept(dept));
}
/**
*
*/
@SaCheckPermission("system:dept:edit")
@Log(title = "部门管理", businessType = BusinessType.UPDATE)
@PutMapping
public R<Void> edit(@Validated @RequestBody SysDept dept) {
Long deptId = dept.getDeptId();
deptService.checkDeptDataScope(deptId);
if (!deptService.checkDeptNameUnique(dept)) {
return R.fail("修改部门'" + dept.getDeptName() + "'失败,部门名称已存在");
} else if (dept.getParentId().equals(deptId)) {
return R.fail("修改部门'" + dept.getDeptName() + "'失败,上级部门不能是自己");
} else if (StringUtils.equals(UserConstants.DEPT_DISABLE, dept.getStatus())
&& deptService.selectNormalChildrenDeptById(deptId) > 0) {
return R.fail("该部门包含未停用的子部门!");
}
return toAjax(deptService.updateDept(dept));
}
/**
*
*
* @param deptId ID
*/
@SaCheckPermission("system:dept:remove")
@Log(title = "部门管理", businessType = BusinessType.DELETE)
@DeleteMapping("/{deptId}")
public R<Void> remove(@PathVariable Long deptId) {
if (deptService.hasChildByDeptId(deptId)) {
return R.warn("存在下级部门,不允许删除");
}
if (deptService.checkDeptExistUser(deptId)) {
return R.warn("部门存在用户,不允许删除");
}
deptService.checkDeptDataScope(deptId);
return toAjax(deptService.deleteDeptById(deptId));
}
}

@ -0,0 +1,116 @@
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.core.controller.BaseController;
import com.ruoyi.common.core.domain.PageQuery;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.domain.entity.SysDictData;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.system.service.ISysDictDataService;
import com.ruoyi.system.service.ISysDictTypeService;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.List;
/**
*
*
* @author Lion Li
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/system/dict/data")
public class SysDictDataController extends BaseController {
private final ISysDictDataService dictDataService;
private final ISysDictTypeService dictTypeService;
/**
*
*/
@SaCheckPermission("system:dict:list")
@GetMapping("/list")
public TableDataInfo<SysDictData> list(SysDictData dictData, PageQuery pageQuery) {
return dictDataService.selectPageDictDataList(dictData, pageQuery);
}
/**
*
*/
@Log(title = "字典数据", businessType = BusinessType.EXPORT)
@SaCheckPermission("system:dict:export")
@PostMapping("/export")
public void export(SysDictData dictData, HttpServletResponse response) {
List<SysDictData> list = dictDataService.selectDictDataList(dictData);
ExcelUtil.exportExcel(list, "字典数据", SysDictData.class, response);
}
/**
*
*
* @param dictCode code
*/
@SaCheckPermission("system:dict:query")
@GetMapping(value = "/{dictCode}")
public R<SysDictData> getInfo(@PathVariable Long dictCode) {
return R.ok(dictDataService.selectDictDataById(dictCode));
}
/**
*
*
* @param dictType
*/
@GetMapping(value = "/type/{dictType}")
public R<List<SysDictData>> dictType(@PathVariable String dictType) {
List<SysDictData> data = dictTypeService.selectDictDataByType(dictType);
if (ObjectUtil.isNull(data)) {
data = new ArrayList<>();
}
return R.ok(data);
}
/**
*
*/
@SaCheckPermission("system:dict:add")
@Log(title = "字典数据", businessType = BusinessType.INSERT)
@PostMapping
public R<Void> add(@Validated @RequestBody SysDictData dict) {
dictDataService.insertDictData(dict);
return R.ok();
}
/**
*
*/
@SaCheckPermission("system:dict:edit")
@Log(title = "字典数据", businessType = BusinessType.UPDATE)
@PutMapping
public R<Void> edit(@Validated @RequestBody SysDictData dict) {
dictDataService.updateDictData(dict);
return R.ok();
}
/**
*
*
* @param dictCodes code
*/
@SaCheckPermission("system:dict:remove")
@Log(title = "字典类型", businessType = BusinessType.DELETE)
@DeleteMapping("/{dictCodes}")
public R<Void> remove(@PathVariable Long[] dictCodes) {
dictDataService.deleteDictDataByIds(dictCodes);
return R.ok();
}
}

@ -0,0 +1,125 @@
package com.ruoyi.web.controller.system;
import cn.dev33.satoken.annotation.SaCheckPermission;
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.SysDictType;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.system.service.ISysDictTypeService;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
/**
*
*
* @author Lion Li
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/system/dict/type")
public class SysDictTypeController extends BaseController {
private final ISysDictTypeService dictTypeService;
/**
*
*/
@SaCheckPermission("system:dict:list")
@GetMapping("/list")
public TableDataInfo<SysDictType> list(SysDictType dictType, PageQuery pageQuery) {
return dictTypeService.selectPageDictTypeList(dictType, pageQuery);
}
/**
*
*/
@Log(title = "字典类型", businessType = BusinessType.EXPORT)
@SaCheckPermission("system:dict:export")
@PostMapping("/export")
public void export(SysDictType dictType, HttpServletResponse response) {
List<SysDictType> list = dictTypeService.selectDictTypeList(dictType);
ExcelUtil.exportExcel(list, "字典类型", SysDictType.class, response);
}
/**
*
*
* @param dictId ID
*/
@SaCheckPermission("system:dict:query")
@GetMapping(value = "/{dictId}")
public R<SysDictType> getInfo(@PathVariable Long dictId) {
return R.ok(dictTypeService.selectDictTypeById(dictId));
}
/**
*
*/
@SaCheckPermission("system:dict:add")
@Log(title = "字典类型", businessType = BusinessType.INSERT)
@PostMapping
public R<Void> add(@Validated @RequestBody SysDictType dict) {
if (!dictTypeService.checkDictTypeUnique(dict)) {
return R.fail("新增字典'" + dict.getDictName() + "'失败,字典类型已存在");
}
dictTypeService.insertDictType(dict);
return R.ok();
}
/**
*
*/
@SaCheckPermission("system:dict:edit")
@Log(title = "字典类型", businessType = BusinessType.UPDATE)
@PutMapping
public R<Void> edit(@Validated @RequestBody SysDictType dict) {
if (!dictTypeService.checkDictTypeUnique(dict)) {
return R.fail("修改字典'" + dict.getDictName() + "'失败,字典类型已存在");
}
dictTypeService.updateDictType(dict);
return R.ok();
}
/**
*
*
* @param dictIds ID
*/
@SaCheckPermission("system:dict:remove")
@Log(title = "字典类型", businessType = BusinessType.DELETE)
@DeleteMapping("/{dictIds}")
public R<Void> remove(@PathVariable Long[] dictIds) {
dictTypeService.deleteDictTypeByIds(dictIds);
return R.ok();
}
/**
*
*/
@SaCheckPermission("system:dict:remove")
@Log(title = "字典类型", businessType = BusinessType.CLEAN)
@DeleteMapping("/refreshCache")
public R<Void> refreshCache() {
dictTypeService.resetDictCache();
return R.ok();
}
/**
*
*/
@GetMapping("/optionselect")
public R<List<SysDictType>> optionselect() {
List<SysDictType> dictTypes = dictTypeService.selectDictTypeAll();
return R.ok(dictTypes);
}
}

@ -0,0 +1,32 @@
package com.ruoyi.web.controller.system;
import cn.dev33.satoken.annotation.SaIgnore;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.utils.StringUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
*
*
* @author Lion Li
*/
@RequiredArgsConstructor
@RestController
public class SysIndexController {
/**
*
*/
private final RuoYiConfig ruoyiConfig;
/**
* 访
*/
@SaIgnore
@GetMapping("/")
public String index() {
return StringUtils.format("欢迎使用{}后台管理框架当前版本v{},请通过前端地址访问。", ruoyiConfig.getName(), ruoyiConfig.getVersion());
}
}

@ -0,0 +1,128 @@
package com.ruoyi.web.controller.system;
import cn.dev33.satoken.annotation.SaIgnore;
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.LoginBody;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.core.domain.model.SmsLoginBody;
import com.ruoyi.common.helper.LoginHelper;
import com.ruoyi.system.domain.vo.RouterVo;
import com.ruoyi.system.service.ISysMenuService;
import com.ruoyi.system.service.ISysUserService;
import com.ruoyi.system.service.SysLoginService;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.constraints.NotBlank;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
*
*
* @author Lion Li
*/
@Validated
@RequiredArgsConstructor
@RestController
public class SysLoginController {
private final SysLoginService loginService;
private final ISysMenuService menuService;
private final ISysUserService userService;
/**
*
*
* @param loginBody
* @return
*/
@SaIgnore
@PostMapping("/login")
public R<Map<String, Object>> login(@Validated @RequestBody LoginBody loginBody) {
Map<String, Object> ajax = new HashMap<>();
// 生成令牌
String token = loginService.login(loginBody.getUsername(), loginBody.getPassword(), loginBody.getCode(),
loginBody.getUuid());
ajax.put(Constants.TOKEN, token);
return R.ok(ajax);
}
/**
* ()
*
* @param smsLoginBody
* @return
*/
@SaIgnore
@PostMapping("/smsLogin")
public R<Map<String, Object>> smsLogin(@Validated @RequestBody SmsLoginBody smsLoginBody) {
Map<String, Object> ajax = new HashMap<>();
// 生成令牌
String token = loginService.smsLogin(smsLoginBody.getPhonenumber(), smsLoginBody.getSmsCode());
ajax.put(Constants.TOKEN, token);
return R.ok(ajax);
}
/**
* ()
*
* @param xcxCode code
* @return
*/
@SaIgnore
@PostMapping("/xcxLogin")
public R<Map<String, Object>> xcxLogin(@NotBlank(message = "{xcx.code.not.blank}") String xcxCode) {
Map<String, Object> ajax = new HashMap<>();
// 生成令牌
String token = loginService.xcxLogin(xcxCode);
ajax.put(Constants.TOKEN, token);
return R.ok(ajax);
}
/**
* 退
*/
@SaIgnore
@PostMapping("/logout")
public R<Void> logout() {
loginService.logout();
return R.ok("退出成功");
}
/**
*
*
* @return
*/
@GetMapping("getInfo")
public R<Map<String, Object>> getInfo() {
LoginUser loginUser = LoginHelper.getLoginUser();
SysUser user = userService.selectUserById(loginUser.getUserId());
Map<String, Object> ajax = new HashMap<>();
ajax.put("user", user);
ajax.put("roles", loginUser.getRolePermission());
ajax.put("permissions", loginUser.getMenuPermission());
return R.ok(ajax);
}
/**
*
*
* @return
*/
@GetMapping("getRouters")
public R<List<RouterVo>> getRouters() {
Long userId = LoginHelper.getUserId();
List<SysMenu> menus = menuService.selectMenuTreeByUserId(userId);
return R.ok(menuService.buildMenus(menus));
}
}

@ -0,0 +1,127 @@
package com.ruoyi.web.controller.system;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.lang.tree.Tree;
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.R;
import com.ruoyi.common.core.domain.entity.SysMenu;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.service.ISysMenuService;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
*
*
* @author Lion Li
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/system/menu")
public class SysMenuController extends BaseController {
private final ISysMenuService menuService;
/**
*
*/
@SaCheckPermission("system:menu:list")
@GetMapping("/list")
public R<List<SysMenu>> list(SysMenu menu) {
List<SysMenu> menus = menuService.selectMenuList(menu, getUserId());
return R.ok(menus);
}
/**
*
*
* @param menuId ID
*/
@SaCheckPermission("system:menu:query")
@GetMapping(value = "/{menuId}")
public R<SysMenu> getInfo(@PathVariable Long menuId) {
return R.ok(menuService.selectMenuById(menuId));
}
/**
*
*/
@GetMapping("/treeselect")
public R<List<Tree<Long>>> treeselect(SysMenu menu) {
List<SysMenu> menus = menuService.selectMenuList(menu, getUserId());
return R.ok(menuService.buildMenuTreeSelect(menus));
}
/**
*
*
* @param roleId ID
*/
@GetMapping(value = "/roleMenuTreeselect/{roleId}")
public R<Map<String, Object>> roleMenuTreeselect(@PathVariable("roleId") Long roleId) {
List<SysMenu> menus = menuService.selectMenuList(getUserId());
Map<String, Object> ajax = new HashMap<>();
ajax.put("checkedKeys", menuService.selectMenuListByRoleId(roleId));
ajax.put("menus", menuService.buildMenuTreeSelect(menus));
return R.ok(ajax);
}
/**
*
*/
@SaCheckPermission("system:menu:add")
@Log(title = "菜单管理", businessType = BusinessType.INSERT)
@PostMapping
public R<Void> add(@Validated @RequestBody SysMenu 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)://开头");
}
return toAjax(menuService.insertMenu(menu));
}
/**
*
*/
@SaCheckPermission("system:menu:edit")
@Log(title = "菜单管理", businessType = BusinessType.UPDATE)
@PutMapping
public R<Void> edit(@Validated @RequestBody SysMenu 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)://开头");
} else if (menu.getMenuId().equals(menu.getParentId())) {
return R.fail("修改菜单'" + menu.getMenuName() + "'失败,上级菜单不能选择自己");
}
return toAjax(menuService.updateMenu(menu));
}
/**
*
*
* @param menuId ID
*/
@SaCheckPermission("system:menu:remove")
@Log(title = "菜单管理", businessType = BusinessType.DELETE)
@DeleteMapping("/{menuId}")
public R<Void> remove(@PathVariable("menuId") Long menuId) {
if (menuService.hasChildByMenuId(menuId)) {
return R.warn("存在子菜单,不允许删除");
}
if (menuService.checkMenuExistRole(menuId)) {
return R.warn("菜单已分配,不允许删除");
}
return toAjax(menuService.deleteMenuById(menuId));
}
}

@ -0,0 +1,80 @@
package com.ruoyi.web.controller.system;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.PageQuery;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.system.domain.SysNotice;
import com.ruoyi.system.service.ISysNoticeService;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
/**
*
*
* @author Lion Li
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/system/notice")
public class SysNoticeController extends BaseController {
private final ISysNoticeService noticeService;
/**
*
*/
@SaCheckPermission("system:notice:list")
@GetMapping("/list")
public TableDataInfo<SysNotice> list(SysNotice notice, PageQuery pageQuery) {
return noticeService.selectPageNoticeList(notice, pageQuery);
}
/**
*
*
* @param noticeId ID
*/
@SaCheckPermission("system:notice:query")
@GetMapping(value = "/{noticeId}")
public R<SysNotice> getInfo(@PathVariable Long noticeId) {
return R.ok(noticeService.selectNoticeById(noticeId));
}
/**
*
*/
@SaCheckPermission("system:notice:add")
@Log(title = "通知公告", businessType = BusinessType.INSERT)
@PostMapping
public R<Void> add(@Validated @RequestBody SysNotice notice) {
return toAjax(noticeService.insertNotice(notice));
}
/**
*
*/
@SaCheckPermission("system:notice:edit")
@Log(title = "通知公告", businessType = BusinessType.UPDATE)
@PutMapping
public R<Void> edit(@Validated @RequestBody SysNotice notice) {
return toAjax(noticeService.updateNotice(notice));
}
/**
*
*
* @param noticeIds ID
*/
@SaCheckPermission("system:notice:remove")
@Log(title = "通知公告", businessType = BusinessType.DELETE)
@DeleteMapping("/{noticeIds}")
public R<Void> remove(@PathVariable Long[] noticeIds) {
return toAjax(noticeService.deleteNoticeByIds(noticeIds));
}
}

@ -0,0 +1,105 @@
package com.ruoyi.web.controller.system;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.annotation.RepeatSubmit;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.PageQuery;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.core.validate.AddGroup;
import com.ruoyi.common.core.validate.EditGroup;
import com.ruoyi.common.core.validate.QueryGroup;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.system.domain.bo.SysOssConfigBo;
import com.ruoyi.system.domain.vo.SysOssConfigVo;
import com.ruoyi.system.service.ISysOssConfigService;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.Arrays;
/**
*
*
* @author Lion Li
* @author
* @date 2021-08-13
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/system/oss/config")
public class SysOssConfigController extends BaseController {
private final ISysOssConfigService iSysOssConfigService;
/**
*
*/
@SaCheckPermission("system:oss:list")
@GetMapping("/list")
public TableDataInfo<SysOssConfigVo> list(@Validated(QueryGroup.class) SysOssConfigBo bo, PageQuery pageQuery) {
return iSysOssConfigService.queryPageList(bo, pageQuery);
}
/**
*
*
* @param ossConfigId OSSID
*/
@SaCheckPermission("system:oss:query")
@GetMapping("/{ossConfigId}")
public R<SysOssConfigVo> getInfo(@NotNull(message = "主键不能为空")
@PathVariable Long ossConfigId) {
return R.ok(iSysOssConfigService.queryById(ossConfigId));
}
/**
*
*/
@SaCheckPermission("system:oss:add")
@Log(title = "对象存储配置", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping()
public R<Void> add(@Validated(AddGroup.class) @RequestBody SysOssConfigBo bo) {
return toAjax(iSysOssConfigService.insertByBo(bo));
}
/**
*
*/
@SaCheckPermission("system:oss:edit")
@Log(title = "对象存储配置", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping()
public R<Void> edit(@Validated(EditGroup.class) @RequestBody SysOssConfigBo bo) {
return toAjax(iSysOssConfigService.updateByBo(bo));
}
/**
*
*
* @param ossConfigIds OSSID
*/
@SaCheckPermission("system:oss:remove")
@Log(title = "对象存储配置", businessType = BusinessType.DELETE)
@DeleteMapping("/{ossConfigIds}")
public R<Void> remove(@NotEmpty(message = "主键不能为空")
@PathVariable Long[] ossConfigIds) {
return toAjax(iSysOssConfigService.deleteWithValidByIds(Arrays.asList(ossConfigIds), true));
}
/**
*
*/
@SaCheckPermission("system:oss:edit")
@Log(title = "对象存储状态修改", businessType = BusinessType.UPDATE)
@PutMapping("/changeStatus")
public R<Void> changeStatus(@RequestBody SysOssConfigBo bo) {
return toAjax(iSysOssConfigService.updateOssConfigStatus(bo));
}
}

@ -0,0 +1,117 @@
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;
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;
import lombok.RequiredArgsConstructor;
import org.springframework.http.MediaType;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotEmpty;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
*
*
* @author Lion Li
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/system/oss")
public class SysOssController extends BaseController {
private final ISysOssService iSysOssService;
/**
* OSS
*/
@SaCheckPermission("system:oss:list")
@GetMapping("/list")
public TableDataInfo<SysOssVo> list(@Validated(QueryGroup.class) SysOssBo bo, PageQuery pageQuery) {
return iSysOssService.queryPageList(bo, pageQuery);
}
/**
* OSSid
*
* @param ossIds OSSID
*/
@SaCheckPermission("system:oss:list")
@GetMapping("/listByIds/{ossIds}")
public R<List<SysOssVo>> listByIds(@NotEmpty(message = "主键不能为空")
@PathVariable Long[] ossIds) {
List<SysOssVo> list = iSysOssService.listByIds(Arrays.asList(ossIds));
return R.ok(list);
}
/**
* OSS
*
* @param file
*/
@SaCheckPermission("system:oss:upload")
@Log(title = "OSS对象存储", businessType = BusinessType.INSERT)
@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("上传文件不能为空");
}
SysOssVo oss = iSysOssService.upload(file);
Map<String, String> map = new HashMap<>(2);
map.put("url", oss.getUrl());
map.put("fileName", oss.getOriginalName());
map.put("ossId", oss.getOssId().toString());
return R.ok(map);
}
/**
* OSS
*
* @param ossId OSSID
*/
@SaCheckPermission("system:oss:download")
@GetMapping("/download/{ossId}")
public void download(@PathVariable Long ossId, HttpServletResponse response) throws IOException {
iSysOssService.download(ossId,response);
}
/**
* OSS
*
* @param ossIds OSSID
*/
@SaCheckPermission("system:oss:remove")
@Log(title = "OSS对象存储", businessType = BusinessType.DELETE)
@DeleteMapping("/{ossIds}")
public R<Void> remove(@NotEmpty(message = "主键不能为空")
@PathVariable Long[] ossIds) {
return toAjax(iSysOssService.deleteWithValidByIds(Arrays.asList(ossIds), true));
}
}

@ -0,0 +1,115 @@
package com.ruoyi.web.controller.system;
import cn.dev33.satoken.annotation.SaCheckPermission;
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.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.system.domain.SysPost;
import com.ruoyi.system.service.ISysPostService;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
/**
*
*
* @author Lion Li
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/system/post")
public class SysPostController extends BaseController {
private final ISysPostService postService;
/**
*
*/
@SaCheckPermission("system:post:list")
@GetMapping("/list")
public TableDataInfo<SysPost> list(SysPost post, PageQuery pageQuery) {
return postService.selectPagePostList(post, pageQuery);
}
/**
*
*/
@Log(title = "岗位管理", businessType = BusinessType.EXPORT)
@SaCheckPermission("system:post:export")
@PostMapping("/export")
public void export(SysPost post, HttpServletResponse response) {
List<SysPost> list = postService.selectPostList(post);
ExcelUtil.exportExcel(list, "岗位数据", SysPost.class, response);
}
/**
*
*
* @param postId ID
*/
@SaCheckPermission("system:post:query")
@GetMapping(value = "/{postId}")
public R<SysPost> getInfo(@PathVariable Long postId) {
return R.ok(postService.selectPostById(postId));
}
/**
*
*/
@SaCheckPermission("system:post:add")
@Log(title = "岗位管理", businessType = BusinessType.INSERT)
@PostMapping
public R<Void> add(@Validated @RequestBody SysPost post) {
if (!postService.checkPostNameUnique(post)) {
return R.fail("新增岗位'" + post.getPostName() + "'失败,岗位名称已存在");
} else if (!postService.checkPostCodeUnique(post)) {
return R.fail("新增岗位'" + post.getPostName() + "'失败,岗位编码已存在");
}
return toAjax(postService.insertPost(post));
}
/**
*
*/
@SaCheckPermission("system:post:edit")
@Log(title = "岗位管理", businessType = BusinessType.UPDATE)
@PutMapping
public R<Void> edit(@Validated @RequestBody SysPost post) {
if (!postService.checkPostNameUnique(post)) {
return R.fail("修改岗位'" + post.getPostName() + "'失败,岗位名称已存在");
} else if (!postService.checkPostCodeUnique(post)) {
return R.fail("修改岗位'" + post.getPostName() + "'失败,岗位编码已存在");
}
return toAjax(postService.updatePost(post));
}
/**
*
*
* @param postIds ID
*/
@SaCheckPermission("system:post:remove")
@Log(title = "岗位管理", businessType = BusinessType.DELETE)
@DeleteMapping("/{postIds}")
public R<Void> remove(@PathVariable Long[] postIds) {
return toAjax(postService.deletePostByIds(postIds));
}
/**
*
*/
@GetMapping("/optionselect")
public R<List<SysPost>> optionselect() {
List<SysPost> posts = postService.selectPostAll();
return R.ok(posts);
}
}

@ -0,0 +1,126 @@
package com.ruoyi.web.controller.system;
import cn.dev33.satoken.secure.BCrypt;
import cn.hutool.core.io.FileUtil;
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.R;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.helper.LoginHelper;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.file.MimeTypeUtils;
import com.ruoyi.system.domain.SysOss;
import com.ruoyi.system.domain.vo.SysOssVo;
import com.ruoyi.system.service.ISysOssService;
import com.ruoyi.system.service.ISysUserService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.MediaType;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
/**
*
*
* @author Lion Li
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/system/user/profile")
public class SysProfileController extends BaseController {
private final ISysUserService userService;
private final ISysOssService iSysOssService;
/**
*
*/
@GetMapping
public R<Map<String, Object>> profile() {
SysUser user = userService.selectUserById(getUserId());
Map<String, Object> ajax = new HashMap<>();
ajax.put("user", user);
ajax.put("roleGroup", userService.selectUserRoleGroup(user.getUserName()));
ajax.put("postGroup", userService.selectUserPostGroup(user.getUserName()));
return R.ok(ajax);
}
/**
*
*/
@Log(title = "个人信息", businessType = BusinessType.UPDATE)
@PutMapping
public R<Void> updateProfile(@RequestBody SysUser user) {
if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) {
return R.fail("修改用户'" + user.getUserName() + "'失败,手机号码已存在");
}
if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user)) {
return R.fail("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在");
}
user.setUserId(getUserId());
user.setUserName(null);
user.setPassword(null);
user.setAvatar(null);
user.setDeptId(null);
if (userService.updateUserProfile(user) > 0) {
return R.ok();
}
return R.fail("修改个人信息异常,请联系管理员");
}
/**
*
*
* @param newPassword
* @param oldPassword
*/
@Log(title = "个人信息", businessType = BusinessType.UPDATE)
@PutMapping("/updatePwd")
public R<Void> updatePwd(String oldPassword, String newPassword) {
SysUser user = userService.selectUserById(LoginHelper.getUserId());
String userName = user.getUserName();
String password = user.getPassword();
if (!BCrypt.checkpw(oldPassword, password)) {
return R.fail("修改密码失败,旧密码错误");
}
if (BCrypt.checkpw(newPassword, password)) {
return R.fail("新密码不能与旧密码相同");
}
if (userService.resetUserPwd(userName, BCrypt.hashpw(newPassword)) > 0) {
return R.ok();
}
return R.fail("修改密码异常,请联系管理员");
}
/**
*
*
* @param avatarfile
*/
@Log(title = "用户头像", businessType = BusinessType.UPDATE)
@PostMapping(value = "/avatar", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public R<Map<String, Object>> avatar(@RequestPart("avatarfile") MultipartFile avatarfile) {
Map<String, Object> ajax = new HashMap<>();
if (!avatarfile.isEmpty()) {
String extension = FileUtil.extName(avatarfile.getOriginalFilename());
if (!StringUtils.equalsAnyIgnoreCase(extension, MimeTypeUtils.IMAGE_EXTENSION)) {
return R.fail("文件格式不正确,请上传" + Arrays.toString(MimeTypeUtils.IMAGE_EXTENSION) + "格式");
}
SysOssVo oss = iSysOssService.upload(avatarfile);
String avatar = oss.getUrl();
if (userService.updateUserAvatar(getUsername(), avatar)) {
ajax.put("imgUrl", avatar);
return R.ok(ajax);
}
}
return R.fail("上传图片异常,请联系管理员");
}
}

@ -0,0 +1,40 @@
package com.ruoyi.web.controller.system;
import cn.dev33.satoken.annotation.SaIgnore;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.domain.model.RegisterBody;
import com.ruoyi.system.service.ISysConfigService;
import com.ruoyi.system.service.SysRegisterService;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
/**
*
*
* @author Lion Li
*/
@Validated
@RequiredArgsConstructor
@RestController
public class SysRegisterController extends BaseController {
private final SysRegisterService registerService;
private final ISysConfigService configService;
/**
*
*/
@SaIgnore
@PostMapping("/register")
public R<Void> register(@Validated @RequestBody RegisterBody user) {
if (!("true".equals(configService.selectConfigByKey("sys.account.registerUser")))) {
return R.fail("当前系统没有开启注册功能!");
}
registerService.register(user);
return R.ok();
}
}

@ -0,0 +1,252 @@
package com.ruoyi.web.controller.system;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.collection.CollUtil;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.constant.CacheConstants;
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;
import com.ruoyi.system.service.ISysRoleService;
import com.ruoyi.system.service.ISysUserService;
import com.ruoyi.system.service.SysPermissionService;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
*
*
* @author Lion Li
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/system/role")
public class SysRoleController extends BaseController {
private final ISysRoleService roleService;
private final ISysUserService userService;
private final ISysDeptService deptService;
private final SysPermissionService permissionService;
/**
*
*/
@SaCheckPermission("system:role:list")
@GetMapping("/list")
public TableDataInfo<SysRole> list(SysRole role, PageQuery pageQuery) {
return roleService.selectPageRoleList(role, pageQuery);
}
/**
*
*/
@Log(title = "角色管理", businessType = BusinessType.EXPORT)
@SaCheckPermission("system:role:export")
@PostMapping("/export")
public void export(SysRole role, HttpServletResponse response) {
List<SysRole> list = roleService.selectRoleList(role);
ExcelUtil.exportExcel(list, "角色数据", SysRole.class, response);
}
/**
*
*
* @param roleId ID
*/
@SaCheckPermission("system:role:query")
@GetMapping(value = "/{roleId}")
public R<SysRole> getInfo(@PathVariable Long roleId) {
roleService.checkRoleDataScope(roleId);
return R.ok(roleService.selectRoleById(roleId));
}
/**
*
*/
@SaCheckPermission("system:role:add")
@Log(title = "角色管理", businessType = BusinessType.INSERT)
@PostMapping
public R<Void> add(@Validated @RequestBody SysRole role) {
if (!roleService.checkRoleNameUnique(role)) {
return R.fail("新增角色'" + role.getRoleName() + "'失败,角色名称已存在");
} else if (!roleService.checkRoleKeyUnique(role)) {
return R.fail("新增角色'" + role.getRoleName() + "'失败,角色权限已存在");
}
return toAjax(roleService.insertRole(role));
}
/**
*
*/
@SaCheckPermission("system:role:edit")
@Log(title = "角色管理", businessType = BusinessType.UPDATE)
@PutMapping
public R<Void> edit(@Validated @RequestBody SysRole role) {
roleService.checkRoleAllowed(role);
roleService.checkRoleDataScope(role.getRoleId());
if (!roleService.checkRoleNameUnique(role)) {
return R.fail("修改角色'" + role.getRoleName() + "'失败,角色名称已存在");
} else if (!roleService.checkRoleKeyUnique(role)) {
return R.fail("修改角色'" + role.getRoleName() + "'失败,角色权限已存在");
}
if (roleService.updateRole(role) > 0) {
List<String> keys = StpUtil.searchTokenValue("", 0, -1, false);
if (CollUtil.isEmpty(keys)) {
return R.ok();
}
// 角色关联的在线用户量过大会导致redis阻塞卡顿 谨慎操作
keys.parallelStream().forEach(key -> {
String token = key.replace(CacheConstants.LOGIN_TOKEN_KEY, "");
// 如果已经过期则跳过
if (StpUtil.stpLogic.getTokenActivityTimeoutByToken(token) < -1) {
return;
}
LoginUser loginUser = LoginHelper.getLoginUser(token);
if (loginUser.getRoles().stream().anyMatch(r -> r.getRoleId().equals(role.getRoleId()))) {
try {
StpUtil.logoutByTokenValue(token);
} catch (NotLoginException ignored) {
}
}
});
return R.ok();
}
return R.fail("修改角色'" + role.getRoleName() + "'失败,请联系管理员");
}
/**
*
*/
@SaCheckPermission("system:role:edit")
@Log(title = "角色管理", businessType = BusinessType.UPDATE)
@PutMapping("/dataScope")
public R<Void> dataScope(@RequestBody SysRole role) {
roleService.checkRoleAllowed(role);
roleService.checkRoleDataScope(role.getRoleId());
return toAjax(roleService.authDataScope(role));
}
/**
*
*/
@SaCheckPermission("system:role:edit")
@Log(title = "角色管理", businessType = BusinessType.UPDATE)
@PutMapping("/changeStatus")
public R<Void> changeStatus(@RequestBody SysRole role) {
roleService.checkRoleAllowed(role);
roleService.checkRoleDataScope(role.getRoleId());
return toAjax(roleService.updateRoleStatus(role));
}
/**
*
*
* @param roleIds ID
*/
@SaCheckPermission("system:role:remove")
@Log(title = "角色管理", businessType = BusinessType.DELETE)
@DeleteMapping("/{roleIds}")
public R<Void> remove(@PathVariable Long[] roleIds) {
return toAjax(roleService.deleteRoleByIds(roleIds));
}
/**
*
*/
@SaCheckPermission("system:role:query")
@GetMapping("/optionselect")
public R<List<SysRole>> optionselect() {
return R.ok(roleService.selectRoleAll());
}
/**
*
*/
@SaCheckPermission("system:role:list")
@GetMapping("/authUser/allocatedList")
public TableDataInfo<SysUser> allocatedList(SysUser user, PageQuery pageQuery) {
return userService.selectAllocatedList(user, pageQuery);
}
/**
*
*/
@SaCheckPermission("system:role:list")
@GetMapping("/authUser/unallocatedList")
public TableDataInfo<SysUser> unallocatedList(SysUser user, PageQuery pageQuery) {
return userService.selectUnallocatedList(user, pageQuery);
}
/**
*
*/
@SaCheckPermission("system:role:edit")
@Log(title = "角色管理", businessType = BusinessType.GRANT)
@PutMapping("/authUser/cancel")
public R<Void> cancelAuthUser(@RequestBody SysUserRole userRole) {
return toAjax(roleService.deleteAuthUser(userRole));
}
/**
*
*
* @param roleId ID
* @param userIds ID
*/
@SaCheckPermission("system:role:edit")
@Log(title = "角色管理", businessType = BusinessType.GRANT)
@PutMapping("/authUser/cancelAll")
public R<Void> cancelAuthUserAll(Long roleId, Long[] userIds) {
return toAjax(roleService.deleteAuthUsers(roleId, userIds));
}
/**
*
*
* @param roleId ID
* @param userIds ID
*/
@SaCheckPermission("system:role:edit")
@Log(title = "角色管理", businessType = BusinessType.GRANT)
@PutMapping("/authUser/selectAll")
public R<Void> selectAuthUserAll(Long roleId, Long[] userIds) {
roleService.checkRoleDataScope(roleId);
return toAjax(roleService.insertAuthUsers(roleId, userIds));
}
/**
*
*
* @param roleId ID
*/
@SaCheckPermission("system:role:list")
@GetMapping(value = "/deptTree/{roleId}")
public R<Map<String, Object>> roleDeptTreeselect(@PathVariable("roleId") Long roleId) {
Map<String, Object> ajax = new HashMap<>();
ajax.put("checkedKeys", deptService.selectDeptListByRoleId(roleId));
ajax.put("depts", deptService.selectDeptTreeList(new SysDept()));
return R.ok(ajax);
}
}

@ -0,0 +1,249 @@
package com.ruoyi.web.controller.system;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.secure.BCrypt;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.util.ArrayUtil;
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.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.excel.ExcelResult;
import com.ruoyi.common.helper.LoginHelper;
import com.ruoyi.common.utils.StreamUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.system.domain.vo.SysUserExportVo;
import com.ruoyi.system.domain.vo.SysUserImportVo;
import com.ruoyi.system.listener.SysUserImportListener;
import com.ruoyi.system.service.ISysDeptService;
import com.ruoyi.system.service.ISysPostService;
import com.ruoyi.system.service.ISysRoleService;
import com.ruoyi.system.service.ISysUserService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.MediaType;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
*
*
* @author Lion Li
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/system/user")
public class SysUserController extends BaseController {
private final ISysUserService userService;
private final ISysRoleService roleService;
private final ISysPostService postService;
private final ISysDeptService deptService;
/**
*
*/
@SaCheckPermission("system:user:list")
@GetMapping("/list")
public TableDataInfo<SysUser> list(SysUser user, PageQuery pageQuery) {
return userService.selectPageUserList(user, pageQuery);
}
/**
*
*/
@Log(title = "用户管理", businessType = BusinessType.EXPORT)
@SaCheckPermission("system:user:export")
@PostMapping("/export")
public void export(SysUser user, HttpServletResponse response) {
List<SysUser> list = userService.selectUserList(user);
List<SysUserExportVo> listVo = BeanUtil.copyToList(list, SysUserExportVo.class);
for (int i = 0; i < list.size(); i++) {
SysDept dept = list.get(i).getDept();
SysUserExportVo vo = listVo.get(i);
if (ObjectUtil.isNotEmpty(dept)) {
vo.setDeptName(dept.getDeptName());
vo.setLeader(dept.getLeader());
}
}
ExcelUtil.exportExcel(listVo, "用户数据", SysUserExportVo.class, response);
}
/**
*
*
* @param file
* @param updateSupport
*/
@Log(title = "用户管理", businessType = BusinessType.IMPORT)
@SaCheckPermission("system:user:import")
@PostMapping(value = "/importData", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public R<Void> importData(@RequestPart("file") MultipartFile file, boolean updateSupport) throws Exception {
ExcelResult<SysUserImportVo> result = ExcelUtil.importExcel(file.getInputStream(), SysUserImportVo.class, new SysUserImportListener(updateSupport));
return R.ok(result.getAnalysis());
}
/**
*
*/
@PostMapping("/importTemplate")
public void importTemplate(HttpServletResponse response) {
ExcelUtil.exportExcel(new ArrayList<>(), "用户数据", SysUserImportVo.class, response);
}
/**
*
*
* @param userId ID
*/
@SaCheckPermission("system:user:query")
@GetMapping(value = {"/", "/{userId}"})
public R<Map<String, Object>> getInfo(@PathVariable(value = "userId", required = false) Long userId) {
userService.checkUserDataScope(userId);
Map<String, Object> ajax = new HashMap<>();
List<SysRole> roles = roleService.selectRoleAll();
ajax.put("roles", LoginHelper.isAdmin(userId) ? roles : StreamUtils.filter(roles, r -> !r.isAdmin()));
ajax.put("posts", postService.selectPostAll());
if (ObjectUtil.isNotNull(userId)) {
SysUser sysUser = userService.selectUserById(userId);
ajax.put("user", sysUser);
ajax.put("postIds", postService.selectPostListByUserId(userId));
ajax.put("roleIds", StreamUtils.toList(sysUser.getRoles(), SysRole::getRoleId));
}
return R.ok(ajax);
}
/**
*
*/
@SaCheckPermission("system:user:add")
@Log(title = "用户管理", businessType = BusinessType.INSERT)
@PostMapping
public R<Void> add(@Validated @RequestBody SysUser user) {
if (!userService.checkUserNameUnique(user)) {
return R.fail("新增用户'" + user.getUserName() + "'失败,登录账号已存在");
} else if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) {
return R.fail("新增用户'" + user.getUserName() + "'失败,手机号码已存在");
} else if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user)) {
return R.fail("新增用户'" + user.getUserName() + "'失败,邮箱账号已存在");
}
user.setPassword(BCrypt.hashpw(user.getPassword()));
return toAjax(userService.insertUser(user));
}
/**
*
*/
@SaCheckPermission("system:user:edit")
@Log(title = "用户管理", businessType = BusinessType.UPDATE)
@PutMapping
public R<Void> edit(@Validated @RequestBody SysUser user) {
userService.checkUserAllowed(user);
userService.checkUserDataScope(user.getUserId());
if (!userService.checkUserNameUnique(user)) {
return R.fail("修改用户'" + user.getUserName() + "'失败,登录账号已存在");
} else if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) {
return R.fail("修改用户'" + user.getUserName() + "'失败,手机号码已存在");
} else if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user)) {
return R.fail("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在");
}
return toAjax(userService.updateUser(user));
}
/**
*
*
* @param userIds ID
*/
@SaCheckPermission("system:user:remove")
@Log(title = "用户管理", businessType = BusinessType.DELETE)
@DeleteMapping("/{userIds}")
public R<Void> remove(@PathVariable Long[] userIds) {
if (ArrayUtil.contains(userIds, getUserId())) {
return R.fail("当前用户不能删除");
}
return toAjax(userService.deleteUserByIds(userIds));
}
/**
*
*/
@SaCheckPermission("system:user:resetPwd")
@Log(title = "用户管理", businessType = BusinessType.UPDATE)
@PutMapping("/resetPwd")
public R<Void> resetPwd(@RequestBody SysUser user) {
userService.checkUserAllowed(user);
userService.checkUserDataScope(user.getUserId());
user.setPassword(BCrypt.hashpw(user.getPassword()));
return toAjax(userService.resetPwd(user));
}
/**
*
*/
@SaCheckPermission("system:user:edit")
@Log(title = "用户管理", businessType = BusinessType.UPDATE)
@PutMapping("/changeStatus")
public R<Void> changeStatus(@RequestBody SysUser user) {
userService.checkUserAllowed(user);
userService.checkUserDataScope(user.getUserId());
return toAjax(userService.updateUserStatus(user));
}
/**
*
*
* @param userId ID
*/
@SaCheckPermission("system:user:query")
@GetMapping("/authRole/{userId}")
public R<Map<String, Object>> authRole(@PathVariable Long userId) {
SysUser user = userService.selectUserById(userId);
List<SysRole> roles = roleService.selectRolesByUserId(userId);
Map<String, Object> ajax = new HashMap<>();
ajax.put("user", user);
ajax.put("roles", LoginHelper.isAdmin(userId) ? roles : StreamUtils.filter(roles, r -> !r.isAdmin()));
return R.ok(ajax);
}
/**
*
*
* @param userId Id
* @param roleIds ID
*/
@SaCheckPermission("system:user:edit")
@Log(title = "用户管理", businessType = BusinessType.GRANT)
@PutMapping("/authRole")
public R<Void> insertAuthRole(Long userId, Long[] roleIds) {
userService.checkUserDataScope(userId);
userService.insertUserAuth(userId, roleIds);
return R.ok();
}
/**
*
*/
@SaCheckPermission("system:user:list")
@GetMapping("/deptTree")
public R<List<Tree<Long>>> deptTree(SysDept dept) {
return R.ok(deptService.selectDeptTreeList(dept));
}
}

@ -0,0 +1,171 @@
--- # 监控中心配置
spring.boot.admin.client:
# 增加客户端开关
enabled: true
url: http://localhost:9090/admin
instance:
service-host-type: IP
username: ruoyi
password: 123456
--- # xxl-job 配置
xxl.job:
# 执行器开关
enabled: true
# 调度中心地址:如调度中心集群部署存在多个地址则用逗号分隔。
admin-addresses: http://localhost:9100/xxl-job-admin
# 执行器通讯TOKEN非空时启用
access-token: xxl-job
executor:
# 执行器AppName执行器心跳注册分组依据为空则关闭自动注册
appname: xxl-job-executor
# 执行器端口号 执行器从9101开始往后写
port: 9101
# 执行器注册默认IP:PORT
address:
# 执行器IP默认自动获取IP
ip:
# 执行器运行日志文件存储磁盘路径
logpath: ./logs/xxl-job
# 执行器日志文件保存天数大于3生效
logretentiondays: 30
--- # 数据源配置
spring:
datasource:
type: com.zaxxer.hikari.HikariDataSource
# 动态数据源文档 https://www.kancloud.cn/tracy5546/dynamic-datasource/content
dynamic:
# 性能分析插件(有性能损耗 不建议生产环境使用)
p6spy: true
# 设置默认的数据源或者数据源组,默认值即为 master
primary: master
# 严格模式 匹配不到数据源则报错
strict: true
datasource:
# 主库数据源
master:
type: ${spring.datasource.type}
driverClassName: com.mysql.cj.jdbc.Driver
# jdbc 所有参数配置参考 https://lionli.blog.csdn.net/article/details/122018562
# rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题)
url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true
username: root
password: root
# 从库数据源
slave:
lazy: true
type: ${spring.datasource.type}
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true
username:
password:
# oracle:
# type: ${spring.datasource.type}
# driverClassName: oracle.jdbc.OracleDriver
# url: jdbc:oracle:thin:@//localhost:1521/XE
# username: ROOT
# password: root
# hikari:
# connectionTestQuery: SELECT 1 FROM DUAL
# postgres:
# type: ${spring.datasource.type}
# driverClassName: org.postgresql.Driver
# url: jdbc:postgresql://localhost:5432/postgres?useUnicode=true&characterEncoding=utf8&useSSL=true&autoReconnect=true&reWriteBatchedInserts=true
# username: root
# password: root
# sqlserver:
# type: ${spring.datasource.type}
# driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver
# url: jdbc:sqlserver://localhost:1433;DatabaseName=tempdb;SelectMethod=cursor;encrypt=false;rewriteBatchedStatements=true
# username: SA
# password: root
hikari:
# 最大连接池数量
maxPoolSize: 20
# 最小空闲线程数量
minIdle: 10
# 配置获取连接等待超时的时间
connectionTimeout: 30000
# 校验超时时间
validationTimeout: 5000
# 空闲连接存活最大时间默认10分钟
idleTimeout: 600000
# 此属性控制池中连接的最长生命周期值0表示无限生命周期默认30分钟
maxLifetime: 1800000
# 连接测试query配置检测连接是否有效
connectionTestQuery: SELECT 1
# 多久检查一次连接的活性
keepaliveTime: 30000
--- # redis 单机配置(单机与集群只能开启一个另一个需要注释掉)
spring:
redis:
# 地址
host: localhost
# 端口默认为6379
port: 6379
# 数据库索引
database: 0
# 密码(如没有密码请注释掉)
password:
# 连接超时时间
timeout: 10s
# 是否开启ssl
ssl: false
redisson:
# redis key前缀
keyPrefix:
# 线程池数量
threads: 4
# Netty线程池数量
nettyThreads: 8
# 单节点配置
singleServerConfig:
# 客户端名称
clientName: ${ruoyi.name}
# 最小空闲连接数
connectionMinimumIdleSize: 8
# 连接池大小
connectionPoolSize: 32
# 连接空闲超时,单位:毫秒
idleConnectionTimeout: 10000
# 命令等待超时,单位:毫秒
timeout: 3000
# 发布和订阅连接池大小
subscriptionConnectionPoolSize: 50
--- # mail 邮件发送
mail:
enabled: false
host: smtp.163.com
port: 465
# 是否需要用户名密码验证
auth: true
# 发送方遵循RFC-822标准
from: xxx@163.com
# 用户名注意如果使用foxmail邮箱此处user为qq号
user: xxx@163.com
# 密码注意某些邮箱需要为SMTP服务单独设置密码详情查看相关帮助
pass: xxxxxxxxxx
# 使用 STARTTLS安全连接STARTTLS是对纯文本通信协议的扩展。
starttlsEnable: true
# 使用SSL安全连接
sslEnable: true
# SMTP超时时长单位毫秒缺省值不超时
timeout: 0
# Socket连接超时值单位毫秒缺省值不超时
connectionTimeout: 0
--- # sms 短信
sms:
enabled: false
# 阿里云 dysmsapi.aliyuncs.com
# 腾讯云 sms.tencentcloudapi.com
endpoint: "dysmsapi.aliyuncs.com"
accessKeyId: xxxxxxx
accessKeySecret: xxxxxx
signName: 测试
# 腾讯专用
sdkAppId:

@ -0,0 +1,174 @@
--- # 临时文件存储位置 避免临时文件被系统清理报错
spring.servlet.multipart.location: /ruoyi/server/temp
--- # 监控中心配置
spring.boot.admin.client:
# 增加客户端开关
enabled: true
url: http://localhost:9090/admin
instance:
service-host-type: IP
username: ruoyi
password: 123456
--- # xxl-job 配置
xxl.job:
# 执行器开关
enabled: true
# 调度中心地址:如调度中心集群部署存在多个地址则用逗号分隔。
admin-addresses: http://localhost:9100/xxl-job-admin
# 执行器通讯TOKEN非空时启用
access-token: xxl-job
executor:
# 执行器AppName执行器心跳注册分组依据为空则关闭自动注册
appname: xxl-job-executor
# 执行器端口号 执行器从9101开始往后写
port: 9101
# 执行器注册默认IP:PORT
address:
# 执行器IP默认自动获取IP
ip:
# 执行器运行日志文件存储磁盘路径
logpath: ./logs/xxl-job
# 执行器日志文件保存天数大于3生效
logretentiondays: 30
--- # 数据源配置
spring:
datasource:
type: com.zaxxer.hikari.HikariDataSource
# 动态数据源文档 https://www.kancloud.cn/tracy5546/dynamic-datasource/content
dynamic:
# 性能分析插件(有性能损耗 不建议生产环境使用)
p6spy: false
# 设置默认的数据源或者数据源组,默认值即为 master
primary: master
# 严格模式 匹配不到数据源则报错
strict: true
datasource:
# 主库数据源
master:
type: ${spring.datasource.type}
driverClassName: com.mysql.cj.jdbc.Driver
# jdbc 所有参数配置参考 https://lionli.blog.csdn.net/article/details/122018562
# rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题)
url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true
username: root
password: root
# 从库数据源
slave:
lazy: true
type: ${spring.datasource.type}
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true
username:
password:
# oracle:
# type: ${spring.datasource.type}
# driverClassName: oracle.jdbc.OracleDriver
# url: jdbc:oracle:thin:@//localhost:1521/XE
# username: ROOT
# password: root
# hikari:
# connectionTestQuery: SELECT 1 FROM DUAL
# postgres:
# type: ${spring.datasource.type}
# driverClassName: org.postgresql.Driver
# url: jdbc:postgresql://localhost:5432/postgres?useUnicode=true&characterEncoding=utf8&useSSL=true&autoReconnect=true&reWriteBatchedInserts=true
# username: root
# password: root
# sqlserver:
# type: ${spring.datasource.type}
# driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver
# url: jdbc:sqlserver://localhost:1433;DatabaseName=tempdb;SelectMethod=cursor;encrypt=false;rewriteBatchedStatements=true
# username: SA
# password: root
hikari:
# 最大连接池数量
maxPoolSize: 20
# 最小空闲线程数量
minIdle: 10
# 配置获取连接等待超时的时间
connectionTimeout: 30000
# 校验超时时间
validationTimeout: 5000
# 空闲连接存活最大时间默认10分钟
idleTimeout: 600000
# 此属性控制池中连接的最长生命周期值0表示无限生命周期默认30分钟
maxLifetime: 1800000
# 连接测试query配置检测连接是否有效
connectionTestQuery: SELECT 1
# 多久检查一次连接的活性
keepaliveTime: 30000
--- # redis 单机配置(单机与集群只能开启一个另一个需要注释掉)
spring:
redis:
# 地址
host: localhost
# 端口默认为6379
port: 6379
# 数据库索引
database: 0
# 密码(如没有密码请注释掉)
# password:
# 连接超时时间
timeout: 10s
# 是否开启ssl
ssl: false
redisson:
# redis key前缀
keyPrefix:
# 线程池数量
threads: 16
# Netty线程池数量
nettyThreads: 32
# 单节点配置
singleServerConfig:
# 客户端名称
clientName: ${ruoyi.name}
# 最小空闲连接数
connectionMinimumIdleSize: 32
# 连接池大小
connectionPoolSize: 64
# 连接空闲超时,单位:毫秒
idleConnectionTimeout: 10000
# 命令等待超时,单位:毫秒
timeout: 3000
# 发布和订阅连接池大小
subscriptionConnectionPoolSize: 50
--- # mail 邮件发送
mail:
enabled: false
host: smtp.163.com
port: 465
# 是否需要用户名密码验证
auth: true
# 发送方遵循RFC-822标准
from: xxx@163.com
# 用户名注意如果使用foxmail邮箱此处user为qq号
user: xxx@163.com
# 密码注意某些邮箱需要为SMTP服务单独设置密码详情查看相关帮助
pass: xxxxxxxxxx
# 使用 STARTTLS安全连接STARTTLS是对纯文本通信协议的扩展。
starttlsEnable: true
# 使用SSL安全连接
sslEnable: true
# SMTP超时时长单位毫秒缺省值不超时
timeout: 0
# Socket连接超时值单位毫秒缺省值不超时
connectionTimeout: 0
--- # sms 短信
sms:
enabled: false
# 阿里云 dysmsapi.aliyuncs.com
# 腾讯云 sms.tencentcloudapi.com
endpoint: "dysmsapi.aliyuncs.com"
accessKeyId: xxxxxxx
accessKeySecret: xxxxxx
signName: 测试
# 腾讯专用
sdkAppId:

@ -0,0 +1,272 @@
# 项目相关配置
ruoyi:
# 名称
name: RuoYi-Vue-Plus
# 版本
version: ${ruoyi-vue-plus.version}
# 版权年份
copyrightYear: 2022
# 实例演示开关
demoEnabled: true
# 获取ip地址开关
addressEnabled: true
# 缓存懒加载
cacheLazy: false
captcha:
# 页面 <参数设置> 可开启关闭 验证码校验
# 验证码类型 math 数组计算 char 字符验证
type: MATH
# line 线段干扰 circle 圆圈干扰 shear 扭曲干扰
category: CIRCLE
# 数字验证码位数
numberLength: 1
# 字符验证码长度
charLength: 4
# 开发环境配置
server:
# 服务器的HTTP端口默认为8080
port: 8080
servlet:
# 应用的访问路径
context-path: /
# undertow 配置
undertow:
# HTTP post内容的最大大小。当值为-1时默认值为大小是无限的
max-http-post-size: -1
# 以下的配置会影响buffer,这些buffer会用于服务器连接的IO操作,有点类似netty的池化内存管理
# 每块buffer的空间大小,越小的空间被利用越充分
buffer-size: 512
# 是否分配的直接内存
direct-buffers: true
threads:
# 设置IO线程数, 它主要执行非阻塞的任务,它们会负责多个连接, 默认设置每个CPU核心一个线程
io: 8
# 阻塞任务线程池, 当执行类似servlet请求阻塞操作, undertow会从这个线程池中取得线程,它的值设置取决于系统的负载
worker: 256
# 日志配置
logging:
level:
com.ruoyi: @logging.level@
org.springframework: warn
config: classpath:logback.xml
# 用户配置
user:
password:
# 密码最大错误次数
maxRetryCount: 5
# 密码锁定时间默认10分钟
lockTime: 10
# Spring配置
spring:
application:
name: ${ruoyi.name}
# 资源信息
messages:
# 国际化资源文件路径
basename: i18n/messages
profiles:
active: @profiles.active@
# 文件上传
servlet:
multipart:
# 单个文件大小
max-file-size: 10MB
# 设置总上传的文件大小
max-request-size: 20MB
# 服务模块
devtools:
restart:
# 热部署开关
enabled: true
mvc:
format:
date-time: yyyy-MM-dd HH:mm:ss
jackson:
# 日期格式化
date-format: yyyy-MM-dd HH:mm:ss
serialization:
# 格式化输出
indent_output: false
# 忽略无法转换的对象
fail_on_empty_beans: false
deserialization:
# 允许对象忽略json中不存在的属性
fail_on_unknown_properties: false
# Sa-Token配置
sa-token:
# token名称 (同时也是cookie名称)
token-name: Authorization
# token有效期 设为一天 (必定过期) 单位: 秒
timeout: 86400
# token临时有效期 (指定时间无操作就过期) 单位: 秒
activity-timeout: 1800
# 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录)
is-concurrent: true
# 在多人登录同一账号时是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token)
is-share: false
# 是否尝试从header里读取token
is-read-header: true
# 是否尝试从cookie里读取token
is-read-cookie: false
# token前缀
token-prefix: "Bearer"
# jwt秘钥
jwt-secret-key: abcdefghijklmnopqrstuvwxyz
# security配置
security:
# 排除路径
excludes:
# 静态资源
- /*.html
- /**/*.html
- /**/*.css
- /**/*.js
# 公共路径
- /favicon.ico
- /error
# swagger 文档配置
- /*/api-docs
- /*/api-docs/**
# actuator 监控配置
- /actuator
- /actuator/**
# MyBatisPlus配置
# https://baomidou.com/config/
mybatis-plus:
# 不支持多包, 如有需要可在注解配置 或 提升扫包等级
# 例如 com.**.**.mapper
mapperPackage: com.ruoyi.**.mapper
# 对应的 XML 文件位置
mapperLocations: classpath*:mapper/**/*Mapper.xml
# 实体扫描多个package用逗号或者分号分隔
typeAliasesPackage: com.ruoyi.**.domain
# 启动时是否检查 MyBatis XML 文件的存在,默认不检查
checkConfigLocation: false
configuration:
# 自动驼峰命名规则camel case映射
mapUnderscoreToCamelCase: true
# MyBatis 自动映射策略
# NONE不启用 PARTIAL只对非嵌套 resultMap 自动映射 FULL对所有 resultMap 自动映射
autoMappingBehavior: PARTIAL
# MyBatis 自动映射时未知列或未知属性处理策
# NONE不做处理 WARNING打印相关警告 FAILING抛出异常和详细信息
autoMappingUnknownColumnBehavior: NONE
# 更详细的日志输出 会有性能损耗 org.apache.ibatis.logging.stdout.StdOutImpl
# 关闭日志记录 (可单纯使用 p6spy 分析) org.apache.ibatis.logging.nologging.NoLoggingImpl
# 默认日志输出 org.apache.ibatis.logging.slf4j.Slf4jImpl
logImpl: org.apache.ibatis.logging.nologging.NoLoggingImpl
global-config:
# 是否打印 Logo banner
banner: true
dbConfig:
# 主键类型
# AUTO 自增 NONE 空 INPUT 用户输入 ASSIGN_ID 雪花 ASSIGN_UUID 唯一 UUID
idType: ASSIGN_ID
# 逻辑已删除值
logicDeleteValue: 2
# 逻辑未删除值
logicNotDeleteValue: 0
# 字段验证策略之 insert,在 insert 的时候的字段验证策略
# IGNORED 忽略 NOT_NULL 非NULL NOT_EMPTY 非空 DEFAULT 默认 NEVER 不加入 SQL
insertStrategy: NOT_NULL
# 字段验证策略之 update,在 update 的时候的字段验证策略
updateStrategy: NOT_NULL
# 字段验证策略之 select,在 select 的时候的字段验证策略既 wrapper 根据内部 entity 生成的 where 条件
where-strategy: NOT_NULL
# 数据加密
mybatis-encryptor:
# 是否开启加密
enable: false
# 默认加密算法
algorithm: BASE64
# 编码方式 BASE64/HEX。默认BASE64
encode: BASE64
# 安全秘钥 对称算法的秘钥 如AESSM4
password:
# 公私钥 非对称算法的公私钥 如SM2RSA
publicKey:
privateKey:
# Swagger配置
swagger:
info:
# 标题
title: '标题:${ruoyi.name}后台管理系统_接口文档'
# 描述
description: '描述:用于管理集团旗下公司的人员信息,具体包括XXX,XXX模块...'
# 版本
version: '版本号: ${ruoyi-vue-plus.version}'
# 作者信息
contact:
name: Lion Li
email: crazylionli@163.com
url: https://gitee.com/dromara/RuoYi-Vue-Plus
components:
# 鉴权方式配置
security-schemes:
apiKey:
type: APIKEY
in: HEADER
name: ${sa-token.token-name}
springdoc:
api-docs:
# 是否开启接口文档
enabled: true
swagger-ui:
# 持久化认证数据
persistAuthorization: true
#这里定义了两个分组,可定义多个,也可以不定义
group-configs:
- group: 1.演示模块
packages-to-scan: com.ruoyi.demo
- group: 2.系统模块
packages-to-scan: com.ruoyi.web
- group: 3.代码生成模块
packages-to-scan: com.ruoyi.generator
# 防止XSS攻击
xss:
# 过滤开关
enabled: true
# 排除链接(多个用逗号分隔)
excludes: /system/notice
# 匹配链接
urlPatterns: /system/*,/monitor/*,/tool/*
# 全局线程池相关配置
thread-pool:
# 是否开启线程池
enabled: false
# 队列最大长度
queueCapacity: 128
# 线程池维护线程所允许的空闲时间
keepAliveSeconds: 300
--- # 分布式锁 lock4j 全局配置
lock4j:
# 获取分布式锁超时时间,默认为 3000 毫秒
acquire-timeout: 3000
# 分布式锁的超时时间,默认为 30 秒
expire: 30000
--- # Actuator 监控端点的配置项
management:
endpoints:
web:
exposure:
include: '*'
endpoint:
health:
show-details: ALWAYS
logfile:
external-file: ./logs/sys-console.log

@ -0,0 +1,8 @@
Application Version: ${ruoyi-vue-plus.version}
Spring Boot Version: ${spring-boot.version}
__________ _____.___.__ ____ ____ __________.__
\______ \__ __ ____\__ | |__| \ \ / /_ __ ____ \______ \ | __ __ ______
| _/ | \/ _ \/ | | | ______ \ Y / | \_/ __ \ ______ | ___/ | | | \/ ___/
| | \ | ( <_> )____ | | /_____/ \ /| | /\ ___/ /_____/ | | | |_| | /\___ \
|____|_ /____/ \____// ______|__| \___/ |____/ \___ > |____| |____/____//____ >
\/ \/ \/ \/

@ -0,0 +1,45 @@
#错误消息
not.null=* 必须填写
user.jcaptcha.error=验证码错误
user.jcaptcha.expire=验证码已失效
user.not.exists=对不起, 您的账号:{0} 不存在.
user.password.not.match=用户不存在/密码错误
user.password.retry.limit.count=密码输入错误{0}次
user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定{1}分钟
user.password.delete=对不起,您的账号:{0} 已被删除
user.blocked=对不起,您的账号:{0} 已禁用,请联系管理员
role.blocked=角色已封禁,请联系管理员
user.logout.success=退出成功
length.not.valid=长度必须在{min}到{max}个字符之间
user.username.not.blank=用户名不能为空
user.username.not.valid=* 2到20个汉字、字母、数字或下划线组成且必须以非数字开头
user.username.length.valid=账户长度必须在{min}到{max}个字符之间
user.password.not.blank=用户密码不能为空
user.password.length.valid=用户密码长度必须在{min}到{max}个字符之间
user.password.not.valid=* 5-50个字符
user.email.not.valid=邮箱格式错误
user.phonenumber.not.blank=用户手机号不能为空
user.mobile.phone.number.not.valid=手机号格式错误
user.login.success=登录成功
user.register.success=注册成功
user.register.save.error=保存用户 {0} 失败,注册账号已存在
user.register.error=注册失败,请联系系统管理人员
user.notfound=请重新登录
user.forcelogout=管理员强制退出,请重新登录
user.unknown.error=未知错误,请重新登录
##文件上传消息
upload.exceed.maxSize=上传的文件大小超出限制的文件大小!<br/>允许的文件最大大小是:{0}MB
upload.filename.exceed.length=上传的文件名最长{0}个字符
##权限
no.permission=您没有数据的权限,请联系管理员添加权限 [{0}]
no.create.permission=您没有创建数据的权限,请联系管理员添加权限 [{0}]
no.update.permission=您没有修改数据的权限,请联系管理员添加权限 [{0}]
no.delete.permission=您没有删除数据的权限,请联系管理员添加权限 [{0}]
no.export.permission=您没有导出数据的权限,请联系管理员添加权限 [{0}]
no.view.permission=您没有查看数据的权限,请联系管理员添加权限 [{0}]
repeat.submit.message=不允许重复提交,请稍候再试
rate.limiter.message=访问过于频繁,请稍候再试
sms.code.not.blank=短信验证码不能为空
sms.code.retry.limit.count=短信验证码输入错误{0}次
sms.code.retry.limit.exceed=短信验证码输入错误{0}次,帐户锁定{1}分钟
xcx.code.not.blank=小程序code不能为空

@ -0,0 +1,45 @@
#错误消息
not.null=* Required fill in
user.jcaptcha.error=Captcha error
user.jcaptcha.expire=Captcha invalid
user.not.exists=Sorry, your account: {0} does not exist
user.password.not.match=User does not exist/Password error
user.password.retry.limit.count=Password input error {0} times
user.password.retry.limit.exceed=Password input error {0} times, account locked for {1} minutes
user.password.delete=Sorry, your account{0} has been deleted
user.blocked=Sorry, your account: {0} has been disabled. Please contact the administrator
role.blocked=Role disabledplease contact administrators
user.logout.success=Exit successful
length.not.valid=The length must be between {min} and {max} characters
user.username.not.blank=Username cannot be blank
user.username.not.valid=* 2 to 20 chinese characters, letters, numbers or underscores, and must start with a non number
user.username.length.valid=Account length must be between {min} and {max} characters
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.phonenumber.not.blank=Phone number cannot be blank
user.mobile.phone.number.not.valid=Phone number format error
user.login.success=Login successful
user.register.success=Register successful
user.register.save.error=Failed to save user {0}, The registered account already exists
user.register.error=Register failed, please contact system administrator
user.notfound=Please login again
user.forcelogout=The administrator is forced to exitplease login again
user.unknown.error=Unknown error, please login again
##文件上传消息
upload.exceed.maxSize=The uploaded file size exceeds the limit file size<br/>the maximum allowed file size is{0}MB
upload.filename.exceed.length=The maximum length of uploaded file name is {0} characters
##权限
no.permission=You do not have permission to the dataplease contact your administrator to add permissions [{0}]
no.create.permission=You do not have permission to create dataplease contact your administrator to add permissions [{0}]
no.update.permission=You do not have permission to modify dataplease contact your administrator to add permissions [{0}]
no.delete.permission=You do not have permission to delete dataplease contact your administrator to add permissions [{0}]
no.export.permission=You do not have permission to export dataplease contact your administrator to add permissions [{0}]
no.view.permission=You do not have permission to view dataplease contact your administrator to add permissions [{0}]
repeat.submit.message=Repeat submit is not allowed, please try again later
rate.limiter.message=Visit too frequently, please try again later
sms.code.not.blank=Sms code cannot be blank
sms.code.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
xcx.code.not.blank=Mini program code cannot be blank

@ -0,0 +1,45 @@
#错误消息
not.null=* 必须填写
user.jcaptcha.error=验证码错误
user.jcaptcha.expire=验证码已失效
user.not.exists=对不起, 您的账号:{0} 不存在.
user.password.not.match=用户不存在/密码错误
user.password.retry.limit.count=密码输入错误{0}次
user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定{1}分钟
user.password.delete=对不起,您的账号:{0} 已被删除
user.blocked=对不起,您的账号:{0} 已禁用,请联系管理员
role.blocked=角色已封禁,请联系管理员
user.logout.success=退出成功
length.not.valid=长度必须在{min}到{max}个字符之间
user.username.not.blank=用户名不能为空
user.username.not.valid=* 2到20个汉字、字母、数字或下划线组成且必须以非数字开头
user.username.length.valid=账户长度必须在{min}到{max}个字符之间
user.password.not.blank=用户密码不能为空
user.password.length.valid=用户密码长度必须在{min}到{max}个字符之间
user.password.not.valid=* 5-50个字符
user.email.not.valid=邮箱格式错误
user.phonenumber.not.blank=用户手机号不能为空
user.mobile.phone.number.not.valid=手机号格式错误
user.login.success=登录成功
user.register.success=注册成功
user.register.save.error=保存用户 {0} 失败,注册账号已存在
user.register.error=注册失败,请联系系统管理人员
user.notfound=请重新登录
user.forcelogout=管理员强制退出,请重新登录
user.unknown.error=未知错误,请重新登录
##文件上传消息
upload.exceed.maxSize=上传的文件大小超出限制的文件大小!<br/>允许的文件最大大小是:{0}MB
upload.filename.exceed.length=上传的文件名最长{0}个字符
##权限
no.permission=您没有数据的权限,请联系管理员添加权限 [{0}]
no.create.permission=您没有创建数据的权限,请联系管理员添加权限 [{0}]
no.update.permission=您没有修改数据的权限,请联系管理员添加权限 [{0}]
no.delete.permission=您没有删除数据的权限,请联系管理员添加权限 [{0}]
no.export.permission=您没有导出数据的权限,请联系管理员添加权限 [{0}]
no.view.permission=您没有查看数据的权限,请联系管理员添加权限 [{0}]
repeat.submit.message=不允许重复提交,请稍候再试
rate.limiter.message=访问过于频繁,请稍候再试
sms.code.not.blank=短信验证码不能为空
sms.code.retry.limit.count=短信验证码输入错误{0}次
sms.code.retry.limit.exceed=短信验证码输入错误{0}次,帐户锁定{1}分钟
xcx.code.not.blank=小程序code不能为空

@ -0,0 +1,129 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<property name="log.path" value="./logs"/>
<property name="console.log.pattern"
value="%red(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta(%logger{36}%n) - %msg%n"/>
<property name="log.pattern" value="%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"/>
<!-- 控制台输出 -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${console.log.pattern}</pattern>
<charset>utf-8</charset>
</encoder>
</appender>
<!-- 控制台输出 -->
<appender name="file_console" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/sys-console.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件名格式 -->
<fileNamePattern>${log.path}/sys-console.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志最大 1天 -->
<maxHistory>1</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
<charset>utf-8</charset>
</encoder>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<!-- 过滤的级别 -->
<level>INFO</level>
</filter>
</appender>
<!-- 系统日志输出 -->
<appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/sys-info.log</file>
<!-- 循环政策:基于时间创建日志文件 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件名格式 -->
<fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>60</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- 过滤的级别 -->
<level>INFO</level>
<!-- 匹配时的操作:接收(记录) -->
<onMatch>ACCEPT</onMatch>
<!-- 不匹配时的操作:拒绝(不记录) -->
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/sys-error.log</file>
<!-- 循环政策:基于时间创建日志文件 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件名格式 -->
<fileNamePattern>${log.path}/sys-error.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>60</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- 过滤的级别 -->
<level>ERROR</level>
<!-- 匹配时的操作:接收(记录) -->
<onMatch>ACCEPT</onMatch>
<!-- 不匹配时的操作:拒绝(不记录) -->
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- info异步输出 -->
<appender name="async_info" class="ch.qos.logback.classic.AsyncAppender">
<!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
<discardingThreshold>0</discardingThreshold>
<!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
<queueSize>512</queueSize>
<!-- 添加附加的appender,最多只能添加一个 -->
<appender-ref ref="file_info"/>
</appender>
<!-- error异步输出 -->
<appender name="async_error" class="ch.qos.logback.classic.AsyncAppender">
<!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
<discardingThreshold>0</discardingThreshold>
<!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
<queueSize>512</queueSize>
<!-- 添加附加的appender,最多只能添加一个 -->
<appender-ref ref="file_error"/>
</appender>
<!-- 整合 skywalking 控制台输出 tid -->
<!-- <appender name="console" class="ch.qos.logback.core.ConsoleAppender">-->
<!-- <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">-->
<!-- <layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">-->
<!-- <pattern>[%tid] ${console.log.pattern}</pattern>-->
<!-- </layout>-->
<!-- <charset>utf-8</charset>-->
<!-- </encoder>-->
<!-- </appender>-->
<!-- 整合 skywalking 推送采集日志 -->
<!-- <appender name="sky_log" class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.log.GRPCLogClientAppender">-->
<!-- <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">-->
<!-- <layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">-->
<!-- <pattern>[%tid] ${console.log.pattern}</pattern>-->
<!-- </layout>-->
<!-- <charset>utf-8</charset>-->
<!-- </encoder>-->
<!-- </appender>-->
<!--系统操作日志-->
<root level="info">
<appender-ref ref="console" />
<appender-ref ref="async_info" />
<appender-ref ref="async_error" />
<appender-ref ref="file_console" />
<!-- <appender-ref ref="sky_log"/>-->
</root>
</configuration>

@ -0,0 +1,28 @@
# p6spy 性能分析插件配置文件
modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory
# 自定义日志打印
logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger
#日志输出到控制台
appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger
# 使用日志系统记录 sql
#appender=com.p6spy.engine.spy.appender.Slf4JLogger
# 设置 p6spy driver 代理
#deregisterdrivers=true
# 取消JDBC URL前缀
useprefix=true
# 配置记录 Log 例外,可去掉的结果集有error,info,batch,debug,statement,commit,rollback,result,resultset.
excludecategories=info,debug,result,commit,resultset
# 日期格式
dateformat=yyyy-MM-dd HH:mm:ss
# SQL语句打印时间格式
databaseDialectTimestampFormat=yyyy-MM-dd HH:mm:ss
# 实际驱动可多个
#driverlist=org.h2.Driver
# 是否开启慢SQL记录
outagedetection=true
# 慢SQL记录标准 2 秒
outagedetectioninterval=2
# 是否过滤 Log
filter=true
# 过滤 Log 时所排除的 sql 关键字,以逗号分隔
exclude=SELECT 1

@ -0,0 +1,45 @@
package com.ruoyi.test;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
/**
*
*
* @author Lion Li
*/
@DisplayName("断言单元测试案例")
public class AssertUnitTest {
@DisplayName("测试 assertEquals 方法")
@Test
public void testAssertEquals() {
Assertions.assertEquals("666", new String("666"));
Assertions.assertNotEquals("666", new String("666"));
}
@DisplayName("测试 assertSame 方法")
@Test
public void testAssertSame() {
Object obj = new Object();
Object obj1 = obj;
Assertions.assertSame(obj, obj1);
Assertions.assertNotSame(obj, obj1);
}
@DisplayName("测试 assertTrue 方法")
@Test
public void testAssertTrue() {
Assertions.assertTrue(true);
Assertions.assertFalse(true);
}
@DisplayName("测试 assertNull 方法")
@Test
public void testAssertNull() {
Assertions.assertNull(null);
Assertions.assertNotNull(null);
}
}

@ -0,0 +1,70 @@
package com.ruoyi.test;
import com.ruoyi.common.config.RuoYiConfig;
import org.junit.jupiter.api.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.concurrent.TimeUnit;
/**
*
*
* @author Lion Li
*/
@SpringBootTest // 此注解只能在 springboot 主包下使用 需包含 main 方法与 yml 配置文件
@DisplayName("单元测试案例")
public class DemoUnitTest {
@Autowired
private RuoYiConfig ruoYiConfig;
@DisplayName("测试 @SpringBootTest @Test @DisplayName 注解")
@Test
public void testTest() {
System.out.println(ruoYiConfig);
}
@Disabled
@DisplayName("测试 @Disabled 注解")
@Test
public void testDisabled() {
System.out.println(ruoYiConfig);
}
@Timeout(value = 2L, unit = TimeUnit.SECONDS)
@DisplayName("测试 @Timeout 注解")
@Test
public void testTimeout() throws InterruptedException {
Thread.sleep(3000);
System.out.println(ruoYiConfig);
}
@DisplayName("测试 @RepeatedTest 注解")
@RepeatedTest(3)
public void testRepeatedTest() {
System.out.println(666);
}
@BeforeAll
public static void testBeforeAll() {
System.out.println("@BeforeAll ==================");
}
@BeforeEach
public void testBeforeEach() {
System.out.println("@BeforeEach ==================");
}
@AfterEach
public void testAfterEach() {
System.out.println("@AfterEach ==================");
}
@AfterAll
public static void testAfterAll() {
System.out.println("@AfterAll ==================");
}
}

@ -0,0 +1,72 @@
package com.ruoyi.test;
import com.ruoyi.common.enums.UserType;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.NullSource;
import org.junit.jupiter.params.provider.ValueSource;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
/**
*
*
* @author Lion Li
*/
@DisplayName("带参数单元测试案例")
public class ParamUnitTest {
@DisplayName("测试 @ValueSource 注解")
@ParameterizedTest
@ValueSource(strings = {"t1", "t2", "t3"})
public void testValueSource(String str) {
System.out.println(str);
}
@DisplayName("测试 @NullSource 注解")
@ParameterizedTest
@NullSource
public void testNullSource(String str) {
System.out.println(str);
}
@DisplayName("测试 @EnumSource 注解")
@ParameterizedTest
@EnumSource(UserType.class)
public void testEnumSource(UserType type) {
System.out.println(type.getUserType());
}
@DisplayName("测试 @MethodSource 注解")
@ParameterizedTest
@MethodSource("getParam")
public void testMethodSource(String str) {
System.out.println(str);
}
public static Stream<String> getParam() {
List<String> list = new ArrayList<>();
list.add("t1");
list.add("t2");
list.add("t3");
return list.stream();
}
@BeforeEach
public void testBeforeEach() {
System.out.println("@BeforeEach ==================");
}
@AfterEach
public void testAfterEach() {
System.out.println("@AfterEach ==================");
}
}

@ -0,0 +1,54 @@
package com.ruoyi.test;
import org.junit.jupiter.api.*;
import org.springframework.boot.test.context.SpringBootTest;
/**
*
*
* @author Lion Li
*/
@SpringBootTest
@DisplayName("标签单元测试案例")
public class TagUnitTest {
@Tag("dev")
@DisplayName("测试 @Tag dev")
@Test
public void testTagDev() {
System.out.println("dev");
}
@Tag("prod")
@DisplayName("测试 @Tag prod")
@Test
public void testTagProd() {
System.out.println("prod");
}
@Tag("local")
@DisplayName("测试 @Tag local")
@Test
public void testTagLocal() {
System.out.println("local");
}
@Tag("exclude")
@DisplayName("测试 @Tag exclude")
@Test
public void testTagExclude() {
System.out.println("exclude");
}
@BeforeEach
public void testBeforeEach() {
System.out.println("@BeforeEach ==================");
}
@AfterEach
public void testAfterEach() {
System.out.println("@AfterEach ==================");
}
}

@ -0,0 +1,164 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>ruoyi-vue-plus</artifactId>
<groupId>com.ruoyi</groupId>
<version>4.6.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>ruoyi-common</artifactId>
<description>
common通用工具
</description>
<dependencies>
<!-- Spring框架基本的核心工具 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<!-- SpringWeb模块 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<!-- Sa-Token 权限认证, 在线文档http://sa-token.dev33.cn/ -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-starter</artifactId>
</dependency>
<!-- Sa-Token 整合 jwt -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-jwt</artifactId>
</dependency>
<!-- 自定义验证注解 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!--常用工具类 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<!-- JSON工具类 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
</dependency>
<!-- yml解析器 -->
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
</dependency>
<!-- servlet包 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<!-- dynamic-datasource 多数据源-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-core</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-http</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-captcha</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-jwt</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-extra</artifactId>
</dependency>
<dependency>
<groupId>com.sun.mail</groupId>
<artifactId>jakarta.mail</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-webmvc-core</artifactId>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-javadoc</artifactId>
</dependency>
<!-- 自动生成YML配置关联JSON文件 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
<!--redisson-->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-data-27</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>lock4j-redisson-spring-boot-starter</artifactId>
</dependency>
<!-- 加密包引入 -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15to18</artifactId>
</dependency>
</dependencies>
</project>

@ -0,0 +1,24 @@
package com.ruoyi.common.annotation;
import com.ruoyi.common.excel.CellMergeStrategy;
import java.lang.annotation.*;
/**
* excel ()
*
* {@link CellMergeStrategy} 使
*
* @author Lion Li
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface CellMerge {
/**
* col index
*/
int index() default -1;
}

@ -0,0 +1,28 @@
package com.ruoyi.common.annotation;
import java.lang.annotation.*;
/**
*
*
*
*
* @author Lion Li
* @version 3.5.0
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataColumn {
/**
*
*/
String[] key() default "deptName";
/**
*
*/
String[] value() default "dept_id";
}

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

@ -0,0 +1,29 @@
package com.ruoyi.common.annotation;
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.ruoyi.common.jackson.DictDataJsonSerializer;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
*
*
* @author itino
* @deprecated 使
*/
@Deprecated
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD})
@JacksonAnnotationsInside
@JsonSerialize(using = DictDataJsonSerializer.class)
public @interface DictDataMapper {
/**
* type (: sys_user_sex)
*/
String dictType() default "";
}

@ -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;
/**
* AESSM4
*/
String password() default "";
/**
* RSASM2
*/
String publicKey() default "";
/**
* RSASM2
*/
String privateKey() default "";
/**
* BASE64
*/
EncodeType encode() default EncodeType.DEFAULT;
}

@ -0,0 +1,32 @@
package com.ruoyi.common.annotation;
import com.ruoyi.common.utils.StringUtils;
import java.lang.annotation.*;
/**
*
*
* @author Lion Li
*/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface ExcelDictFormat {
/**
* type (: sys_user_sex)
*/
String dictType() default "";
/**
* (: 0=,1=,2=)
*/
String readConverterExp() 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();
/**
* codecode
*/
String codeField() default "code";
/**
* texttext
*/
String textField() default "text";
}

@ -0,0 +1,47 @@
package com.ruoyi.common.annotation;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.enums.OperatorType;
import java.lang.annotation.*;
/**
*
*
* @author ruoyi
*/
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log {
/**
*
*/
String title() default "";
/**
*
*/
BusinessType businessType() default BusinessType.OTHER;
/**
*
*/
OperatorType operatorType() default OperatorType.MANAGE;
/**
*
*/
boolean isSaveRequestData() default true;
/**
*
*/
boolean isSaveResponseData() default true;
/**
*
*/
String[] excludeParamNames() default {};
}

@ -0,0 +1,41 @@
package com.ruoyi.common.annotation;
import com.ruoyi.common.enums.LimitType;
import java.lang.annotation.*;
/**
*
*
* @author Lion Li
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RateLimiter {
/**
* key,使Spring el
* #code.id #{#code}
*/
String key() default "";
/**
* ,
*/
int time() default 60;
/**
*
*/
int count() default 100;
/**
*
*/
LimitType limitType() default LimitType.DEFAULT;
/**
* {code}
*/
String message() default "{rate.limiter.message}";
}

@ -0,0 +1,29 @@
package com.ruoyi.common.annotation;
import java.lang.annotation.*;
import java.util.concurrent.TimeUnit;
/**
*
*
* @author Lion Li
*/
@Inherited
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RepeatSubmit {
/**
* (ms)
*/
int interval() default 5000;
TimeUnit timeUnit() default TimeUnit.MILLISECONDS;
/**
* {code}
*/
String message() default "{repeat.submit.message}";
}

@ -0,0 +1,24 @@
package com.ruoyi.common.annotation;
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.ruoyi.common.enums.SensitiveStrategy;
import com.ruoyi.common.jackson.SensitiveJsonSerializer;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
*
*
* @author zhujie
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@JacksonAnnotationsInside
@JsonSerialize(using = SensitiveJsonSerializer.class)
public @interface Sensitive {
SensitiveStrategy strategy();
}

@ -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();
}

@ -0,0 +1,85 @@
package com.ruoyi.common.captcha;
import cn.hutool.captcha.generator.CodeGenerator;
import cn.hutool.core.math.Calculator;
import cn.hutool.core.util.CharUtil;
import cn.hutool.core.util.RandomUtil;
import com.ruoyi.common.utils.StringUtils;
/**
*
*
* @author Lion Li
*/
public class UnsignedMathGenerator implements CodeGenerator {
private static final long serialVersionUID = -5514819971774091076L;
private static final String OPERATORS = "+-*";
/**
*
*/
private final int numberLength;
/**
*
*/
public UnsignedMathGenerator() {
this(2);
}
/**
*
*
* @param numberLength
*/
public UnsignedMathGenerator(int numberLength) {
this.numberLength = numberLength;
}
@Override
public String generate() {
final int limit = getLimit();
int a = RandomUtil.randomInt(limit);
int b = RandomUtil.randomInt(limit);
String max = Integer.toString(Math.max(a,b));
String min = Integer.toString(Math.min(a,b));
max = StringUtils.rightPad(max, this.numberLength, CharUtil.SPACE);
min = StringUtils.rightPad(min, this.numberLength, CharUtil.SPACE);
return max + RandomUtil.randomChar(OPERATORS) + min + '=';
}
@Override
public boolean verify(String code, String userInputCode) {
int result;
try {
result = Integer.parseInt(userInputCode);
} catch (NumberFormatException e) {
// 用户输入非数字
return false;
}
final int calculateResult = (int) Calculator.conversion(code);
return result == calculateResult;
}
/**
*
*
* @return
*/
public int getLength() {
return this.numberLength * 2 + 2;
}
/**
*
*
* @return
*/
private int getLimit() {
return Integer.parseInt("1" + StringUtils.repeat('0', this.numberLength));
}
}

@ -0,0 +1,54 @@
package com.ruoyi.common.config;
import lombok.Data;
import lombok.Getter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
*
*
* @author Lion Li
*/
@Data
@Component
@ConfigurationProperties(prefix = "ruoyi")
public class RuoYiConfig {
/**
*
*/
private String name;
/**
*
*/
private String version;
/**
*
*/
private String copyrightYear;
/**
*
*/
private boolean demoEnabled;
/**
*
*/
private boolean cacheLazy;
/**
*
*/
@Getter
private static boolean addressEnabled;
public void setAddressEnabled(boolean addressEnabled) {
RuoYiConfig.addressEnabled = addressEnabled;
}
}

@ -0,0 +1,49 @@
package com.ruoyi.common.constant;
/**
* key
*
* @author ruoyi
*/
public interface CacheConstants {
/**
* redis key
*/
String LOGIN_TOKEN_KEY = "Authorization:login:token:";
/**
* 线 redis key
*/
String ONLINE_TOKEN_KEY = "online_tokens:";
/**
* redis key
*/
String CAPTCHA_CODE_KEY = "captcha_codes:";
/**
* cache key
*/
String SYS_CONFIG_KEY = "sys_config:";
/**
* cache key
*/
String SYS_DICT_KEY = "sys_dict:";
/**
* redis key
*/
String REPEAT_SUBMIT_KEY = "repeat_submit:";
/**
* redis key
*/
String RATE_LIMIT_KEY = "rate_limit:";
/**
* redis key
*/
String PWD_ERR_CNT_KEY = "pwd_err_cnt:";
}

@ -0,0 +1,58 @@
package com.ruoyi.common.constant;
/**
*
* <p>
* key cacheNames#ttl#maxIdleTime#maxSize
* <p>
* ttl 0 0
* maxIdleTime LRU 0 0
* maxSize LRU 0 0
* <p>
* : test#60stest#0#60stest#0#1m#1000test#1h#0#500
*
* @author Lion Li
*/
public interface CacheNames {
/**
*
*/
String DEMO_CACHE = "demo:cache#60s#10m#20";
/**
*
*/
String SYS_CONFIG = "sys_config";
/**
*
*/
String SYS_DICT = "sys_dict";
/**
*
*/
String SYS_USER_NAME = "sys_user_name#30d";
/**
*
*/
String SYS_DEPT = "sys_dept#30d";
/**
* OSS
*/
String SYS_OSS = "sys_oss#30d";
/**
* OSS
*/
String SYS_OSS_CONFIG = "sys_oss_config";
/**
* 线
*/
String ONLINE_TOKEN = "online_tokens";
}

@ -0,0 +1,76 @@
package com.ruoyi.common.constant;
/**
*
*
* @author ruoyi
*/
public interface Constants {
/**
* UTF-8
*/
String UTF8 = "UTF-8";
/**
* GBK
*/
String GBK = "GBK";
/**
* www
*/
String WWW = "www.";
/**
* http
*/
String HTTP = "http://";
/**
* https
*/
String HTTPS = "https://";
/**
*
*/
String SUCCESS = "0";
/**
*
*/
String FAIL = "1";
/**
*
*/
String LOGIN_SUCCESS = "Success";
/**
*
*/
String LOGOUT = "Logout";
/**
*
*/
String REGISTER = "Register";
/**
*
*/
String LOGIN_FAIL = "Error";
/**
*
*/
Integer CAPTCHA_EXPIRATION = 2;
/**
*
*/
String TOKEN = "token";
}

@ -0,0 +1,193 @@
package com.ruoyi.common.constant;
/**
*
*
* @author ruoyi
*/
public interface GenConstants {
/**
*
*/
String TPL_CRUD = "crud";
/**
*
*/
String TPL_TREE = "tree";
/**
*
*/
String TPL_SUB = "sub";
/**
*
*/
String TREE_CODE = "treeCode";
/**
*
*/
String TREE_PARENT_CODE = "treeParentCode";
/**
*
*/
String TREE_NAME = "treeName";
/**
* ID
*/
String PARENT_MENU_ID = "parentMenuId";
/**
*
*/
String PARENT_MENU_NAME = "parentMenuName";
/**
*
*/
String[] COLUMNTYPE_STR = {"char", "varchar", "nvarchar", "varchar2"};
/**
*
*/
String[] COLUMNTYPE_TEXT = {"tinytext", "text", "mediumtext", "longtext"};
/**
*
*/
String[] COLUMNTYPE_TIME = {"datetime", "time", "date", "timestamp"};
/**
*
*/
String[] COLUMNTYPE_NUMBER = {"tinyint", "smallint", "mediumint", "int", "number", "integer",
"bit", "bigint", "float", "double", "decimal"};
/**
* BO
*/
String[] COLUMNNAME_NOT_ADD = {"create_by", "create_time", "del_flag", "update_by",
"update_time", "version"};
/**
* BO
*/
String[] COLUMNNAME_NOT_EDIT = {"create_by", "create_time", "del_flag", "update_by",
"update_time", "version"};
/**
* VO
*/
String[] COLUMNNAME_NOT_LIST = {"create_by", "create_time", "del_flag", "update_by",
"update_time", "version"};
/**
* BO
*/
String[] COLUMNNAME_NOT_QUERY = {"id", "create_by", "create_time", "del_flag", "update_by",
"update_time", "remark", "version"};
/**
* Entity
*/
String[] BASE_ENTITY = {"createBy", "createTime", "updateBy", "updateTime"};
/**
* Tree
*/
String[] TREE_ENTITY = {"parentName", "parentId", "children"};
/**
*
*/
String HTML_INPUT = "input";
/**
*
*/
String HTML_TEXTAREA = "textarea";
/**
*
*/
String HTML_SELECT = "select";
/**
*
*/
String HTML_RADIO = "radio";
/**
*
*/
String HTML_CHECKBOX = "checkbox";
/**
*
*/
String HTML_DATETIME = "datetime";
/**
*
*/
String HTML_IMAGE_UPLOAD = "imageUpload";
/**
*
*/
String HTML_FILE_UPLOAD = "fileUpload";
/**
*
*/
String HTML_EDITOR = "editor";
/**
*
*/
String TYPE_STRING = "String";
/**
*
*/
String TYPE_INTEGER = "Integer";
/**
*
*/
String TYPE_LONG = "Long";
/**
*
*/
String TYPE_DOUBLE = "Double";
/**
*
*/
String TYPE_BIGDECIMAL = "BigDecimal";
/**
*
*/
String TYPE_DATE = "Date";
/**
*
*/
String QUERY_LIKE = "LIKE";
/**
*
*/
String QUERY_EQ = "EQ";
/**
*
*/
String REQUIRE = "1";
}

@ -0,0 +1,93 @@
package com.ruoyi.common.constant;
/**
*
*
* @author Lion Li
*/
public interface HttpStatus {
/**
*
*/
int SUCCESS = 200;
/**
*
*/
int CREATED = 201;
/**
*
*/
int ACCEPTED = 202;
/**
*
*/
int NO_CONTENT = 204;
/**
*
*/
int MOVED_PERM = 301;
/**
*
*/
int SEE_OTHER = 303;
/**
*
*/
int NOT_MODIFIED = 304;
/**
*
*/
int BAD_REQUEST = 400;
/**
*
*/
int UNAUTHORIZED = 401;
/**
* 访
*/
int FORBIDDEN = 403;
/**
*
*/
int NOT_FOUND = 404;
/**
* http
*/
int BAD_METHOD = 405;
/**
*
*/
int CONFLICT = 409;
/**
*
*/
int UNSUPPORTED_TYPE = 415;
/**
*
*/
int ERROR = 500;
/**
*
*/
int NOT_IMPLEMENTED = 501;
/**
*
*/
int WARN = 601;
}

@ -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";
/**
* typelabel
*/
String DICT_TYPE_TO_LABEL = "dict_type_to_label";
/**
* ossIdurl
*/
String OSS_ID_TO_URL = "oss_id_to_url";
}

@ -0,0 +1,132 @@
package com.ruoyi.common.constant;
/**
*
*
* @author ruoyi
*/
public interface UserConstants {
/**
*
*/
String SYS_USER = "SYS_USER";
/**
*
*/
String NORMAL = "0";
/**
*
*/
String EXCEPTION = "1";
/**
*
*/
String USER_NORMAL = "0";
/**
*
*/
String USER_DISABLE = "1";
/**
*
*/
String ROLE_NORMAL = "0";
/**
*
*/
String ROLE_DISABLE = "1";
/**
*
*/
String DEPT_NORMAL = "0";
/**
*
*/
String DEPT_DISABLE = "1";
/**
*
*/
String DICT_NORMAL = "0";
/**
*
*/
String YES = "Y";
/**
*
*/
String YES_FRAME = "0";
/**
*
*/
String NO_FRAME = "1";
/**
*
*/
String MENU_NORMAL = "0";
/**
*
*/
String MENU_DISABLE = "1";
/**
*
*/
String TYPE_DIR = "M";
/**
*
*/
String TYPE_MENU = "C";
/**
*
*/
String TYPE_BUTTON = "F";
/**
* Layout
*/
String LAYOUT = "Layout";
/**
* ParentView
*/
String PARENT_VIEW = "ParentView";
/**
* InnerLink
*/
String INNER_LINK = "InnerLink";
/**
*
*/
int USERNAME_MIN_LENGTH = 2;
int USERNAME_MAX_LENGTH = 20;
/**
*
*/
int PASSWORD_MIN_LENGTH = 5;
int PASSWORD_MAX_LENGTH = 20;
/**
* ID
*/
Long ADMIN_ID = 1L;
}

@ -0,0 +1,52 @@
package com.ruoyi.common.convert;
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 lombok.extern.slf4j.Slf4j;
import java.math.BigDecimal;
/**
*
* Excel 15 15
*
* @author Lion Li
*/
@Slf4j
public class ExcelBigNumberConvert implements Converter<Long> {
@Override
public Class<Long> supportJavaTypeKey() {
return Long.class;
}
@Override
public CellDataTypeEnum supportExcelTypeKey() {
return CellDataTypeEnum.STRING;
}
@Override
public Long convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
return Convert.toLong(cellData.getData());
}
@Override
public WriteCellData<Object> convertToExcelData(Long object, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
if (ObjectUtil.isNotNull(object)) {
String str = Convert.toStr(object);
if (str.length() > 15) {
return new WriteCellData<>(str);
}
}
WriteCellData<Object> cellData = new WriteCellData<>(new BigDecimal(object));
cellData.setType(CellDataTypeEnum.NUMBER);
return cellData;
}
}

@ -0,0 +1,73 @@
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.ExcelDictFormat;
import com.ruoyi.common.core.service.DictService;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.common.utils.spring.SpringUtils;
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.Field;
/**
*
*
* @author Lion Li
*/
@Slf4j
public class ExcelDictConvert 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) {
ExcelDictFormat anno = getAnnotation(contentProperty.getField());
String type = anno.dictType();
String label = cellData.getStringValue();
String value;
if (StringUtils.isBlank(type)) {
value = ExcelUtil.reverseByExp(label, anno.readConverterExp(), anno.separator());
} else {
value = SpringUtils.getBean(DictService.class).getDictValue(type, label, anno.separator());
}
return Convert.convert(contentProperty.getField().getType(), value);
}
@Override
public WriteCellData<String> convertToExcelData(Object object, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
if (ObjectUtil.isNull(object)) {
return new WriteCellData<>("");
}
ExcelDictFormat anno = getAnnotation(contentProperty.getField());
String type = anno.dictType();
String value = Convert.toStr(object);
String label;
if (StringUtils.isBlank(type)) {
label = ExcelUtil.convertByExp(value, anno.readConverterExp(), anno.separator());
} else {
label = SpringUtils.getBean(DictService.class).getDictLabel(type, value, anno.separator());
}
return new WriteCellData<>(label);
}
private ExcelDictFormat getAnnotation(Field field) {
return AnnotationUtil.getAnnotation(field, ExcelDictFormat.class);
}
}

@ -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);
}
}

@ -0,0 +1,69 @@
package com.ruoyi.common.core.controller;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.helper.LoginHelper;
import com.ruoyi.common.utils.StringUtils;
/**
* web
*
* @author Lion Li
*/
public class BaseController {
/**
*
*
* @param rows
* @return
*/
protected R<Void> toAjax(int rows) {
return rows > 0 ? R.ok() : R.fail();
}
/**
*
*
* @param result
* @return
*/
protected R<Void> toAjax(boolean result) {
return result ? R.ok() : R.fail();
}
/**
*
*/
public String redirect(String url) {
return StringUtils.format("redirect:{}", url);
}
/**
*
*/
public LoginUser getLoginUser() {
return LoginHelper.getLoginUser();
}
/**
* id
*/
public Long getUserId() {
return LoginHelper.getUserId();
}
/**
* id
*/
public Long getDeptId() {
return LoginHelper.getDeptId();
}
/**
*
*/
public String getUsername() {
return LoginHelper.getUsername();
}
}

@ -0,0 +1,63 @@
package com.ruoyi.common.core.domain;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* Entity
*
* @author Lion Li
*/
@Data
public class BaseEntity implements Serializable {
private static final long serialVersionUID = 1L;
/**
*
*/
@JsonIgnore
@TableField(exist = false)
private String searchValue;
/**
*
*/
@TableField(fill = FieldFill.INSERT)
private String createBy;
/**
*
*/
@TableField(fill = FieldFill.INSERT)
private Date createTime;
/**
*
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private String updateBy;
/**
*
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
/**
*
*/
@JsonInclude(JsonInclude.Include.NON_EMPTY)
@TableField(exist = false)
private Map<String, Object> params = new HashMap<>();
}

@ -0,0 +1,112 @@
package com.ruoyi.common.core.domain;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.sql.SqlUtil;
import lombok.Data;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
/**
*
*
* @author Lion Li
*/
@Data
public class PageQuery implements Serializable {
private static final long serialVersionUID = 1L;
/**
*
*/
private Integer pageSize;
/**
*
*/
private Integer pageNum;
/**
*
*/
private String orderByColumn;
/**
* descasc
*/
private String isAsc;
/**
*
*/
public static final int DEFAULT_PAGE_NUM = 1;
/**
*
*/
public static final int DEFAULT_PAGE_SIZE = Integer.MAX_VALUE;
public <T> Page<T> build() {
Integer pageNum = ObjectUtil.defaultIfNull(getPageNum(), DEFAULT_PAGE_NUM);
Integer pageSize = ObjectUtil.defaultIfNull(getPageSize(), DEFAULT_PAGE_SIZE);
if (pageNum <= 0) {
pageNum = DEFAULT_PAGE_NUM;
}
Page<T> page = new Page<>(pageNum, pageSize);
List<OrderItem> orderItems = buildOrderItem();
if (CollUtil.isNotEmpty(orderItems)) {
page.addOrder(orderItems);
}
return page;
}
/**
*
*
* :
* {isAsc:"asc",orderByColumn:"id"} order by id asc
* {isAsc:"asc",orderByColumn:"id,createTime"} order by id asc,create_time asc
* {isAsc:"desc",orderByColumn:"id,createTime"} order by id desc,create_time desc
* {isAsc:"asc,desc",orderByColumn:"id,createTime"} order by id asc,create_time desc
*/
private List<OrderItem> buildOrderItem() {
if (StringUtils.isBlank(orderByColumn) || StringUtils.isBlank(isAsc)) {
return null;
}
String orderBy = SqlUtil.escapeOrderBySql(orderByColumn);
orderBy = StringUtils.toUnderScoreCase(orderBy);
// 兼容前端排序类型
isAsc = StringUtils.replaceEach(isAsc, new String[]{"ascending", "descending"}, new String[]{"asc", "desc"});
String[] orderByArr = orderBy.split(StringUtils.SEPARATOR);
String[] isAscArr = isAsc.split(StringUtils.SEPARATOR);
if (isAscArr.length != 1 && isAscArr.length != orderByArr.length) {
throw new ServiceException("排序参数有误");
}
List<OrderItem> list = new ArrayList<>();
// 每个字段各自排序
for (int i = 0; i < orderByArr.length; i++) {
String orderByStr = orderByArr[i];
String isAscStr = isAscArr.length == 1 ? isAscArr[0] : isAscArr[i];
if ("asc".equals(isAscStr)) {
list.add(OrderItem.asc(orderByStr));
} else if ("desc".equals(isAscStr)) {
list.add(OrderItem.desc(orderByStr));
} else {
throw new ServiceException("排序参数有误");
}
}
return list;
}
}

@ -0,0 +1,107 @@
package com.ruoyi.common.core.domain;
import com.ruoyi.common.constant.HttpStatus;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
*
*
* @author Lion Li
*/
@Data
@NoArgsConstructor
public class R<T> implements Serializable {
private static final long serialVersionUID = 1L;
/**
*
*/
public static final int SUCCESS = 200;
/**
*
*/
public static final int FAIL = 500;
private int code;
private String msg;
private T data;
public static <T> R<T> ok() {
return restResult(null, SUCCESS, "操作成功");
}
public static <T> R<T> ok(T data) {
return restResult(data, SUCCESS, "操作成功");
}
public static <T> R<T> ok(String msg) {
return restResult(null, SUCCESS, msg);
}
public static <T> R<T> ok(String msg, T data) {
return restResult(data, SUCCESS, msg);
}
public static <T> R<T> fail() {
return restResult(null, FAIL, "操作失败");
}
public static <T> R<T> fail(String msg) {
return restResult(null, FAIL, msg);
}
public static <T> R<T> fail(T data) {
return restResult(data, FAIL, "操作失败");
}
public static <T> R<T> fail(String msg, T data) {
return restResult(data, FAIL, msg);
}
public static <T> R<T> fail(int code, String msg) {
return restResult(null, code, msg);
}
/**
*
*
* @param msg
* @return
*/
public static <T> R<T> warn(String msg) {
return restResult(null, HttpStatus.WARN, msg);
}
/**
*
*
* @param msg
* @param data
* @return
*/
public static <T> R<T> warn(String msg, T data) {
return restResult(data, HttpStatus.WARN, msg);
}
private static <T> R<T> restResult(T data, int code, String msg) {
R<T> r = new R<>();
r.setCode(code);
r.setData(data);
r.setMsg(msg);
return r;
}
public static <T> Boolean isError(R<T> ret) {
return !isSuccess(ret);
}
public static <T> Boolean isSuccess(R<T> ret) {
return R.SUCCESS == ret.getCode();
}
}

@ -0,0 +1,39 @@
package com.ruoyi.common.core.domain;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.ArrayList;
import java.util.List;
/**
* Tree
*
* @author Lion Li
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class TreeEntity<T> extends BaseEntity {
private static final long serialVersionUID = 1L;
/**
*
*/
@TableField(exist = false)
private String parentName;
/**
* ID
*/
private Long parentId;
/**
*
*/
@TableField(exist = false)
private List<T> children = new ArrayList<>();
}

@ -0,0 +1,38 @@
package com.ruoyi.common.core.domain.dto;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
*
*
* @author Lion Li
*/
@Data
@NoArgsConstructor
public class RoleDTO implements Serializable {
/**
* ID
*/
private Long roleId;
/**
*
*/
private String roleName;
/**
*
*/
private String roleKey;
/**
* 12345
*/
private String dataScope;
}

@ -0,0 +1,60 @@
package com.ruoyi.common.core.domain.dto;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* 线
*
* @author ruoyi
*/
@Data
@NoArgsConstructor
public class UserOnlineDTO implements Serializable {
private static final long serialVersionUID = 1L;
/**
*
*/
private String tokenId;
/**
*
*/
private String deptName;
/**
*
*/
private String userName;
/**
* IP
*/
private String ipaddr;
/**
*
*/
private String loginLocation;
/**
*
*/
private String browser;
/**
*
*/
private String os;
/**
*
*/
private Long loginTime;
}

@ -0,0 +1,80 @@
package com.ruoyi.common.core.domain.entity;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import com.ruoyi.common.core.domain.TreeEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
/**
* sys_dept
*
* @author Lion Li
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("sys_dept")
public class SysDept extends TreeEntity<SysDept> {
private static final long serialVersionUID = 1L;
/**
* ID
*/
@TableId(value = "dept_id")
private Long deptId;
/**
*
*/
@NotBlank(message = "部门名称不能为空")
@Size(min = 0, max = 30, message = "部门名称长度不能超过{max}个字符")
private String deptName;
/**
*
*/
@NotNull(message = "显示顺序不能为空")
private Integer orderNum;
/**
*
*/
private String leader;
/**
*
*/
@Size(min = 0, max = 11, message = "联系电话长度不能超过{max}个字符")
private String phone;
/**
*
*/
@Email(message = "邮箱格式不正确")
@Size(min = 0, max = 50, message = "邮箱长度不能超过{max}个字符")
private String email;
/**
* :0,1
*/
private String status;
/**
* 0 2
*/
@TableLogic
private String delFlag;
/**
*
*/
private String ancestors;
}

@ -0,0 +1,100 @@
package com.ruoyi.common.core.domain.entity;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.ruoyi.common.annotation.ExcelDictFormat;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.convert.ExcelDictConvert;
import com.ruoyi.common.core.domain.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
/**
* sys_dict_data
*
* @author Lion Li
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("sys_dict_data")
@ExcelIgnoreUnannotated
public class SysDictData extends BaseEntity {
/**
*
*/
@ExcelProperty(value = "字典编码")
@TableId(value = "dict_code")
private Long dictCode;
/**
*
*/
@ExcelProperty(value = "字典排序")
private Integer dictSort;
/**
*
*/
@ExcelProperty(value = "字典标签")
@NotBlank(message = "字典标签不能为空")
@Size(min = 0, max = 100, message = "字典标签长度不能超过{max}个字符")
private String dictLabel;
/**
*
*/
@ExcelProperty(value = "字典键值")
@NotBlank(message = "字典键值不能为空")
@Size(min = 0, max = 100, message = "字典键值长度不能超过{max}个字符")
private String dictValue;
/**
*
*/
@ExcelProperty(value = "字典类型")
@NotBlank(message = "字典类型不能为空")
@Size(min = 0, max = 100, message = "字典类型长度不能超过{max}个字符")
private String dictType;
/**
*
*/
@Size(min = 0, max = 100, message = "样式属性长度不能超过{max}个字符")
private String cssClass;
/**
*
*/
private String listClass;
/**
* Y N
*/
@ExcelProperty(value = "是否默认", converter = ExcelDictConvert.class)
@ExcelDictFormat(dictType = "sys_yes_no")
private String isDefault;
/**
* 0 1
*/
@ExcelProperty(value = "状态", converter = ExcelDictConvert.class)
@ExcelDictFormat(dictType = "sys_normal_disable")
private String status;
/**
*
*/
private String remark;
public boolean getDefault() {
return UserConstants.YES.equals(this.isDefault);
}
}

@ -0,0 +1,65 @@
package com.ruoyi.common.core.domain.entity;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.ruoyi.common.annotation.ExcelDictFormat;
import com.ruoyi.common.convert.ExcelDictConvert;
import com.ruoyi.common.core.domain.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
/**
* sys_dict_type
*
* @author Lion Li
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("sys_dict_type")
@ExcelIgnoreUnannotated
public class SysDictType extends BaseEntity {
/**
*
*/
@ExcelProperty(value = "字典主键")
@TableId(value = "dict_id")
private Long dictId;
/**
*
*/
@ExcelProperty(value = "字典名称")
@NotBlank(message = "字典名称不能为空")
@Size(min = 0, max = 100, message = "字典类型名称长度不能超过{max}个字符")
private String dictName;
/**
*
*/
@ExcelProperty(value = "字典类型")
@NotBlank(message = "字典类型不能为空")
@Size(min = 0, max = 100, message = "字典类型类型长度不能超过{max}个字符")
@Pattern(regexp = "^[a-z][a-z0-9_]*$", message = "字典类型必须以字母开头,且只能为(小写字母,数字,下滑线)")
private String dictType;
/**
* 0 1
*/
@ExcelProperty(value = "状态", converter = ExcelDictConvert.class)
@ExcelDictFormat(dictType = "sys_normal_disable")
private String status;
/**
*
*/
private String remark;
}

@ -0,0 +1,104 @@
package com.ruoyi.common.core.domain.entity;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.ruoyi.common.core.domain.TreeEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
/**
* sys_menu
*
* @author Lion Li
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("sys_menu")
public class SysMenu extends TreeEntity<SysMenu> {
/**
* ID
*/
@TableId(value = "menu_id")
private Long menuId;
/**
*
*/
@NotBlank(message = "菜单名称不能为空")
@Size(min = 0, max = 50, message = "菜单名称长度不能超过{max}个字符")
private String menuName;
/**
*
*/
@NotNull(message = "显示顺序不能为空")
private Integer orderNum;
/**
*
*/
@Size(min = 0, max = 200, message = "路由地址不能超过{max}个字符")
private String path;
/**
*
*/
@Size(min = 0, max = 200, message = "组件路径不能超过{max}个字符")
private String component;
/**
*
*/
private String queryParam;
/**
* 0 1
*/
private String isFrame;
/**
* 0 1
*/
private String isCache;
/**
* M C F
*/
@NotBlank(message = "菜单类型不能为空")
private String menuType;
/**
* 0 1
*/
private String visible;
/**
* 0 1
*/
private String status;
/**
*
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
@Size(min = 0, max = 100, message = "权限标识长度不能超过{max}个字符")
private String perms;
/**
*
*/
private String icon;
/**
*
*/
private String remark;
}

@ -0,0 +1,124 @@
package com.ruoyi.common.core.domain.entity;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import com.ruoyi.common.annotation.ExcelDictFormat;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.convert.ExcelDictConvert;
import com.ruoyi.common.core.domain.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
/**
* sys_role
*
* @author Lion Li
*/
@Data
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
@TableName("sys_role")
@ExcelIgnoreUnannotated
public class SysRole extends BaseEntity {
/**
* ID
*/
@ExcelProperty(value = "角色序号")
@TableId(value = "role_id")
private Long roleId;
/**
*
*/
@ExcelProperty(value = "角色名称")
@NotBlank(message = "角色名称不能为空")
@Size(min = 0, max = 30, message = "角色名称长度不能超过{max}个字符")
private String roleName;
/**
*
*/
@ExcelProperty(value = "角色权限")
@NotBlank(message = "权限字符不能为空")
@Size(min = 0, max = 100, message = "权限字符长度不能超过{max}个字符")
private String roleKey;
/**
*
*/
@ExcelProperty(value = "角色排序")
@NotNull(message = "显示顺序不能为空")
private Integer roleSort;
/**
* 12345
*/
@ExcelProperty(value = "数据范围", converter = ExcelDictConvert.class)
@ExcelDictFormat(readConverterExp = "1=所有数据权限,2=自定义数据权限,3=本部门数据权限,4=本部门及以下数据权限,5=仅本人数据权限")
private String dataScope;
/**
* 0 1
*/
private Boolean menuCheckStrictly;
/**
* 0 1
*/
private Boolean deptCheckStrictly;
/**
* 0 1
*/
@ExcelProperty(value = "角色状态", converter = ExcelDictConvert.class)
@ExcelDictFormat(dictType = "sys_normal_disable")
private String status;
/**
* 0 2
*/
@TableLogic
private String delFlag;
/**
*
*/
private String remark;
/**
*
*/
@TableField(exist = false)
private boolean flag = false;
/**
*
*/
@TableField(exist = false)
private Long[] menuIds;
/**
*
*/
@TableField(exist = false)
private Long[] deptIds;
public SysRole(Long roleId) {
this.roleId = roleId;
}
public boolean isAdmin() {
return UserConstants.ADMIN_ID.equals(this.roleId);
}
}

@ -0,0 +1,168 @@
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;
import com.ruoyi.common.enums.SensitiveStrategy;
import com.ruoyi.common.xss.Xss;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
import java.util.Date;
import java.util.List;
/**
* sys_user
*
* @author Lion Li
*/
@Data
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
@TableName("sys_user")
public class SysUser extends BaseEntity {
/**
* ID
*/
@TableId(value = "user_id")
private Long userId;
/**
* ID
*/
private Long deptId;
/**
*
*/
@Xss(message = "用户账号不能包含脚本字符")
@NotBlank(message = "用户账号不能为空")
@Size(min = 0, max = 30, message = "用户账号长度不能超过{max}个字符")
private String userName;
/**
*
*/
@Xss(message = "用户昵称不能包含脚本字符")
@Size(min = 0, max = 30, message = "用户昵称长度不能超过{max}个字符")
private String nickName;
/**
* sys_user
*/
private String userType;
/**
*
*/
@Sensitive(strategy = SensitiveStrategy.EMAIL)
@Email(message = "邮箱格式不正确")
@Size(min = 0, max = 50, message = "邮箱长度不能超过{max}个字符")
private String email;
/**
*
*/
@Sensitive(strategy = SensitiveStrategy.PHONE)
private String phonenumber;
/**
*
*/
private String sex;
/**
*
*/
private String avatar;
/**
*
*/
@TableField(
insertStrategy = FieldStrategy.NOT_EMPTY,
updateStrategy = FieldStrategy.NOT_EMPTY,
whereStrategy = FieldStrategy.NOT_EMPTY
)
private String password;
@JsonIgnore
@JsonProperty
public String getPassword() {
return password;
}
/**
* 0 1
*/
private String status;
/**
* 0 2
*/
@TableLogic
private String delFlag;
/**
* IP
*/
private String loginIp;
/**
*
*/
private Date loginDate;
/**
*
*/
private String remark;
/**
*
*/
@TableField(exist = false)
private SysDept dept;
/**
*
*/
@TableField(exist = false)
private List<SysRole> roles;
/**
*
*/
@TableField(exist = false)
private Long[] roleIds;
/**
*
*/
@TableField(exist = false)
private Long[] postIds;
/**
* ID
*/
@TableField(exist = false)
private Long roleId;
public SysUser(Long userId) {
this.userId = userId;
}
public boolean isAdmin() {
return UserConstants.ADMIN_ID.equals(this.userId);
}
}

@ -0,0 +1,44 @@
package com.ruoyi.common.core.domain.event;
import lombok.Data;
import javax.servlet.http.HttpServletRequest;
import java.io.Serializable;
/**
*
*
* @author Lion Li
*/
@Data
public class LogininforEvent implements Serializable {
private static final long serialVersionUID = 1L;
/**
*
*/
private String username;
/**
* 0 1
*/
private String status;
/**
*
*/
private String message;
/**
*
*/
private HttpServletRequest request;
/**
*
*/
private Object[] args;
}

@ -0,0 +1,104 @@
package com.ruoyi.common.core.domain.event;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
*
*
* @author Lion Li
*/
@Data
public class OperLogEvent implements Serializable {
private static final long serialVersionUID = 1L;
/**
*
*/
private Long operId;
/**
*
*/
private String title;
/**
* 0 1 2 3
*/
private Integer businessType;
/**
*
*/
private Integer[] businessTypes;
/**
*
*/
private String method;
/**
*
*/
private String requestMethod;
/**
* 0 1 2
*/
private Integer operatorType;
/**
*
*/
private String operName;
/**
*
*/
private String deptName;
/**
* url
*/
private String operUrl;
/**
*
*/
private String operIp;
/**
*
*/
private String operLocation;
/**
*
*/
private String operParam;
/**
*
*/
private String jsonResult;
/**
* 0 1
*/
private Integer status;
/**
*
*/
private String errorMsg;
/**
*
*/
private Date operTime;
}

@ -0,0 +1,42 @@
package com.ruoyi.common.core.domain.model;
import com.ruoyi.common.constant.UserConstants;
import lombok.Data;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotBlank;
/**
*
*
* @author Lion Li
*/
@Data
public class LoginBody {
/**
*
*/
@NotBlank(message = "{user.username.not.blank}")
@Length(min = UserConstants.USERNAME_MIN_LENGTH, max = UserConstants.USERNAME_MAX_LENGTH, message = "{user.username.length.valid}")
private String username;
/**
*
*/
@NotBlank(message = "{user.password.not.blank}")
@Length(min = UserConstants.PASSWORD_MIN_LENGTH, max = UserConstants.PASSWORD_MAX_LENGTH, message = "{user.password.length.valid}")
private String password;
/**
*
*/
private String code;
/**
*
*/
private String uuid;
}

@ -0,0 +1,116 @@
package com.ruoyi.common.core.domain.model;
import com.ruoyi.common.core.domain.dto.RoleDTO;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.List;
import java.util.Set;
/**
*
*
* @author Lion Li
*/
@Data
@NoArgsConstructor
public class LoginUser implements Serializable {
private static final long serialVersionUID = 1L;
/**
* ID
*/
private Long userId;
/**
* ID
*/
private Long deptId;
/**
*
*/
private String deptName;
/**
*
*/
private String token;
/**
*
*/
private String userType;
/**
*
*/
private Long loginTime;
/**
*
*/
private Long expireTime;
/**
* IP
*/
private String ipaddr;
/**
*
*/
private String loginLocation;
/**
*
*/
private String browser;
/**
*
*/
private String os;
/**
*
*/
private Set<String> menuPermission;
/**
*
*/
private Set<String> rolePermission;
/**
*
*/
private String username;
/**
*
*/
private List<RoleDTO> roles;
/**
* ID
*/
private Long roleId;
/**
* id
*/
public String getLoginId() {
if (userType == null) {
throw new IllegalArgumentException("用户类型不能为空");
}
if (userId == null) {
throw new IllegalArgumentException("用户ID不能为空");
}
return userType + ":" + userId;
}
}

@ -0,0 +1,17 @@
package com.ruoyi.common.core.domain.model;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
*
*
* @author Lion Li
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class RegisterBody extends LoginBody {
private String userType;
}

@ -0,0 +1,28 @@
package com.ruoyi.common.core.domain.model;
import lombok.Data;
import javax.validation.constraints.NotBlank;
/**
*
*
* @author Lion Li
*/
@Data
public class SmsLoginBody {
/**
*
*/
@NotBlank(message = "{user.phonenumber.not.blank}")
private String phonenumber;
/**
*
*/
@NotBlank(message = "{sms.code.not.blank}")
private String smsCode;
}

@ -0,0 +1,24 @@
package com.ruoyi.common.core.domain.model;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
/**
*
*
* @author Lion Li
*/
@Data
@EqualsAndHashCode(callSuper = true)
@NoArgsConstructor
public class XcxLoginUser extends LoginUser {
private static final long serialVersionUID = 1L;
/**
* openid
*/
private String openid;
}

@ -0,0 +1,192 @@
package com.ruoyi.common.core.mapper;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.*;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.toolkit.Db;
import com.ruoyi.common.utils.BeanCopyUtils;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
* Mapper ,
*
* @param <M> mapper
* @param <T> table
* @param <V> vo
* @author Lion Li
* @since 2021-05-13
*/
@SuppressWarnings("unchecked")
public interface BaseMapperPlus<M, T, V> extends BaseMapper<T> {
Log log = LogFactory.getLog(BaseMapperPlus.class);
default Class<V> currentVoClass() {
return (Class<V>) ReflectionKit.getSuperClassGenericType(this.getClass(), BaseMapperPlus.class, 2);
}
default Class<T> currentModelClass() {
return (Class<T>) ReflectionKit.getSuperClassGenericType(this.getClass(), BaseMapperPlus.class, 1);
}
default Class<M> currentMapperClass() {
return (Class<M>) ReflectionKit.getSuperClassGenericType(this.getClass(), BaseMapperPlus.class, 0);
}
default List<T> selectList() {
return this.selectList(new QueryWrapper<>());
}
/**
*
*/
default boolean insertBatch(Collection<T> entityList) {
return Db.saveBatch(entityList);
}
/**
*
*/
default boolean updateBatchById(Collection<T> entityList) {
return Db.updateBatchById(entityList);
}
/**
*
*/
default boolean insertOrUpdateBatch(Collection<T> entityList) {
return Db.saveOrUpdateBatch(entityList);
}
/**
* ()
*/
default boolean insertBatch(Collection<T> entityList, int batchSize) {
return Db.saveBatch(entityList, batchSize);
}
/**
* ()
*/
default boolean updateBatchById(Collection<T> entityList, int batchSize) {
return Db.updateBatchById(entityList, batchSize);
}
/**
* ()
*/
default boolean insertOrUpdateBatch(Collection<T> entityList, int batchSize) {
return Db.saveOrUpdateBatch(entityList, batchSize);
}
/**
* ()
*/
default boolean insertOrUpdate(T entity) {
return Db.saveOrUpdate(entity);
}
default V selectVoById(Serializable id) {
return selectVoById(id, this.currentVoClass());
}
/**
* ID
*/
default <C> C selectVoById(Serializable id, Class<C> voClass) {
T obj = this.selectById(id);
if (ObjectUtil.isNull(obj)) {
return null;
}
return BeanCopyUtils.copy(obj, voClass);
}
default List<V> selectVoBatchIds(Collection<? extends Serializable> idList) {
return selectVoBatchIds(idList, this.currentVoClass());
}
/**
* ID
*/
default <C> List<C> selectVoBatchIds(Collection<? extends Serializable> idList, Class<C> voClass) {
List<T> list = this.selectBatchIds(idList);
if (CollUtil.isEmpty(list)) {
return CollUtil.newArrayList();
}
return BeanCopyUtils.copyList(list, voClass);
}
default List<V> selectVoByMap(Map<String, Object> map) {
return selectVoByMap(map, this.currentVoClass());
}
/**
* columnMap
*/
default <C> List<C> selectVoByMap(Map<String, Object> map, Class<C> voClass) {
List<T> list = this.selectByMap(map);
if (CollUtil.isEmpty(list)) {
return CollUtil.newArrayList();
}
return BeanCopyUtils.copyList(list, voClass);
}
default V selectVoOne(Wrapper<T> wrapper) {
return selectVoOne(wrapper, this.currentVoClass());
}
/**
* entity
*/
default <C> C selectVoOne(Wrapper<T> wrapper, Class<C> voClass) {
T obj = this.selectOne(wrapper);
if (ObjectUtil.isNull(obj)) {
return null;
}
return BeanCopyUtils.copy(obj, voClass);
}
default List<V> selectVoList(Wrapper<T> wrapper) {
return selectVoList(wrapper, this.currentVoClass());
}
/**
* entity
*/
default <C> List<C> selectVoList(Wrapper<T> wrapper, Class<C> voClass) {
List<T> list = this.selectList(wrapper);
if (CollUtil.isEmpty(list)) {
return CollUtil.newArrayList();
}
return BeanCopyUtils.copyList(list, voClass);
}
default <P extends IPage<V>> P selectVoPage(IPage<T> page, Wrapper<T> wrapper) {
return selectVoPage(page, wrapper, this.currentVoClass());
}
/**
* VO
*/
default <C, P extends IPage<C>> P selectVoPage(IPage<T> page, Wrapper<T> wrapper, Class<C> voClass) {
IPage<T> pageData = this.selectPage(page, wrapper);
IPage<C> voPage = new Page<>(pageData.getCurrent(), pageData.getSize(), pageData.getTotal());
if (CollUtil.isEmpty(pageData.getRecords())) {
return (P) voPage;
}
voPage.setRecords(BeanCopyUtils.copyList(pageData.getRecords(), voClass));
return (P) voPage;
}
}

@ -0,0 +1,78 @@
package com.ruoyi.common.core.page;
import cn.hutool.http.HttpStatus;
import com.baomidou.mybatisplus.core.metadata.IPage;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.List;
/**
*
*
* @author Lion Li
*/
@Data
@NoArgsConstructor
public class TableDataInfo<T> implements Serializable {
private static final long serialVersionUID = 1L;
/**
*
*/
private long total;
/**
*
*/
private List<T> rows;
/**
*
*/
private int code;
/**
*
*/
private String msg;
/**
*
*
* @param list
* @param total
*/
public TableDataInfo(List<T> list, long total) {
this.rows = list;
this.total = total;
}
public static <T> TableDataInfo<T> build(IPage<T> page) {
TableDataInfo<T> rspData = new TableDataInfo<>();
rspData.setCode(HttpStatus.HTTP_OK);
rspData.setMsg("查询成功");
rspData.setRows(page.getRecords());
rspData.setTotal(page.getTotal());
return rspData;
}
public static <T> TableDataInfo<T> build(List<T> list) {
TableDataInfo<T> rspData = new TableDataInfo<>();
rspData.setCode(HttpStatus.HTTP_OK);
rspData.setMsg("查询成功");
rspData.setRows(list);
rspData.setTotal(list.size());
return rspData;
}
public static <T> TableDataInfo<T> build() {
TableDataInfo<T> rspData = new TableDataInfo<>();
rspData.setCode(HttpStatus.HTTP_OK);
rspData.setMsg("查询成功");
return rspData;
}
}

@ -0,0 +1,18 @@
package com.ruoyi.common.core.service;
/**
*
*
* @author Lion Li
*/
public interface ConfigService {
/**
* key
*
* @param configKey key
* @return
*/
String getConfigValue(String configKey);
}

@ -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,57 @@
package com.ruoyi.common.core.service;
/**
*
*
* @author Lion Li
*/
public interface DictService {
/**
*
*/
String SEPARATOR = ",";
/**
*
*
* @param dictType
* @param dictValue
* @return
*/
default String getDictLabel(String dictType, String dictValue) {
return getDictLabel(dictType, dictValue, SEPARATOR);
}
/**
*
*
* @param dictType
* @param dictLabel
* @return
*/
default String getDictValue(String dictType, String dictLabel) {
return getDictValue(dictType, dictLabel, SEPARATOR);
}
/**
*
*
* @param dictType
* @param dictValue
* @param separator
* @return
*/
String getDictLabel(String dictType, String dictValue, String separator);
/**
*
*
* @param dictType
* @param dictLabel
* @param separator
* @return
*/
String getDictValue(String dictType, String dictLabel, String separator);
}

@ -0,0 +1,18 @@
package com.ruoyi.common.core.service;
/**
* OSS
*
* @author Lion Li
*/
public interface OssService {
/**
* ossIdurl
*
* @param ossIds ossId
* @return url
*/
String selectUrlByIds(String ossIds);
}

@ -0,0 +1,18 @@
package com.ruoyi.common.core.service;
/**
*
*
*
*
* @author Lion Li
* @version 3.6.0
*/
public interface SensitiveService {
/**
*
*/
boolean isSensitive();
}

@ -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,9 @@
package com.ruoyi.common.core.validate;
/**
* add
*
* @author Lion Li
*/
public interface AddGroup {
}

@ -0,0 +1,9 @@
package com.ruoyi.common.core.validate;
/**
* edit
*
* @author Lion Li
*/
public interface EditGroup {
}

@ -0,0 +1,9 @@
package com.ruoyi.common.core.validate;
/**
* query
*
* @author Lion Li
*/
public interface QueryGroup {
}

@ -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;
}

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

Loading…
Cancel
Save