ruoyi-geek-springboot3

This commit is contained in:
廖德云 2025-11-24 22:54:02 +08:00
commit d97d2b55db
705 changed files with 79294 additions and 0 deletions

50
.gitignore vendored Normal file
View File

@ -0,0 +1,50 @@
######################################################################
# 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/
dist/
nbdist/
.nb-gradle/
######################################################################
# Others
*.log
*.xml.versionsBackup
*.swp
*.lck
!*/build/*.java
!*/build/*.html
!*/build/*.xml
effective-pom.xml

16
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,16 @@
{
// 使 IntelliSense
//
// 访: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "java",
"name": "RuoYiApplication",
"request": "launch",
"mainClass": "com.ruoyi.RuoYiApplication",
"projectName": "ruoyi-admin",
"vmArgs": "-Dfile.encoding=GBK "
}
]
}

65
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,65 @@
{
"java.configuration.updateBuildConfiguration": "interactive",
"java.compile.nullAnalysis.mode": "disabled",
"maven.executable.options": "-T 4",
"maven.pomfile.autoUpdateEffectivePOM": true,
"java.debug.settings.hotCodeReplace": "auto",
"java.jdt.ls.vmargs": "-XX:+UseParallelGC -XX:GCTimeRatio=9 -XX:AdaptiveSizePolicyWeight=90 -Dsun.zip.disableMemoryMapping=true -Xmx4G -Xms4G -Xlog:disable",
"maven.excludedFolders": [
"**/.vscode",
"**/.idea",
"**/target",
"**/.*",
"**/node_modules",
"**/target",
"**/bin",
"**/archetype-resources"
],
"boot-java.rewrite.refactorings.on": true,
"maven.executable.preferMavenWrapper": true,
"java.import.maven.enabled": true,
"dbcode.connections": [
{
"connectionId": "btr-_nFe7R0oOvCj0mMun",
"name": "ry-mysql",
"driver": "mysql",
"connectionType": "host",
"host": "127.0.0.1",
"port": 3306,
"ssl": false,
"username": "root",
"password": "123456",
"savePassword": "secretStorage",
"database": "ry",
"connectionTimeout": 30,
"driverOptions": {
"retrievePublickey": true
}
},
{
"connectionId": "7NX2UhXl__9t3Ca6TzEsB",
"name": "ry-postgres",
"driver": "postgres",
"connectionType": "host",
"host": "127.0.0.1",
"port": 5432,
"ssl": false,
"username": "postgres",
"password": "123456",
"savePassword": "secretStorage",
"connectionTimeout": 30
},
{
"connectionId": "fNsY4HlOb21w_5TnIGy_d",
"name": "localhost",
"driver": "redis",
"connectionType": "host",
"host": "127.0.0.1",
"port": 6379,
"ssl": false,
"savePassword": "na",
"readOnly": false,
"connectionTimeout": 30
}
],
}

20
LICENSE Normal file
View File

@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2018 RuoYi
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.

219
README.md Normal file
View File

@ -0,0 +1,219 @@
<p align="center">
<span>
<img alt="logo" src="https://oscimg.oschina.net/oscnet/up-d3d0a9303e11d522a06cd263f3079027715.png">
</span>
<span>+</span>
<span>
<img alt="logo" src="./doc/image/logo.png">
</span>
</p>
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">RuoYi-Geek v3.8.9-G</h1>
<h4 align="center">基于SpringBoot3+Vue3前后端分离的Java快速开发框架</h4>
<p align="center">
<img src="https://img.shields.io/github/license/mashape/apistatus.svg">
</p>
# 当前版本是3.8.9-G
本人的其他两个推荐搭配的项目
1. [RuoYi-App-Geek: 这是若依极客生态的小程序版本 (gitee.com)](https://gitee.com/geek-xd/geek-uniapp-vue3-uview-plus-uchart)
2. [RuoYi-Vue3-Geek: 这是若依极客生态的Vue3版本 (gitee.com)](https://gitee.com/geek-xd/ruo-yi-vue3-geek)
与本项目同为一个作者开发,兼容性最好,学习成本最低。
# 引言
RuoYi-Vue与RuoYi-App是基于SpringBoot2+Vue2打造的企业级开发框架得到了广大开发者的喜爱和积极反馈。随着技术的迭代进步SpringBoot3与Vue3逐渐进入开发者的视野。为了满足开发者对于新技术的追求RuoYi官方文档提供了SpringBoot2至SpringBoot3的升级方法。与此同时社区也涌现出了RuoYi-Vue3、RuoYi-App-Vue3的版本展现了开发者社区对于技术升级的热情与努力。
然而在升级的过程中官方的方法为了兼顾Java1.8的特性与一些老旧的方法未完全拥抱SpringBoot3与Java17的全部特性。而社区的RuoYi-Vue3、RuoYi-App-Vue3版本由于出自不同的团队之手兼容性及整合性上存在些许不足。更为关键的是尽管这些版本支持TypeScript但缺乏与之相匹配的tsconfig.json配置文件这使得在主流编辑器如VSCode中TypeScript的语法提示环境并未达到最佳状态。
鉴于此RuoYi-Geek生态应运而生。它旨在为广大开发者提供一个既保留原版本核心特性又整合社区版优点的全新解决方案。在RuoYi-Geek中我们深入调研了企业开发中常用的RuoYi扩展并直接在框架中集成确保开发者能够快速上手高效开发。同时我们采用了最新的SpringBoot3+Vue3技术栈彻底移除了为了兼容Java1.8而保留的老旧方法。更为重要的是我们为TypeScript开发环境加入了常用的tsconfig.json配置使得开发者在VSCode等编辑器中能够获得更为舒适、便捷的语法提示体验。
RuoYi-Geek不仅仅是一个简单的升级版本更是对于RuoYi生态的一次全面优化与整合。我们相信通过RuoYi-Geek开发者将能够更为高效、愉悦地开发出优秀的企业级应用。
## 平台简介
若依是一套全部开源的快速开发平台,毫无保留给个人及企业免费使用。
* 前端采用Vue3、Element Plus。
* 后端采用Spring Boot3、Spring Security、Redis & Jwt。
* 权限认证使用Jwt支持多终端认证系统。
* 支持加载动态权限菜单,多方式轻松权限控制。
* 高效率开发,使用代码生成器可以一键生成前后端代码。
* 多数据源与分库分表默认集成
* 所有非基本模块可随意插拔,让开发更加简单高效
* 提供了多个工具模块助力开发在线接口模块、mybatis-jpa模块
* 提供了多个常见业务模块简化开发,如:第三方认证模块、支付模块
* 提供了多个常见的服务模块集成开发websocket模块、minio模块
* 特别鸣谢:[element](https://github.com/ElemeFE/element)[vue-element-admin](https://github.com/PanJiaChen/vue-element-admin)[eladmin-web](https://github.com/elunez/eladmin-web)。
* 阿里云折扣场:[点我进入](http://aly.ruoyi.vip),腾讯云秒杀场:[点我进入](http://txy.ruoyi.vip)&nbsp;&nbsp;
* 阿里云优惠券:[点我领取](https://www.aliyun.com/minisite/goods?userCode=brki8iof&share_source=copy_link),腾讯云优惠券:[点我领取](https://cloud.tencent.com/redirect.php?redirect=1025&cps_key=198c8df2ed259157187173bc7f4f32fd&from=console)&nbsp;&nbsp;
## 本项目与原项目的区别
### 核心
**模块化架构设计,支持各个模块的快速安拆,对第三方认证、第三方支付模块设计了基础的规范和基础模块。**
### 细节
1. 改用SpringBoot3+java17的更新的技术栈并改掉所有的弃用的方法全面拥抱java17的全新特性。
2. 升级了代码生成器配合本项目的vue3版本才可用使关联表生成更加简单
3. 改用最新版本的SpringSecurity安全框架以及采用最新的lambda 表达式的配置方式,更加通俗易懂!
4. 自动Api文档以springfox替代springdoc来适配knife4j框架的4.x版本更好的适配springboot3
5. 默认引入mybatis-plus增强mybatis并自创工具模块mybatis-jpa简化CRUD
6. 默认引入lombok简化代码基础模块并未使用mybatis-plus和lombok对这两个扩展有争议的小伙伴可以直接删除不会影响到框架本身滴以及knife4j直接删除也不会影响到springdoc主要还是为了方便咱们开发者呢
7. 提供了大量可随意插拔的模块助力快速开发同时ruoyi插件集成中的常用插件也以模块的形式直接集成在了项目中也是可以随意插拔的嗷~
## 模块介绍(简单开发必看)
* 最简单的开发就是删除所有的可移除模块,按需添加模块。
* 测试中的模块请自己使用的时候一定要测试一下。
* 对于小白,开发中的模块请直接删除。
```
com.ruoyi
├── common // 工具类
│ └── annotation // 自定义注解
│ └── config // 全局配置
│ └── constant // 通用常量
│ └── core // 核心控制
│ └── enums // 通用枚举
│ └── exception // 通用异常
│ └── filter // 过滤器处理
│ └── utils // 通用类处理
├── framework // 框架核心
│ └── aspectj // 注解实现
│ └── config // 系统配置
│ └── datasource // 数据权限
│ └── interceptor // 拦截器
│ └── manager // 异步处理
│ └── security // 权限控制
│ └── web // 前端控制
├── ruoyi-admin // 后台服务
├── ruoyi-pay // 支付场景(基本业务完成)
│ └── common // 支付框架基础模块(基础框架搭建完成)
│ └── sqb // 收钱吧支付模块(功能可用,业务需要开发)
│ └── wx // 微信支付模块(基础业务完成)
│ └── alipay // 支付宝支付模块(基础业务完成)
│ └── starter // 支付场景启动器
├── ruoyi-auth // 第三方认证场景(基础框架搭建完成)
│ └── common // 第三方认证基础模块(开发中)
│ └── justauth // 网站第三方认证模块测试中参照若依扩展改进因没有这么多场景的code请大家测试出问题后help解决一下发出来
│ └── wx // 微信小程序认证模块(测试中)
│ └── phone // 手机认证模块(基础业务完成)
│ └── email // 邮箱认证模块(基础业务完成)
│ └── starter // 第三方认证启动器
├── ruoyi-file // 文件服务系统(支持公开访问、临时凭证、分片上传等常见功能)
│ └── common // 基础模块和本地磁盘方式
│ └── minio // minio分布式文件服务
│ └── oss-alibaba // alibaba的oss云储存服务
│ └── starter // 第三方认证启动器
├── ruoyi-models // 业务场景
│ └── online // 在线开发模块(可移除)
│ └── quartz // 定时任务(可移除)
│ └── generator // 代码生成(可移除)
│ └── form // 自定义在线表单(可移除)
│ └── flowable // 流程管理可移除依赖于form模块
│ └── message // 消息模块可移除可以与phone、email模块协同
│ └── starter // 业务场景启动器
├── ruoyi-plugins // 插件
│ └── ehcache // ehcache缓存插件与redis模块同类两者二选一
│ └── mybatis-jpa // mybatis-jpa插件可移除简化CRUD以数据模型为基础开发
│ └── mybatis-plus // mybatis-plus插件可移除简化CRUD
│ └── mybatis-interceptor // mybatis-interceptor插件可移除简化数据鉴权和分页扩展性强
│ └── atomikos // atomikos分布式事务插件可移除
│ └── netty // netty插件可移除
│ └── rabbitmq // rabbitmq队列服务模块
│ └── redis // redis缓存服务模块与ehcache插件同类两者二选一
│ └── websocket // websocket插件可移除
│ └── starter // 插件整合模块
├── ruoyi-system // 系统代码
```
## 内置功能
1. 用户管理:用户是系统操作者,该功能主要完成系统用户配置。
2. 部门管理:配置系统组织机构(公司、部门、小组),树结构展现支持数据权限。
3. 岗位管理:配置系统用户所属担任职务。
4. 菜单管理:配置系统菜单,操作权限,按钮权限标识等。
5. 角色管理:角色菜单权限分配、设置角色按机构进行数据范围权限划分。
6. 字典管理:对系统中经常使用的一些较为固定的数据进行维护。
7. 参数管理:对系统动态配置常用参数。
8. 通知公告:系统通知公告信息发布维护。
9. 操作日志:系统正常操作日志记录和查询;系统异常信息日志记录和查询。
10. 登录日志:系统登录日志记录查询包含登录异常。
11. 在线用户:当前系统中活跃用户状态监控。
12. 定时任务:在线(添加、修改、删除)任务调度包含执行结果日志。
13. 代码生成前后端代码的生成java、html、xml、sql支持CRUD下载 。
14. 系统接口根据业务代码自动生成相关的api接口文档。
15. 服务监控监视当前系统CPU、内存、磁盘、堆栈等相关信息。
16. 缓存监控:对系统的缓存信息查询,命令统计等。
17. 在线构建器拖动表单元素生成相应的HTML代码。
18. 连接池监视监视当前系统数据库连接池状态可进行分析SQL找出系统性能瓶颈。
19. 支付场景
20. 第三方登录场景
21. 中间件场景
## 演示图
### 新加功能和增强功能演示
<table>
<tr>
<td><img src="./doc/image/online-mb-list.png"/></td>
<td><img src="./doc/image/online-mb-edit.png"/></td>
</tr>
<tr>
<td><img src="./doc/image/online-mb-code.png"/></td>
<td><img src="./doc/image/form-edit.png"/></td>
</tr>
<tr>
<td><img src="./doc/image/code-edit.png"/></td>
<td><img src="./doc/image/code-show.png"/></td>
</tr>
</table>
### 原有功能演示
<table>
<tr>
<td><img src="https://oscimg.oschina.net/oscnet/cd1f90be5f2684f4560c9519c0f2a232ee8.jpg"/></td>
<td><img src="https://oscimg.oschina.net/oscnet/1cbcf0e6f257c7d3a063c0e3f2ff989e4b3.jpg"/></td>
</tr>
<tr>
<td><img src="https://oscimg.oschina.net/oscnet/up-8074972883b5ba0622e13246738ebba237a.png"/></td>
<td><img src="https://oscimg.oschina.net/oscnet/up-9f88719cdfca9af2e58b352a20e23d43b12.png"/></td>
</tr>
<tr>
<td><img src="https://oscimg.oschina.net/oscnet/up-39bf2584ec3a529b0d5a3b70d15c9b37646.png"/></td>
<td><img src="https://oscimg.oschina.net/oscnet/up-936ec82d1f4872e1bc980927654b6007307.png"/></td>
</tr>
<tr>
<td><img src="https://oscimg.oschina.net/oscnet/up-b2d62ceb95d2dd9b3fbe157bb70d26001e9.png"/></td>
<td><img src="https://oscimg.oschina.net/oscnet/up-d67451d308b7a79ad6819723396f7c3d77a.png"/></td>
</tr>
<tr>
<td><img src="https://oscimg.oschina.net/oscnet/5e8c387724954459291aafd5eb52b456f53.jpg"/></td>
<td><img src="https://oscimg.oschina.net/oscnet/644e78da53c2e92a95dfda4f76e6d117c4b.jpg"/></td>
</tr>
<tr>
<td><img src="https://oscimg.oschina.net/oscnet/up-8370a0d02977eebf6dbf854c8450293c937.png"/></td>
<td><img src="https://oscimg.oschina.net/oscnet/up-49003ed83f60f633e7153609a53a2b644f7.png"/></td>
</tr>
<tr>
<td><img src="https://oscimg.oschina.net/oscnet/up-d4fe726319ece268d4746602c39cffc0621.png"/></td>
<td><img src="https://oscimg.oschina.net/oscnet/up-c195234bbcd30be6927f037a6755e6ab69c.png"/></td>
</tr>
<tr>
<td><img src="https://oscimg.oschina.net/oscnet/b6115bc8c31de52951982e509930b20684a.jpg"/></td>
<td><img src="https://oscimg.oschina.net/oscnet/up-5e4daac0bb59612c5038448acbcef235e3a.png"/></td>
</tr>
</table>
# 联系我们:
QQ交流群744785891

12
bin/clean.bat Normal file
View File

@ -0,0 +1,12 @@
@echo off
echo.
echo [信息] 清理工程target生成路径。
echo.
%~d0
cd %~dp0
cd ..
call mvn clean
pause

12
bin/package.bat Normal file
View File

@ -0,0 +1,12 @@
@echo off
echo.
echo [信息] 打包Web工程生成war/jar包文件。
echo.
%~d0
cd %~dp0
cd ..
call mvn clean package -Dmaven.test.skip=true
pause

14
bin/run.bat Normal file
View File

@ -0,0 +1,14 @@
@echo off
echo.
echo [信息] 使用Jar命令运行Web工程。
echo.
cd %~dp0
cd ../ruoyi-admin/target
set JAVA_OPTS=-Xms256m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m
java -jar %JAVA_OPTS% ruoyi-admin.jar
cd bin
pause

BIN
doc/image/code-edit.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 KiB

BIN
doc/image/code-show.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

BIN
doc/image/form-edit.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 KiB

BIN
doc/image/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 181 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

311
doc/代码生成.drawio Normal file
View File

@ -0,0 +1,311 @@
<mxfile host="65bd71144e">
<diagram id="xy79Wy17eWdTJqY27ViR" name="第 1 页">
<mxGraphModel dx="1674" dy="779" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
<root>
<mxCell id="0"/>
<mxCell id="1" parent="0"/>
<mxCell id="7" value="" style="edgeStyle=none;html=1;" parent="1" source="2" target="6" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="2" value="代码生成" style="html=1;" parent="1" vertex="1">
<mxGeometry x="205" y="20" width="110" height="50" as="geometry"/>
</mxCell>
<mxCell id="9" value="" style="edgeStyle=none;html=1;" parent="1" source="3" target="8" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="3" value="预览代码" style="html=1;" parent="1" vertex="1">
<mxGeometry x="525" y="20" width="110" height="50" as="geometry"/>
</mxCell>
<mxCell id="13" value="" style="edgeStyle=none;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="1" source="6" target="12" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="6" value="GenTableServiceImpl.previewCode" style="html=1;" parent="1" vertex="1">
<mxGeometry x="150" y="120" width="220" height="50" as="geometry"/>
</mxCell>
<mxCell id="11" value="" style="edgeStyle=none;html=1;" parent="1" source="8" target="10" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="8" value="GenTableServiceImpl.downloadCode" style="html=1;" parent="1" vertex="1">
<mxGeometry x="460" y="100" width="240" height="50" as="geometry"/>
</mxCell>
<mxCell id="14" style="edgeStyle=none;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="1" source="10" target="12" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="10" value="GenTableServiceImpl.generatorCode" style="html=1;" parent="1" vertex="1">
<mxGeometry x="460" y="190" width="240" height="50" as="geometry"/>
</mxCell>
<mxCell id="16" style="edgeStyle=none;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="1" source="12" target="17" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="360" y="580" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="12" value="GenTableMapper.selectGenTableById&amp;nbsp; &amp;nbsp;查询表信息" style="html=1;" parent="1" vertex="1">
<mxGeometry x="230" y="330" width="310" height="50" as="geometry"/>
</mxCell>
<mxCell id="19" value="" style="edgeStyle=none;html=1;" parent="1" source="17" target="18" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="17" value="GenTableServiceImpl.setSubTable&amp;nbsp; 设置主子表信息" style="html=1;" parent="1" vertex="1">
<mxGeometry x="237.5" y="420" width="297.5" height="50" as="geometry"/>
</mxCell>
<mxCell id="21" value="" style="edgeStyle=none;html=1;" parent="1" source="18" target="20" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="18" value="GenTableServiceImpl.setPkColumn&amp;nbsp; 设置主键列信息" style="html=1;" parent="1" vertex="1">
<mxGeometry x="232.5" y="515" width="307.5" height="50" as="geometry"/>
</mxCell>
<mxCell id="23" value="" style="edgeStyle=none;html=1;" parent="1" source="20" target="22" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="20" value="VelocityInitializer.initVelocity&amp;nbsp; &amp;nbsp;初始化vm方法" style="html=1;" parent="1" vertex="1">
<mxGeometry x="242.5" y="595" width="287.5" height="50" as="geometry"/>
</mxCell>
<mxCell id="25" value="" style="edgeStyle=none;html=1;" parent="1" source="22" target="24" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="22" value="VelocityUtils.prepareContext&amp;nbsp; &lt;font color=&quot;#ff0000&quot;&gt;设置模板变量信息&lt;/font&gt;" style="html=1;" parent="1" vertex="1">
<mxGeometry x="242.5" y="685" width="287.5" height="50" as="geometry"/>
</mxCell>
<mxCell id="27" value="" style="edgeStyle=none;html=1;" parent="1" source="24" target="26" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="24" value="VelocityUtils.getTemplateList&amp;nbsp; &lt;font color=&quot;#ff0000&quot;&gt;获取模板列表&lt;/font&gt;" style="html=1;" parent="1" vertex="1">
<mxGeometry x="242.5" y="770" width="287.5" height="50" as="geometry"/>
</mxCell>
<mxCell id="30" value="" style="edgeStyle=none;html=1;" parent="1" source="26" target="29" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="26" value="Velocity.getTemplate 渲染模板" style="html=1;" parent="1" vertex="1">
<mxGeometry x="242.5" y="860" width="287.5" height="50" as="geometry"/>
</mxCell>
<mxCell id="29" value="预览代码 -&amp;gt; 结果储存到dataMap中&lt;br&gt;自定义路径 -&amp;gt; 通过FileUtils写入到指定位置&lt;br&gt;下载代码 -&amp;gt; 将生抽的信息添加到zip然后下载" style="html=1;" parent="1" vertex="1">
<mxGeometry x="242.5" y="950" width="287.5" height="50" as="geometry"/>
</mxCell>
<mxCell id="31" value="&lt;div style=&quot;color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-weight: normal; font-size: 14px; line-height: 19px; white-space-collapse: preserve;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;GenTable&lt;/span&gt;&lt;/div&gt;" style="shape=table;startSize=30;container=1;collapsible=1;childLayout=tableLayout;fixedRows=1;rowLines=0;fontStyle=1;align=center;resizeLast=1;html=1;" vertex="1" parent="1">
<mxGeometry x="910" y="20" width="180" height="540" as="geometry"/>
</mxCell>
<mxCell id="32" value="" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;top=0;left=0;right=0;bottom=1;" vertex="1" parent="31">
<mxGeometry y="30" width="180" height="30" as="geometry"/>
</mxCell>
<mxCell id="33" value="PK" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;fontStyle=1;overflow=hidden;whiteSpace=wrap;html=1;" vertex="1" parent="32">
<mxGeometry width="30" height="30" as="geometry">
<mxRectangle width="30" height="30" as="alternateBounds"/>
</mxGeometry>
</mxCell>
<mxCell id="34" value="&lt;div style=&quot;color: #000000;background-color: #ffffff;font-family: Consolas, &#39;Courier New&#39;, monospace;font-weight: normal;font-size: 14px;line-height: 19px;white-space: pre;&quot;&gt;&lt;span style=&quot;color: #880088;font-weight: bold;&quot;&gt;tableId&lt;/span&gt;&lt;/div&gt;" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;fontStyle=5;overflow=hidden;whiteSpace=wrap;html=1;" vertex="1" parent="32">
<mxGeometry x="30" width="150" height="30" as="geometry">
<mxRectangle width="150" height="30" as="alternateBounds"/>
</mxGeometry>
</mxCell>
<mxCell id="35" value="" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;top=0;left=0;right=0;bottom=0;" vertex="1" parent="31">
<mxGeometry y="60" width="180" height="30" as="geometry"/>
</mxCell>
<mxCell id="36" value="" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;editable=1;overflow=hidden;whiteSpace=wrap;html=1;" vertex="1" parent="35">
<mxGeometry width="30" height="30" as="geometry">
<mxRectangle width="30" height="30" as="alternateBounds"/>
</mxGeometry>
</mxCell>
<mxCell id="37" value="&lt;div style=&quot;color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 14px; line-height: 19px; white-space: pre;&quot;&gt;&lt;span style=&quot;color: #880088;font-weight: bold;&quot;&gt;tableName&lt;/span&gt;&lt;/div&gt;" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;overflow=hidden;whiteSpace=wrap;html=1;" vertex="1" parent="35">
<mxGeometry x="30" width="150" height="30" as="geometry">
<mxRectangle width="150" height="30" as="alternateBounds"/>
</mxGeometry>
</mxCell>
<mxCell id="38" value="" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;top=0;left=0;right=0;bottom=0;" vertex="1" parent="31">
<mxGeometry y="90" width="180" height="30" as="geometry"/>
</mxCell>
<mxCell id="39" value="" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;editable=1;overflow=hidden;whiteSpace=wrap;html=1;" vertex="1" parent="38">
<mxGeometry width="30" height="30" as="geometry">
<mxRectangle width="30" height="30" as="alternateBounds"/>
</mxGeometry>
</mxCell>
<mxCell id="40" value="&lt;div style=&quot;color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 14px; line-height: 19px; white-space: pre;&quot;&gt;&lt;span style=&quot;color: #880088;font-weight: bold;&quot;&gt;tableAlias&lt;/span&gt;&lt;/div&gt;" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;overflow=hidden;whiteSpace=wrap;html=1;" vertex="1" parent="38">
<mxGeometry x="30" width="150" height="30" as="geometry">
<mxRectangle width="150" height="30" as="alternateBounds"/>
</mxGeometry>
</mxCell>
<mxCell id="41" value="" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;top=0;left=0;right=0;bottom=0;" vertex="1" parent="31">
<mxGeometry y="120" width="180" height="30" as="geometry"/>
</mxCell>
<mxCell id="42" value="" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;editable=1;overflow=hidden;whiteSpace=wrap;html=1;" vertex="1" parent="41">
<mxGeometry width="30" height="30" as="geometry">
<mxRectangle width="30" height="30" as="alternateBounds"/>
</mxGeometry>
</mxCell>
<mxCell id="43" value="&lt;div style=&quot;color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 14px; line-height: 19px; white-space: pre;&quot;&gt;&lt;span style=&quot;color: #880088;font-weight: bold;&quot;&gt;tableComment&lt;/span&gt;&lt;/div&gt;" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;overflow=hidden;whiteSpace=wrap;html=1;" vertex="1" parent="41">
<mxGeometry x="30" width="150" height="30" as="geometry">
<mxRectangle width="150" height="30" as="alternateBounds"/>
</mxGeometry>
</mxCell>
<mxCell id="44" value="" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;top=0;left=0;right=0;bottom=0;" vertex="1" parent="31">
<mxGeometry y="150" width="180" height="30" as="geometry"/>
</mxCell>
<mxCell id="45" value="" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;editable=1;overflow=hidden;" vertex="1" parent="44">
<mxGeometry width="30" height="30" as="geometry">
<mxRectangle width="30" height="30" as="alternateBounds"/>
</mxGeometry>
</mxCell>
<mxCell id="46" value="subTableName" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;overflow=hidden;" vertex="1" parent="44">
<mxGeometry x="30" width="150" height="30" as="geometry">
<mxRectangle width="150" height="30" as="alternateBounds"/>
</mxGeometry>
</mxCell>
<mxCell id="47" value="" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;top=0;left=0;right=0;bottom=0;" vertex="1" parent="31">
<mxGeometry y="180" width="180" height="30" as="geometry"/>
</mxCell>
<mxCell id="48" value="" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;editable=1;overflow=hidden;" vertex="1" parent="47">
<mxGeometry width="30" height="30" as="geometry">
<mxRectangle width="30" height="30" as="alternateBounds"/>
</mxGeometry>
</mxCell>
<mxCell id="49" value="subTableFkName" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;overflow=hidden;" vertex="1" parent="47">
<mxGeometry x="30" width="150" height="30" as="geometry">
<mxRectangle width="150" height="30" as="alternateBounds"/>
</mxGeometry>
</mxCell>
<mxCell id="50" value="" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;top=0;left=0;right=0;bottom=0;" vertex="1" parent="31">
<mxGeometry y="210" width="180" height="30" as="geometry"/>
</mxCell>
<mxCell id="51" value="" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;editable=1;overflow=hidden;" vertex="1" parent="50">
<mxGeometry width="30" height="30" as="geometry">
<mxRectangle width="30" height="30" as="alternateBounds"/>
</mxGeometry>
</mxCell>
<mxCell id="52" value="className" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;overflow=hidden;" vertex="1" parent="50">
<mxGeometry x="30" width="150" height="30" as="geometry">
<mxRectangle width="150" height="30" as="alternateBounds"/>
</mxGeometry>
</mxCell>
<mxCell id="57" value="" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;top=0;left=0;right=0;bottom=0;" vertex="1" parent="31">
<mxGeometry y="240" width="180" height="30" as="geometry"/>
</mxCell>
<mxCell id="58" value="" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;editable=1;overflow=hidden;" vertex="1" parent="57">
<mxGeometry width="30" height="30" as="geometry">
<mxRectangle width="30" height="30" as="alternateBounds"/>
</mxGeometry>
</mxCell>
<mxCell id="59" value="tplCategory" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;overflow=hidden;" vertex="1" parent="57">
<mxGeometry x="30" width="150" height="30" as="geometry">
<mxRectangle width="150" height="30" as="alternateBounds"/>
</mxGeometry>
</mxCell>
<mxCell id="60" value="" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;top=0;left=0;right=0;bottom=0;" vertex="1" parent="31">
<mxGeometry y="270" width="180" height="30" as="geometry"/>
</mxCell>
<mxCell id="61" value="" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;editable=1;overflow=hidden;" vertex="1" parent="60">
<mxGeometry width="30" height="30" as="geometry">
<mxRectangle width="30" height="30" as="alternateBounds"/>
</mxGeometry>
</mxCell>
<mxCell id="62" value="tplWebType" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;overflow=hidden;" vertex="1" parent="60">
<mxGeometry x="30" width="150" height="30" as="geometry">
<mxRectangle width="150" height="30" as="alternateBounds"/>
</mxGeometry>
</mxCell>
<mxCell id="63" value="" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;top=0;left=0;right=0;bottom=0;" vertex="1" parent="31">
<mxGeometry y="300" width="180" height="30" as="geometry"/>
</mxCell>
<mxCell id="64" value="" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;editable=1;overflow=hidden;" vertex="1" parent="63">
<mxGeometry width="30" height="30" as="geometry">
<mxRectangle width="30" height="30" as="alternateBounds"/>
</mxGeometry>
</mxCell>
<mxCell id="65" value="moduleName" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;overflow=hidden;" vertex="1" parent="63">
<mxGeometry x="30" width="150" height="30" as="geometry">
<mxRectangle width="150" height="30" as="alternateBounds"/>
</mxGeometry>
</mxCell>
<mxCell id="66" value="" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;top=0;left=0;right=0;bottom=0;" vertex="1" parent="31">
<mxGeometry y="330" width="180" height="30" as="geometry"/>
</mxCell>
<mxCell id="67" value="" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;editable=1;overflow=hidden;" vertex="1" parent="66">
<mxGeometry width="30" height="30" as="geometry">
<mxRectangle width="30" height="30" as="alternateBounds"/>
</mxGeometry>
</mxCell>
<mxCell id="68" value="businessName" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;overflow=hidden;" vertex="1" parent="66">
<mxGeometry x="30" width="150" height="30" as="geometry">
<mxRectangle width="150" height="30" as="alternateBounds"/>
</mxGeometry>
</mxCell>
<mxCell id="69" value="" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;top=0;left=0;right=0;bottom=0;" vertex="1" parent="31">
<mxGeometry y="360" width="180" height="30" as="geometry"/>
</mxCell>
<mxCell id="70" value="" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;editable=1;overflow=hidden;" vertex="1" parent="69">
<mxGeometry width="30" height="30" as="geometry">
<mxRectangle width="30" height="30" as="alternateBounds"/>
</mxGeometry>
</mxCell>
<mxCell id="71" value="functionName" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;overflow=hidden;" vertex="1" parent="69">
<mxGeometry x="30" width="150" height="30" as="geometry">
<mxRectangle width="150" height="30" as="alternateBounds"/>
</mxGeometry>
</mxCell>
<mxCell id="72" value="" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;top=0;left=0;right=0;bottom=0;" vertex="1" parent="31">
<mxGeometry y="390" width="180" height="30" as="geometry"/>
</mxCell>
<mxCell id="73" value="" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;editable=1;overflow=hidden;" vertex="1" parent="72">
<mxGeometry width="30" height="30" as="geometry">
<mxRectangle width="30" height="30" as="alternateBounds"/>
</mxGeometry>
</mxCell>
<mxCell id="74" value="functionAuthor" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;overflow=hidden;" vertex="1" parent="72">
<mxGeometry x="30" width="150" height="30" as="geometry">
<mxRectangle width="150" height="30" as="alternateBounds"/>
</mxGeometry>
</mxCell>
<mxCell id="75" value="" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;top=0;left=0;right=0;bottom=0;" vertex="1" parent="31">
<mxGeometry y="420" width="180" height="30" as="geometry"/>
</mxCell>
<mxCell id="76" value="" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;editable=1;overflow=hidden;" vertex="1" parent="75">
<mxGeometry width="30" height="30" as="geometry">
<mxRectangle width="30" height="30" as="alternateBounds"/>
</mxGeometry>
</mxCell>
<mxCell id="77" value="genType" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;overflow=hidden;" vertex="1" parent="75">
<mxGeometry x="30" width="150" height="30" as="geometry">
<mxRectangle width="150" height="30" as="alternateBounds"/>
</mxGeometry>
</mxCell>
<mxCell id="78" value="" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;top=0;left=0;right=0;bottom=0;" vertex="1" parent="31">
<mxGeometry y="450" width="180" height="30" as="geometry"/>
</mxCell>
<mxCell id="79" value="" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;editable=1;overflow=hidden;" vertex="1" parent="78">
<mxGeometry width="30" height="30" as="geometry">
<mxRectangle width="30" height="30" as="alternateBounds"/>
</mxGeometry>
</mxCell>
<mxCell id="80" value="genPath" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;overflow=hidden;" vertex="1" parent="78">
<mxGeometry x="30" width="150" height="30" as="geometry">
<mxRectangle width="150" height="30" as="alternateBounds"/>
</mxGeometry>
</mxCell>
<mxCell id="84" value="" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;top=0;left=0;right=0;bottom=0;" vertex="1" parent="31">
<mxGeometry y="480" width="180" height="30" as="geometry"/>
</mxCell>
<mxCell id="85" value="" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;editable=1;overflow=hidden;" vertex="1" parent="84">
<mxGeometry width="30" height="30" as="geometry">
<mxRectangle width="30" height="30" as="alternateBounds"/>
</mxGeometry>
</mxCell>
<mxCell id="86" value="pkColumn" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;overflow=hidden;" vertex="1" parent="84">
<mxGeometry x="30" width="150" height="30" as="geometry">
<mxRectangle width="150" height="30" as="alternateBounds"/>
</mxGeometry>
</mxCell>
<mxCell id="87" value="" style="shape=tableRow;horizontal=0;startSize=0;swimlaneHead=0;swimlaneBody=0;fillColor=none;collapsible=0;dropTarget=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;top=0;left=0;right=0;bottom=0;" vertex="1" parent="31">
<mxGeometry y="510" width="180" height="30" as="geometry"/>
</mxCell>
<mxCell id="88" value="" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;editable=1;overflow=hidden;" vertex="1" parent="87">
<mxGeometry width="30" height="30" as="geometry">
<mxRectangle width="30" height="30" as="alternateBounds"/>
</mxGeometry>
</mxCell>
<mxCell id="89" value="subTable" style="shape=partialRectangle;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;overflow=hidden;" vertex="1" parent="87">
<mxGeometry x="30" width="150" height="30" as="geometry">
<mxRectangle width="150" height="30" as="alternateBounds"/>
</mxGeometry>
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

View File

@ -0,0 +1,149 @@
<mxfile host="65bd71144e">
<diagram id="27K8aLRXgDd5Ry5UGMpc" name="第 1 页">
<mxGraphModel dx="2298" dy="760" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
<root>
<mxCell id="0"/>
<mxCell id="1" parent="0"/>
<mxCell id="7" value="" style="edgeStyle=none;html=1;" parent="1" source="2" target="4" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="2" value="数据源1" style="html=1;" parent="1" vertex="1">
<mxGeometry x="510" y="340" width="110" height="50" as="geometry"/>
</mxCell>
<mxCell id="8" value="" style="edgeStyle=none;html=1;" parent="1" source="3" target="5" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="3" value="数据源2" style="html=1;" parent="1" vertex="1">
<mxGeometry x="-90" y="340" width="110" height="50" as="geometry"/>
</mxCell>
<mxCell id="9" style="edgeStyle=none;html=1;" parent="1" source="4" target="28" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="571.4814814814818" y="470" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="44" style="edgeStyle=none;html=1;" parent="1" source="4" target="43" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="4" value="&lt;div style=&quot;background-color: rgb(255, 255, 255); font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 14px; line-height: 19px;&quot;&gt;DataSource1&lt;/div&gt;" style="html=1;" parent="1" vertex="1">
<mxGeometry x="325" y="340" width="110" height="50" as="geometry"/>
</mxCell>
<mxCell id="10" style="edgeStyle=none;html=1;" parent="1" source="5" target="28" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="556.25" y="470" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="45" style="edgeStyle=none;html=1;" parent="1" source="5" target="43" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="5" value="&lt;div style=&quot;background-color: rgb(255, 255, 255); font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 14px; line-height: 19px;&quot;&gt;DataSource2&lt;/div&gt;" style="html=1;" parent="1" vertex="1">
<mxGeometry x="100" y="340" width="110" height="50" as="geometry"/>
</mxCell>
<mxCell id="15" value="" style="edgeStyle=none;html=1;" parent="1" source="11" target="28" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="636.25" y="470" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="11" value="&lt;div style=&quot;background-color: rgb(255, 255, 255); font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 14px; line-height: 19px;&quot;&gt;&lt;div style=&quot;line-height: 19px;&quot;&gt;DynamicDataSourceContextHolder&lt;/div&gt;&lt;/div&gt;" style="html=1;" parent="1" vertex="1">
<mxGeometry x="460" y="460" width="270" height="50" as="geometry"/>
</mxCell>
<mxCell id="13" value="&lt;div style=&quot;background-color: rgb(255, 255, 255); font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 14px; line-height: 19px;&quot;&gt;&lt;h1 style=&quot;box-sizing: border-box; outline: 0px; margin: 0px; padding: 0px; font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Microsoft YaHei&amp;quot;, SimHei, Arial, SimSun; font-size: 28px; overflow-wrap: break-word; color: rgb(34, 34, 38); word-break: break-all; font-variant-ligatures: common-ligatures; text-align: start;&quot; id=&quot;articleContentId&quot; class=&quot;title-article&quot;&gt;Spring AbstractRoutingDataSource&lt;br&gt;&lt;br&gt;&lt;/h1&gt;&lt;div&gt;* determineCurrentLookupKey&lt;/div&gt;&lt;/div&gt;" style="html=1;" parent="1" vertex="1">
<mxGeometry x="-380" y="455" width="520" height="70" as="geometry"/>
</mxCell>
<mxCell id="19" value="" style="edgeStyle=none;html=1;" parent="1" source="16" target="18" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="16" value="方法1" style="html=1;" parent="1" vertex="1">
<mxGeometry x="880" y="340" width="110" height="50" as="geometry"/>
</mxCell>
<mxCell id="20" value="" style="edgeStyle=none;html=1;" parent="1" source="17" target="18" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="17" value="方法2" style="html=1;" parent="1" vertex="1">
<mxGeometry x="880" y="580" width="110" height="50" as="geometry"/>
</mxCell>
<mxCell id="21" value="" style="edgeStyle=none;html=1;" parent="1" source="18" target="11" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="18" value="&lt;div style=&quot;background-color: rgb(255, 255, 255); font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 14px; line-height: 19px;&quot;&gt;DataSourceAspect&lt;/div&gt;" style="html=1;" parent="1" vertex="1">
<mxGeometry x="790" y="460" width="290" height="50" as="geometry"/>
</mxCell>
<mxCell id="22" value="" style="edgeStyle=none;html=1;" parent="1" source="23" target="26" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="23" value="数据源1" style="html=1;" parent="1" vertex="1">
<mxGeometry x="-90" y="580" width="110" height="50" as="geometry"/>
</mxCell>
<mxCell id="24" value="" style="edgeStyle=none;html=1;" parent="1" source="25" target="27" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="25" value="数据源2" style="html=1;" parent="1" vertex="1">
<mxGeometry x="510" y="580" width="110" height="50" as="geometry"/>
</mxCell>
<mxCell id="29" style="edgeStyle=none;html=1;" parent="1" source="26" target="28" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="46" style="edgeStyle=none;html=1;" parent="1" source="26" target="47" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="240" y="790" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="26" value="&lt;div style=&quot;background-color: rgb(255, 255, 255); font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 14px; line-height: 19px;&quot;&gt;&lt;div style=&quot;line-height: 19px;&quot;&gt;AtomikosDataSourceBean1&lt;/div&gt;&lt;/div&gt;" style="html=1;" parent="1" vertex="1">
<mxGeometry x="60" y="580" width="190" height="50" as="geometry"/>
</mxCell>
<mxCell id="30" style="edgeStyle=none;html=1;" parent="1" source="27" target="28" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="49" style="edgeStyle=none;html=1;" parent="1" source="27" target="48" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="27" value="&lt;div style=&quot;background-color: rgb(255, 255, 255); font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 14px; line-height: 19px;&quot;&gt;&lt;div style=&quot;line-height: 19px;&quot;&gt;AtomikosDataSourceBean2&lt;/div&gt;&lt;/div&gt;" style="html=1;" parent="1" vertex="1">
<mxGeometry x="285" y="580" width="190" height="50" as="geometry"/>
</mxCell>
<mxCell id="59" style="edgeStyle=none;html=1;" parent="1" source="28" target="13" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="28" value="&lt;div style=&quot;background-color: rgb(255, 255, 255); font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 14px; line-height: 19px;&quot;&gt;DynamicDataSource&lt;/div&gt;" style="whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="170" y="455" width="230" height="60" as="geometry"/>
</mxCell>
<mxCell id="54" value="" style="edgeStyle=none;html=1;" parent="1" source="43" target="53" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="43" value="&lt;div style=&quot;background-color: rgb(255, 255, 255); font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 14px; line-height: 19px;&quot;&gt;sqlSessionFactory&lt;/div&gt;" style="whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="160" y="210" width="210" height="60" as="geometry"/>
</mxCell>
<mxCell id="51" style="edgeStyle=none;html=1;" parent="1" source="47" target="50" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="47" value="&lt;div style=&quot;background-color: rgb(255, 255, 255); font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 14px; line-height: 19px;&quot;&gt;SqlSessionFactory1&lt;/div&gt;" style="html=1;" parent="1" vertex="1">
<mxGeometry x="55" y="670" width="200" height="50" as="geometry"/>
</mxCell>
<mxCell id="52" style="edgeStyle=none;html=1;" parent="1" source="48" target="50" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="48" value="&lt;div style=&quot;background-color: rgb(255, 255, 255); font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 14px; line-height: 19px;&quot;&gt;SqlSessionFactory2&lt;/div&gt;" style="whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="280" y="670" width="200" height="50" as="geometry"/>
</mxCell>
<mxCell id="56" style="edgeStyle=none;html=1;" parent="1" source="50" target="57" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="-593.9655172413791" y="670" as="targetPoint"/>
<Array as="points">
<mxPoint x="-470" y="815"/>
</Array>
</mxGeometry>
</mxCell>
<mxCell id="50" value="&lt;div style=&quot;background-color: rgb(255, 255, 255); font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 14px; line-height: 19px;&quot;&gt;DynamicSqlSessionTemplate&lt;/div&gt;" style="whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="160" y="790" width="210" height="50" as="geometry"/>
</mxCell>
<mxCell id="58" style="edgeStyle=none;html=1;" parent="1" source="53" target="57" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="53" value="&lt;div style=&quot;background-color: rgb(255, 255, 255); font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 14px; line-height: 19px;&quot;&gt;&lt;div style=&quot;line-height: 19px;&quot;&gt;SqlSessionTemplate&lt;/div&gt;&lt;/div&gt;" style="whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="-573" y="210" width="210" height="60" as="geometry"/>
</mxCell>
<mxCell id="57" value="mybatis" style="html=1;" parent="1" vertex="1">
<mxGeometry x="-523" y="465" width="110" height="50" as="geometry"/>
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

76
doc/微信登录.drawio Normal file
View File

@ -0,0 +1,76 @@
<mxfile host="65bd71144e">
<diagram id="X-9zxwZzwVme25TE_fPk" name="第 1 页">
<mxGraphModel dx="1002" dy="1732" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
<root>
<mxCell id="0"/>
<mxCell id="1" parent="0"/>
<mxCell id="2" value="微信小程序" style="shape=umlLifeline;perimeter=lifelinePerimeter;whiteSpace=wrap;html=1;container=1;collapsible=0;recursiveResize=0;outlineConnect=0;" vertex="1" parent="1">
<mxGeometry x="100" y="130" width="100" height="390" as="geometry"/>
</mxCell>
<mxCell id="16" value="" style="html=1;points=[];perimeter=orthogonalPerimeter;" vertex="1" parent="2">
<mxGeometry x="45" y="140" width="10" height="40" as="geometry"/>
</mxCell>
<mxCell id="17" value="wx.login" style="edgeStyle=orthogonalEdgeStyle;html=1;align=left;spacingLeft=2;endArrow=block;rounded=0;entryX=1;entryY=0;" edge="1" target="16" parent="2">
<mxGeometry relative="1" as="geometry">
<mxPoint x="50" y="120" as="sourcePoint"/>
<Array as="points">
<mxPoint x="80" y="120"/>
</Array>
</mxGeometry>
</mxCell>
<mxCell id="20" value="" style="html=1;points=[];perimeter=orthogonalPerimeter;" vertex="1" parent="2">
<mxGeometry x="45" y="260" width="10" height="40" as="geometry"/>
</mxCell>
<mxCell id="3" value="开发者服务器" style="shape=umlLifeline;perimeter=lifelinePerimeter;whiteSpace=wrap;html=1;container=1;collapsible=0;recursiveResize=0;outlineConnect=0;" vertex="1" parent="1">
<mxGeometry x="330" y="130" width="100" height="390" as="geometry"/>
</mxCell>
<mxCell id="9" value="" style="html=1;points=[];perimeter=orthogonalPerimeter;" vertex="1" parent="3">
<mxGeometry x="45" y="170" width="10" height="110" as="geometry"/>
</mxCell>
<mxCell id="4" value="微信接口服务" style="shape=umlLifeline;perimeter=lifelinePerimeter;whiteSpace=wrap;html=1;container=1;collapsible=0;recursiveResize=0;outlineConnect=0;" vertex="1" parent="1">
<mxGeometry x="590" y="130" width="100" height="400" as="geometry"/>
</mxCell>
<mxCell id="11" value="" style="html=1;points=[];perimeter=orthogonalPerimeter;" vertex="1" parent="4">
<mxGeometry x="45" y="175" width="10" height="60" as="geometry"/>
</mxCell>
<mxCell id="8" value="" style="html=1;verticalAlign=bottom;endArrow=block;exitX=0.9;exitY=0.89;exitDx=0;exitDy=0;exitPerimeter=0;" edge="1" parent="1" source="16" target="9">
<mxGeometry width="80" relative="1" as="geometry">
<mxPoint x="155" y="305" as="sourcePoint"/>
<mxPoint x="290" y="340" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="12" value="携带code" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="8">
<mxGeometry x="-0.1218" y="-1" relative="1" as="geometry">
<mxPoint as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="10" value="携带appid、appsecret、code" style="html=1;verticalAlign=bottom;endArrow=block;entryX=-0.02;entryY=0.01;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="1" source="9" target="11">
<mxGeometry width="80" relative="1" as="geometry">
<mxPoint x="460" y="330" as="sourcePoint"/>
<mxPoint x="550" y="330" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="18" value="返回session_key、openid" style="html=1;verticalAlign=bottom;endArrow=open;dashed=1;endSize=8;entryX=1.02;entryY=0.567;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="1" source="11" target="9">
<mxGeometry relative="1" as="geometry">
<mxPoint x="574" y="360" as="sourcePoint"/>
<mxPoint x="414" y="360" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="19" value="返回token" style="html=1;verticalAlign=bottom;endArrow=open;dashed=1;endSize=8;entryX=0.98;entryY=0.235;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="1" source="9" target="20">
<mxGeometry relative="1" as="geometry">
<mxPoint x="290" y="400" as="sourcePoint"/>
<mxPoint x="210" y="400" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="21" value="code临时登录凭证只能使用一次&lt;br&gt;appid开发者id&lt;br&gt;appsecret开发者密码&lt;br&gt;openid用户唯一标识&lt;br&gt;session_key会话密钥对用户数据进行加密的签名" style="html=1;dropTarget=0;align=left;" vertex="1" parent="1">
<mxGeometry x="90" y="-80" width="300" height="160" as="geometry"/>
</mxCell>
<mxCell id="22" value="" style="shape=module;jettyWidth=8;jettyHeight=4;" vertex="1" parent="21">
<mxGeometry x="1" width="20" height="20" relative="1" as="geometry">
<mxPoint x="-27" y="7" as="offset"/>
</mxGeometry>
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

View File

@ -0,0 +1,43 @@
<mxfile host="65bd71144e">
<diagram id="eUwQ9UTd_wFMP36Shy1F" name="第 1 页">
<mxGraphModel dx="1173" dy="766" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
<root>
<mxCell id="0"/>
<mxCell id="1" parent="0"/>
<mxCell id="4" value="" style="edgeStyle=none;html=1;" edge="1" parent="1" source="2" target="3">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="2" value="获取支付链接" style="html=1;" vertex="1" parent="1">
<mxGeometry x="400" y="140" width="110" height="50" as="geometry"/>
</mxCell>
<mxCell id="6" value="" style="edgeStyle=none;html=1;" edge="1" parent="1" source="3" target="5">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="3" value="用户支付" style="html=1;" vertex="1" parent="1">
<mxGeometry x="400" y="220" width="110" height="50" as="geometry"/>
</mxCell>
<mxCell id="8" value="" style="edgeStyle=none;html=1;" edge="1" parent="1" source="5" target="7">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="5" value="进入支付回调" style="html=1;" vertex="1" parent="1">
<mxGeometry x="400" y="310" width="110" height="50" as="geometry"/>
</mxCell>
<mxCell id="17" value="" style="edgeStyle=none;html=1;" edge="1" parent="1" source="7" target="13">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="7" value="验签" style="html=1;" vertex="1" parent="1">
<mxGeometry x="400" y="400" width="110" height="50" as="geometry"/>
</mxCell>
<mxCell id="9" value="用户定义的回调业务" style="html=1;" vertex="1" parent="1">
<mxGeometry x="400" y="580" width="110" height="50" as="geometry"/>
</mxCell>
<mxCell id="18" value="" style="edgeStyle=none;html=1;" edge="1" parent="1" source="13" target="9">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="13" value="更改订单状态" style="html=1;" vertex="1" parent="1">
<mxGeometry x="400" y="490" width="110" height="50" as="geometry"/>
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

View File

@ -0,0 +1,222 @@
<mxfile host="65bd71144e">
<diagram id="OUCQybPowJdnNh2YIqGc" name="第 1 页">
<mxGraphModel dx="823" dy="766" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
<root>
<mxCell id="0"/>
<mxCell id="1" parent="0"/>
<mxCell id="2" value="" style="ellipse;html=1;shape=startState;fillColor=#000000;strokeColor=#ff0000;" parent="1" vertex="1">
<mxGeometry x="388" y="260" width="30" height="30" as="geometry"/>
</mxCell>
<mxCell id="3" value="" style="edgeStyle=orthogonalEdgeStyle;html=1;verticalAlign=bottom;endArrow=open;endSize=8;strokeColor=#ff0000;" parent="1" source="2" target="5" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="403" y="440" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="4" value="创建订单" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="3" vertex="1" connectable="0">
<mxGeometry x="-0.1879" y="-1" relative="1" as="geometry">
<mxPoint as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="5" value="未支付" style="rounded=1;whiteSpace=wrap;html=1;arcSize=40;fontColor=#000000;fillColor=#ffffc0;strokeColor=#ff0000;" parent="1" vertex="1">
<mxGeometry x="343" y="370" width="120" height="40" as="geometry"/>
</mxCell>
<mxCell id="6" value="" style="edgeStyle=orthogonalEdgeStyle;html=1;verticalAlign=bottom;endArrow=open;endSize=8;strokeColor=#ff0000;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="1" source="5" target="8" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="462" y="540" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="7" value="支付" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="6" vertex="1" connectable="0">
<mxGeometry x="-0.2513" y="-1" relative="1" as="geometry">
<mxPoint x="1" y="13" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="8" value="已支付" style="rounded=1;whiteSpace=wrap;html=1;arcSize=40;fontColor=#000000;fillColor=#ffffc0;strokeColor=#ff0000;" parent="1" vertex="1">
<mxGeometry x="343" y="490" width="120" height="40" as="geometry"/>
</mxCell>
<mxCell id="9" value="" style="edgeStyle=orthogonalEdgeStyle;html=1;verticalAlign=bottom;endArrow=open;endSize=8;strokeColor=#ff0000;exitX=0;exitY=0.25;exitDx=0;exitDy=0;entryX=1;entryY=0.25;entryDx=0;entryDy=0;" parent="1" source="8" target="14" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="288" y="590" as="targetPoint"/>
<mxPoint x="343" y="510" as="sourcePoint"/>
</mxGeometry>
</mxCell>
<mxCell id="10" value="申请开票" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="9" vertex="1" connectable="0">
<mxGeometry x="-0.2606" relative="1" as="geometry">
<mxPoint x="-7" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="13" value="" style="ellipse;html=1;shape=endState;fillColor=#000000;strokeColor=#ff0000;" parent="1" vertex="1">
<mxGeometry x="388" y="595" width="30" height="30" as="geometry"/>
</mxCell>
<mxCell id="14" value="待开票" style="rounded=1;whiteSpace=wrap;html=1;arcSize=40;fontColor=#000000;fillColor=#ffffc0;strokeColor=#ff0000;" parent="1" vertex="1">
<mxGeometry x="129" y="490" width="120" height="40" as="geometry"/>
</mxCell>
<mxCell id="15" value="" style="edgeStyle=orthogonalEdgeStyle;html=1;verticalAlign=bottom;endArrow=open;endSize=8;strokeColor=#ff0000;exitX=0.5;exitY=1;exitDx=0;exitDy=0;" parent="1" source="14" target="17" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="273" y="730" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="16" value="同意开票" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="15" vertex="1" connectable="0">
<mxGeometry x="-0.2741" y="1" relative="1" as="geometry">
<mxPoint x="-1" y="8" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="17" value="已开票" style="rounded=1;whiteSpace=wrap;html=1;arcSize=40;fontColor=#000000;fillColor=#ffffc0;strokeColor=#ff0000;" parent="1" vertex="1">
<mxGeometry x="129" y="590" width="120" height="40" as="geometry"/>
</mxCell>
<mxCell id="18" value="" style="edgeStyle=orthogonalEdgeStyle;html=1;verticalAlign=bottom;endArrow=open;endSize=8;strokeColor=#ff0000;entryX=0;entryY=0.5;entryDx=0;entryDy=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" parent="1" source="17" target="13" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="273" y="840" as="targetPoint"/>
<Array as="points"/>
<mxPoint x="299" y="800" as="sourcePoint"/>
</mxGeometry>
</mxCell>
<mxCell id="19" value="下载发票" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="18" vertex="1" connectable="0">
<mxGeometry x="-0.2583" y="-1" relative="1" as="geometry">
<mxPoint x="-12" y="-1" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="21" value="开票失败" style="rounded=1;whiteSpace=wrap;html=1;arcSize=40;fontColor=#000000;fillColor=#ffffc0;strokeColor=#ff0000;" parent="1" vertex="1">
<mxGeometry x="129" y="370" width="120" height="40" as="geometry"/>
</mxCell>
<mxCell id="22" value="" style="edgeStyle=orthogonalEdgeStyle;html=1;verticalAlign=bottom;endArrow=open;endSize=8;strokeColor=#ff0000;exitX=0.75;exitY=0;exitDx=0;exitDy=0;entryX=0.75;entryY=1;entryDx=0;entryDy=0;" parent="1" source="14" target="21" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="478" y="750" as="targetPoint"/>
<Array as="points">
<mxPoint x="219" y="410"/>
<mxPoint x="219" y="410"/>
</Array>
</mxGeometry>
</mxCell>
<mxCell id="23" value="不同意开票" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="22" vertex="1" connectable="0">
<mxGeometry x="-0.1415" y="-1" relative="1" as="geometry">
<mxPoint x="-1" y="13" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="25" value="" style="edgeStyle=orthogonalEdgeStyle;html=1;verticalAlign=bottom;endArrow=open;endSize=8;strokeColor=#ff0000;exitX=0.25;exitY=1;exitDx=0;exitDy=0;entryX=0.25;entryY=0;entryDx=0;entryDy=0;" parent="1" source="21" target="14" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="149" y="580" as="targetPoint"/>
<mxPoint x="79" y="440" as="sourcePoint"/>
<Array as="points">
<mxPoint x="159" y="460"/>
<mxPoint x="159" y="460"/>
</Array>
</mxGeometry>
</mxCell>
<mxCell id="26" value="再次申请开票" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="25" vertex="1" connectable="0">
<mxGeometry x="-0.1415" y="-1" relative="1" as="geometry">
<mxPoint x="5" y="-1" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="27" value="待退款" style="rounded=1;whiteSpace=wrap;html=1;arcSize=40;fontColor=#000000;fillColor=#ffffc0;strokeColor=#ff0000;" parent="1" vertex="1">
<mxGeometry x="579" y="490" width="120" height="40" as="geometry"/>
</mxCell>
<mxCell id="28" value="" style="edgeStyle=orthogonalEdgeStyle;html=1;verticalAlign=bottom;endArrow=open;endSize=8;strokeColor=#ff0000;entryX=0.5;entryY=0;entryDx=0;entryDy=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;" parent="1" source="27" target="32" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="462" y="700" as="targetPoint"/>
<mxPoint x="579" y="650" as="sourcePoint"/>
</mxGeometry>
</mxCell>
<mxCell id="34" value="同意退款" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="28" vertex="1" connectable="0">
<mxGeometry x="-0.2333" y="-2" relative="1" as="geometry">
<mxPoint x="-2" y="3" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="30" value="" style="edgeStyle=orthogonalEdgeStyle;html=1;verticalAlign=bottom;endArrow=open;endSize=8;strokeColor=#ff0000;movable=1;resizable=1;rotatable=1;deletable=1;editable=1;connectable=1;exitX=1;exitY=0.75;exitDx=0;exitDy=0;entryX=0;entryY=0.75;entryDx=0;entryDy=0;" parent="1" source="8" target="27" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="499" y="550" as="targetPoint"/>
<mxPoint x="669" y="570" as="sourcePoint"/>
<Array as="points"/>
</mxGeometry>
</mxCell>
<mxCell id="31" value="申请退款" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="30" vertex="1" connectable="0">
<mxGeometry x="0.2333" relative="1" as="geometry">
<mxPoint x="-28" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="32" value="已退款" style="rounded=1;whiteSpace=wrap;html=1;arcSize=40;fontColor=#000000;fillColor=#ffffc0;strokeColor=#ff0000;" parent="1" vertex="1">
<mxGeometry x="579" y="590" width="120" height="40" as="geometry"/>
</mxCell>
<mxCell id="33" value="" style="edgeStyle=orthogonalEdgeStyle;html=1;verticalAlign=bottom;endArrow=open;endSize=8;strokeColor=#ff0000;entryX=1;entryY=0.5;entryDx=0;entryDy=0;exitX=0;exitY=0.5;exitDx=0;exitDy=0;" parent="1" source="32" target="13" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="462" y="830" as="targetPoint"/>
<Array as="points"/>
</mxGeometry>
</mxCell>
<mxCell id="35" value="退款到账" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="33" vertex="1" connectable="0">
<mxGeometry x="0.0112" y="3" relative="1" as="geometry">
<mxPoint x="30" y="-3" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="36" value="退款失败" style="rounded=1;whiteSpace=wrap;html=1;arcSize=40;fontColor=#000000;fillColor=#ffffc0;strokeColor=#ff0000;" parent="1" vertex="1">
<mxGeometry x="579" y="370" width="120" height="40" as="geometry"/>
</mxCell>
<mxCell id="37" value="" style="edgeStyle=orthogonalEdgeStyle;html=1;verticalAlign=bottom;endArrow=open;endSize=8;strokeColor=#ff0000;exitX=0.75;exitY=0;exitDx=0;exitDy=0;entryX=0.75;entryY=1;entryDx=0;entryDy=0;" parent="1" source="27" target="36" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="688" y="800" as="targetPoint"/>
<Array as="points">
<mxPoint x="669" y="460"/>
<mxPoint x="669" y="460"/>
</Array>
</mxGeometry>
</mxCell>
<mxCell id="38" value="拒绝退款" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="37" vertex="1" connectable="0">
<mxGeometry x="-0.4012" y="2" relative="1" as="geometry">
<mxPoint x="2" y="-16" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="40" value="" style="edgeStyle=orthogonalEdgeStyle;html=1;verticalAlign=bottom;endArrow=open;endSize=8;strokeColor=#ff0000;entryX=0.25;entryY=0;entryDx=0;entryDy=0;exitX=0.25;exitY=1;exitDx=0;exitDy=0;" parent="1" source="36" target="27" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="698" y="710" as="targetPoint"/>
<mxPoint x="738" y="590" as="sourcePoint"/>
<Array as="points">
<mxPoint x="609" y="470"/>
<mxPoint x="609" y="470"/>
</Array>
</mxGeometry>
</mxCell>
<mxCell id="41" value="再次申请退款" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="40" vertex="1" connectable="0">
<mxGeometry x="-0.4012" y="2" relative="1" as="geometry">
<mxPoint x="-12" y="16" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="43" value="" style="edgeStyle=orthogonalEdgeStyle;html=1;verticalAlign=bottom;endArrow=open;endSize=8;strokeColor=#ff0000;movable=1;resizable=1;rotatable=1;deletable=1;editable=1;connectable=1;entryX=1;entryY=0.25;entryDx=0;entryDy=0;exitX=0;exitY=0.25;exitDx=0;exitDy=0;" parent="1" source="27" target="8" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="442" y="640" as="targetPoint"/>
<mxPoint x="539" y="500" as="sourcePoint"/>
<Array as="points">
<mxPoint x="539" y="500"/>
<mxPoint x="539" y="500"/>
</Array>
</mxGeometry>
</mxCell>
<mxCell id="44" value="拒绝退款" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="43" vertex="1" connectable="0">
<mxGeometry x="0.2333" relative="1" as="geometry">
<mxPoint x="12" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="45" value="" style="edgeStyle=orthogonalEdgeStyle;html=1;verticalAlign=bottom;endArrow=open;endSize=8;strokeColor=#ff0000;entryX=0;entryY=0.75;entryDx=0;entryDy=0;exitX=1;exitY=0.75;exitDx=0;exitDy=0;" parent="1" source="14" target="8" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="349" y="610" as="targetPoint"/>
<mxPoint x="359" y="550" as="sourcePoint"/>
</mxGeometry>
</mxCell>
<mxCell id="46" value="拒绝开票" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="45" vertex="1" connectable="0">
<mxGeometry x="-0.2606" relative="1" as="geometry">
<mxPoint x="6" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="49" value="" style="edgeStyle=orthogonalEdgeStyle;html=1;verticalAlign=bottom;endArrow=open;endSize=8;strokeColor=#ff0000;entryX=0.5;entryY=0;entryDx=0;entryDy=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;" parent="1" source="8" target="13" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="413" y="500" as="targetPoint"/>
<mxPoint x="413" y="420" as="sourcePoint"/>
</mxGeometry>
</mxCell>
<mxCell id="50" value="不支持开发票" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="49" vertex="1" connectable="0">
<mxGeometry x="-0.2513" y="-1" relative="1" as="geometry">
<mxPoint x="1" y="6" as="offset"/>
</mxGeometry>
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

68
doc/权限控制.md Normal file
View File

@ -0,0 +1,68 @@
### 与权限有关的注解
`@Anonymous`注解用于配置公开接口
`@PreAuthorize`注解用于配置接口要求用户拥有某些权限才可访问,它拥有如下方法
| 方法 | 参数 | 描述 |
| ----------- | ------ | ---------------------------------------------- |
| hasPermi | String | 验证用户是否具备某权限 |
| lacksPermi | String | 验证用户是否不具备某权限,与 hasPermi逻辑相反 |
| hasAnyPermi | String | 验证用户是否具有以下任意一个权限 |
| hasRole | String | 判断用户是否拥有某个角色 |
| lacksRole | String | 验证用户是否不具备某角色,与 isRole逻辑相反 |
| hasAnyRoles | String | 验证用户是否具有以下任意一个角色,多个逗号分隔 |
```java
@PreAuthorize("@ss.hasPermi('system:user:list')")
@PreAuthorize("@ss.lacksPermi('system:user:list')")
@PreAuthorize("@ss.hasAnyPermi('system:user:add,system:user:edit')")
```
`@DataScope`注解用于配置接口数据权限
* `deptAlias`用于指定部门表的别名;
* `userAlias`用于指定用户表的别名;
* 实体需要继承BaseEntity类;
* `全部数据权限`、`自定数据权限`、`部门数据权限`、`部门及以下数据权限`、`仅本人数据权限`五种权限模式在后台角色管理界面配置数据权限
```java
// 部门数据权限注解
@DataScope(deptAlias = "d")
// 部门及用户权限注解
@DataScope(deptAlias = "d", userAlias = "u")
```
1. 使用注解
```java
@DataScope(deptAlias = "d", userAlias = "u")
public List<...> select(...)
{
return mapper.select(...);
}
```
2. 配置mybatis的xml
```xml
<select id="select" parameterType="..." resultMap="...Result">
<include refid="select...Vo"/>
<!-- 数据范围过滤 -->
${params.dataScope}
</select>
```
### 安全工具类
com.ruoyi.common.utils.SecurityUtils
| 方法 | 参数 | 返回 | 描述 |
| ------------ | ------ | ---------- | ------------------------ |
| getUserId | 无 | Long | 获取当前用户ID |
| getDeptId | 无 | Long | 获取当前部门ID |
| getUsername | 无 | String | 获取当前用户账户 |
| getLoginUser | 无 | LonginUser | 获取当前登录用户代理 |
| hasPermi | String | boolean | 验证用户是否具备某权限 |
| hasRole | String | boolean | 验证用户是否拥有某个角色 |

View File

@ -0,0 +1,100 @@
<mxfile host="65bd71144e">
<diagram id="TvbLNUR7pZR6Xx6IZirq" name="第 1 页">
<mxGraphModel dx="1050" dy="563" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
<root>
<mxCell id="0"/>
<mxCell id="1" parent="0"/>
<mxCell id="2" value="ruoyi-admin" style="html=1;" parent="1" vertex="1">
<mxGeometry x="380" y="780" width="110" height="50" as="geometry"/>
</mxCell>
<mxCell id="4" style="edgeStyle=none;html=1;" parent="1" source="3" target="5" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="10" style="edgeStyle=none;html=1;" edge="1" parent="1" source="3" target="9">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="28" style="edgeStyle=none;html=1;" edge="1" parent="1" source="3" target="26">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="34" style="edgeStyle=none;html=1;" edge="1" parent="1" source="3" target="31">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="3" value="ruoyi-common" style="html=1;" parent="1" vertex="1">
<mxGeometry x="380" y="320" width="110" height="50" as="geometry"/>
</mxCell>
<mxCell id="7" style="edgeStyle=none;html=1;" edge="1" parent="1" source="5" target="6">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="5" value="ruoyi-system" style="html=1;" vertex="1" parent="1">
<mxGeometry x="380" y="440" width="110" height="50" as="geometry"/>
</mxCell>
<mxCell id="8" style="edgeStyle=none;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="6" target="2">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="13" style="edgeStyle=none;html=1;" edge="1" parent="1" source="6" target="12">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="24" style="edgeStyle=none;html=1;" edge="1" parent="1" source="6" target="20">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="6" value="ruoyi-framework" style="html=1;" vertex="1" parent="1">
<mxGeometry x="380" y="560" width="110" height="50" as="geometry"/>
</mxCell>
<mxCell id="11" style="edgeStyle=none;html=1;" edge="1" parent="1" source="9" target="2">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="9" value="ruoyi-quartz" style="html=1;" vertex="1" parent="1">
<mxGeometry x="260" y="585" width="110" height="50" as="geometry"/>
</mxCell>
<mxCell id="14" style="edgeStyle=none;html=1;" edge="1" parent="1" source="12" target="2">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="12" value="ruoyi-online" style="html=1;" vertex="1" parent="1">
<mxGeometry x="520" y="585" width="110" height="50" as="geometry"/>
</mxCell>
<mxCell id="18" style="edgeStyle=none;html=1;" edge="1" parent="1" source="15" target="2">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="15" value="ruoyi-oauth-starter" style="html=1;" vertex="1" parent="1">
<mxGeometry x="590" y="700" width="110" height="50" as="geometry"/>
</mxCell>
<mxCell id="17" style="edgeStyle=none;html=1;" edge="1" parent="1" source="16" target="2">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="16" value="ruoyi-pay-stater" style="html=1;" vertex="1" parent="1">
<mxGeometry x="180" y="730" width="110" height="50" as="geometry"/>
</mxCell>
<mxCell id="23" style="edgeStyle=none;html=1;" edge="1" parent="1" source="19" target="15">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="19" value="ruoyi-oauth-xx" style="html=1;" vertex="1" parent="1">
<mxGeometry x="660" y="585" width="110" height="50" as="geometry"/>
</mxCell>
<mxCell id="22" style="edgeStyle=none;html=1;" edge="1" parent="1" source="20" target="19">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="20" value="ruoyi-oauth-common" style="html=1;" vertex="1" parent="1">
<mxGeometry x="660" y="370" width="110" height="50" as="geometry"/>
</mxCell>
<mxCell id="30" style="edgeStyle=none;html=1;" edge="1" parent="1" source="25" target="16">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="25" value="ruoyi-pay-xx" style="html=1;" vertex="1" parent="1">
<mxGeometry x="20" y="585" width="110" height="50" as="geometry"/>
</mxCell>
<mxCell id="29" style="edgeStyle=none;html=1;" edge="1" parent="1" source="26" target="25">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="26" value="ruoyi-pay-common" style="html=1;" vertex="1" parent="1">
<mxGeometry x="20" y="400" width="110" height="50" as="geometry"/>
</mxCell>
<mxCell id="33" style="edgeStyle=none;html=1;" edge="1" parent="1" source="31" target="2">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="31" value="ruoyi-generator" style="html=1;" vertex="1" parent="1">
<mxGeometry x="140" y="585" width="110" height="50" as="geometry"/>
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

131
doc/登录+JWT逻辑.drawio Normal file
View File

@ -0,0 +1,131 @@
<mxfile host="65bd71144e">
<diagram id="IyeDk-cAWrKOq7F3gyWb" name="第 1 页">
<mxGraphModel dx="344" dy="766" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" background="#FFFFFF" math="0" shadow="0">
<root>
<mxCell id="0"/>
<mxCell id="1" parent="0"/>
<mxCell id="7" value="" style="edgeStyle=none;html=1;" parent="1" source="5" target="6" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="5" value="访问/login" style="html=1;" parent="1" vertex="1">
<mxGeometry x="300" y="30" width="110" height="50" as="geometry"/>
</mxCell>
<mxCell id="9" value="" style="edgeStyle=none;html=1;" parent="1" source="6" target="20" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="359.0000000000001" y="200" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="6" value="被SysLoginController.login()捕获" style="html=1;" parent="1" vertex="1">
<mxGeometry x="261.5" y="100" width="187" height="30" as="geometry"/>
</mxCell>
<mxCell id="20" value="&lt;span style=&quot;font-weight: 400;&quot;&gt;调用方法SysLoginService.login()执行登录逻辑&lt;/span&gt;" style="swimlane;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="110" y="155" width="490" height="935" as="geometry">
<mxRectangle x="109" y="195" width="290" height="30" as="alternateBounds"/>
</mxGeometry>
</mxCell>
<mxCell id="22" value="" style="edgeStyle=none;html=1;" parent="20" source="10" target="21" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="10" value="&lt;div style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;background-color: transparent;&quot;&gt;调用方法SysLoginService.validateCaptcha()&lt;/span&gt;&lt;/div&gt;&lt;div style=&quot;&quot;&gt;&lt;span style=&quot;background-color: transparent;&quot;&gt;1. &lt;/span&gt;&lt;font style=&quot;background-color: transparent; color: rgb(0, 0, 204);&quot;&gt;从redis里拿到验证码对应的验证信息&lt;/font&gt;&lt;/div&gt;&lt;div style=&quot;&quot;&gt;&lt;span style=&quot;background-color: transparent;&quot;&gt;2. &lt;/span&gt;&lt;font style=&quot;background-color: transparent; color: rgb(0, 0, 204);&quot;&gt;删除redis中验证码对应的验证信息&lt;/font&gt;&lt;/div&gt;&lt;div style=&quot;&quot;&gt;&lt;span style=&quot;background-color: transparent;&quot;&gt;3. 如果验证信息为空,&lt;/span&gt;&lt;font style=&quot;background-color: transparent;&quot;&gt;抛出验证码过期的异常&lt;/font&gt;&lt;/div&gt;&lt;div style=&quot;&quot;&gt;&lt;span style=&quot;background-color: transparent;&quot;&gt;4. 如果code与验证信息的值不同&lt;/span&gt;&lt;font style=&quot;background-color: transparent;&quot;&gt;抛出验证码错误的异常&lt;/font&gt;&lt;/div&gt;" style="html=1;align=left;" parent="20" vertex="1">
<mxGeometry x="87.42999999999999" y="35" width="315.15" height="100" as="geometry"/>
</mxCell>
<mxCell id="24" value="" style="edgeStyle=none;html=1;" parent="20" source="21" target="23" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="21" value="调用方法SysLoginService.loginPreCheck()&lt;br&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;background-color: transparent;&quot;&gt;1. &lt;/span&gt;&lt;font style=&quot;background-color: transparent;&quot;&gt;用户名或密码为空 -&amp;gt; 错误 &lt;font style=&quot;color: rgb(255, 0, 0);&quot;&gt;抛出异常&lt;/font&gt;&lt;/font&gt;&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;background-color: transparent;&quot;&gt;2. &lt;/span&gt;&lt;font style=&quot;background-color: transparent;&quot;&gt;密码不在指定范围内 -&amp;gt; 错误 &lt;font style=&quot;color: rgb(255, 0, 0);&quot;&gt;抛出异常&lt;/font&gt;&lt;/font&gt;&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;background-color: transparent;&quot;&gt;3. &lt;/span&gt;&lt;font style=&quot;background-color: transparent;&quot;&gt;用户名不在指定范围内 -&amp;gt; 错误 &lt;font style=&quot;color: rgb(255, 0, 0);&quot;&gt;抛出异常&lt;/font&gt;&lt;/font&gt;&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;background-color: transparent;&quot;&gt;4. &lt;/span&gt;&lt;font style=&quot;background-color: transparent;&quot;&gt;IP黑名单校验 -&amp;gt; 错误 &lt;font style=&quot;color: rgb(255, 0, 0);&quot;&gt;抛出异常&lt;/font&gt;&lt;/font&gt;&lt;/div&gt;" style="html=1;" parent="20" vertex="1">
<mxGeometry x="112.91" y="170" width="267.1" height="100" as="geometry"/>
</mxCell>
<mxCell id="26" style="edgeStyle=none;html=1;" parent="20" source="23" target="25" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="23" value="&lt;font style=&quot;color: rgb(0, 204, 0);&quot;&gt;通过new UsernamePasswordAuthenticationToken生成一个认证信息&lt;br&gt;通过AuthenticationContextHolder.setContext将认证信息设置到上下文&lt;/font&gt;&lt;br&gt;通过AuthenticationManager.authenticate()对认证信息进行认证,这个方法&lt;br&gt;会去调用UserDetailsServiceImpl.loadUserByUsername()方法进行认证" style="html=1;" parent="20" vertex="1">
<mxGeometry x="43.39" y="295" width="406.13" height="70" as="geometry"/>
</mxCell>
<mxCell id="68" style="edgeStyle=none;html=1;" edge="1" parent="20" source="25" target="40">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="25" value="&lt;span style=&quot;font-weight: 400;&quot;&gt;loadUserByUsername&lt;/span&gt;" style="swimlane;whiteSpace=wrap;html=1;" parent="20" vertex="1">
<mxGeometry x="27.43" y="385" width="438.06" height="355" as="geometry">
<mxRectangle x="25.97999999999999" y="380" width="160" height="30" as="alternateBounds"/>
</mxGeometry>
</mxCell>
<mxCell id="29" value="" style="edgeStyle=none;html=1;" parent="25" source="27" target="28" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="27" value="调用ISysUserService.selectUserByUserName()获取用户信息&lt;br&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;background-color: transparent;&quot;&gt;校验是否为空 -&amp;gt; &lt;/span&gt;&lt;font style=&quot;background-color: transparent; color: rgb(255, 0, 0);&quot;&gt;抛出用户不存在的异常&lt;/font&gt;&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;background-color: transparent;&quot;&gt;校验是否已被删除 -&amp;gt; &lt;/span&gt;&lt;font style=&quot;background-color: transparent; color: rgb(255, 0, 0);&quot;&gt;抛出用户已被删除的异常&lt;/font&gt;&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;background-color: transparent;&quot;&gt;校验是否已被停用 -&amp;gt; &lt;/span&gt;&lt;font style=&quot;background-color: transparent; color: rgb(255, 0, 0);&quot;&gt;抛出用户已被停用的异常&lt;/font&gt;&lt;/div&gt;" style="whiteSpace=wrap;html=1;" parent="25" vertex="1">
<mxGeometry x="48.55" y="40" width="340.95" height="60" as="geometry"/>
</mxCell>
<mxCell id="28" value="调用SysPasswordService.validate()进行认证&lt;br&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;background-color: transparent;&quot;&gt;1. 获取上下文的认证信息,并获取认证信息中携带的用户名和密码&lt;/span&gt;&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;background-color: transparent;&quot;&gt;2. &lt;/span&gt;&lt;font style=&quot;background-color: transparent; color: rgb(0, 0, 255);&quot;&gt;从redis中获取该用户名密码错误次数&lt;/font&gt;&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;background-color: transparent;&quot;&gt;3. 校验该用户名密码错误次数是否过多&lt;/span&gt;&lt;span style=&quot;background-color: transparent;&quot;&gt;若次数过多,则&lt;/span&gt;&lt;font style=&quot;background-color: transparent; color: rgb(255, 0, 0);&quot;&gt;抛出密码错误次数过多的异常&lt;/font&gt;&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;background-color: transparent;&quot;&gt;4. 调用SecurityUtils.matchesPassword方法校验用户名密码是否匹配&lt;/span&gt;&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;background-color: transparent;&quot;&gt;5. 若不匹配,&lt;font style=&quot;color: rgb(0, 0, 255);&quot;&gt;则密码错误次数加一&lt;/font&gt;,且&lt;/span&gt;&lt;font style=&quot;background-color: transparent;&quot;&gt;抛&lt;font style=&quot;color: rgb(255, 0, 0);&quot;&gt;出密码错误的异常&lt;/font&gt;&lt;/font&gt;&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;background-color: transparent;&quot;&gt;6. 若匹配,&lt;font style=&quot;color: rgb(0, 0, 255);&quot;&gt;则清除密码错误次数&lt;/font&gt;,校验结束即校验通过&lt;/span&gt;&lt;/div&gt;" style="whiteSpace=wrap;html=1;" parent="25" vertex="1">
<mxGeometry x="33.56" y="135" width="370.95" height="120" as="geometry"/>
</mxCell>
<mxCell id="36" value="调用createLoginUser方法new&amp;nbsp;LoginUser()创建一个UserDetails凭证&lt;br&gt;调用SysPermissionService.getMenuPermission()方法获取用户角色权限" style="html=1;" parent="25" vertex="1">
<mxGeometry x="27.98" y="290" width="382.09" height="50" as="geometry"/>
</mxCell>
<mxCell id="37" style="edgeStyle=none;html=1;" parent="25" source="28" target="36" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="324.02" y="280" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="44" value="" style="edgeStyle=none;html=1;" parent="20" source="40" target="43" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="40" value="1. &lt;font color=&quot;#ff0000&quot;&gt;若上述环节抛出异常则处理异常并抛出对应的新的异常&lt;/font&gt;&lt;div&gt;&lt;font style=&quot;color: rgb(0, 0, 0);&quot;&gt;2. 若上述环节正常运行,则校验环节结束&lt;/font&gt;&lt;br&gt;3. 无论校验结果如何,清除上下文&lt;/div&gt;" style="html=1;align=left;" parent="20" vertex="1">
<mxGeometry x="90.98" y="765" width="308.06" height="55" as="geometry"/>
</mxCell>
<mxCell id="43" value="1. 调用createToken()记录并更新用户信息&lt;br&gt;2. 调用TokenService.createToken()生成token&lt;br&gt;3. 设置用户代理信息LoginUser将&lt;font style=&quot;color: rgb(0, 0, 255);&quot;&gt;LoginUser存入redis&lt;/font&gt;" style="html=1;align=left;" parent="20" vertex="1">
<mxGeometry x="92.42999999999999" y="845" width="308.06" height="70" as="geometry"/>
</mxCell>
<mxCell id="47" style="edgeStyle=none;html=1;" parent="1" source="43" target="48" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="333.06500000000005" y="1290.0000000000002" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="65" value="" style="edgeStyle=none;html=1;fontColor=#00FFFF;" parent="1" source="48" target="64" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="48" value="认证成功将Token返回给前端" style="html=1;" parent="1" vertex="1">
<mxGeometry x="254.73000000000002" y="1110" width="203.46" height="31" as="geometry"/>
</mxCell>
<mxCell id="51" value="" style="edgeStyle=none;html=1;" parent="1" source="49" target="50" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="49" value="Token认证" style="whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="765" y="1010" width="120" height="60" as="geometry"/>
</mxCell>
<mxCell id="53" value="" style="edgeStyle=none;html=1;" parent="1" source="50" target="52" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="50" value="触发JwtAuthenticationTokenFilter拦截器&lt;br&gt;调用doFilterInternal方法进行处理" style="whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="690" y="920" width="270" height="60" as="geometry"/>
</mxCell>
<mxCell id="55" value="" style="edgeStyle=none;html=1;" parent="1" source="52" target="54" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="52" value="调用TokenService.getLoginUser()方法&lt;br&gt;1. 调用方法getToken从request的请求头中获取token&lt;br&gt;&lt;font style=&quot;color: rgb(0, 0, 204);&quot;&gt;2. 通过token在redis中获取用户代理信息LoginUser&lt;/font&gt;" style="whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="675" y="830" width="300" height="60" as="geometry"/>
</mxCell>
<mxCell id="57" value="" style="edgeStyle=none;html=1;" parent="1" source="54" target="56" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="54" value="判断用户凭证是非空并且判断上下文认证信息为空&lt;br&gt;调用TokenService.verifyToken()校验token并判断最小时间刷新token" style="whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="630" y="750" width="390" height="60" as="geometry"/>
</mxCell>
<mxCell id="59" value="" style="edgeStyle=none;html=1;" parent="1" source="56" target="58" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="56" value="&lt;font style=&quot;color: rgb(0, 153, 0);&quot;&gt;1. 通过UsernamePasswordAuthenticationToken创建一个认证信息&lt;br&gt;2. 通过WebAuthenticationDetailsSource构建一个用户凭证并通过UsernamePasswordAuthenticationToken.setDetails设置到认证信息里&lt;br&gt;3. 将认证信息通过SecurityContextHolder.getContext().setAuthentication拿到上下文并设置到上下文&lt;/font&gt;" style="whiteSpace=wrap;html=1;strokeColor=#00CC00;align=left;" parent="1" vertex="1">
<mxGeometry x="632.75" y="640" width="384.51" height="80" as="geometry"/>
</mxCell>
<mxCell id="58" value="继续过滤&lt;br&gt;在鉴权系统中会用到上述设置的上下文" style="whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="755" y="555" width="140" height="60" as="geometry"/>
</mxCell>
<mxCell id="66" style="edgeStyle=none;html=1;fontColor=#00FFFF;" parent="1" source="64" target="49" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="64" value="前端拿到token后存到浏览器中&lt;br&gt;后续发送请求在浏览器中拿到token&lt;br&gt;并将token设置到请求头里" style="whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="705.49" y="1090" width="239.02" height="70" as="geometry"/>
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

Binary file not shown.

View File

@ -0,0 +1,260 @@
<mxfile host="65bd71144e">
<diagram id="PzGHVj2JrUCharDApkJk" name="第 1 页">
<mxGraphModel dx="1496" dy="1393" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
<root>
<mxCell id="0"/>
<mxCell id="1" parent="0"/>
<mxCell id="7" value="" style="edgeStyle=none;html=1;" parent="1" source="5" target="6" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="5" value="调用登录&lt;br&gt;发送验证码&lt;br&gt;接口" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.start_1;whiteSpace=wrap;" parent="1" vertex="1">
<mxGeometry x="59" y="60" width="100" height="60" as="geometry"/>
</mxCell>
<mxCell id="9" value="" style="edgeStyle=none;html=1;" parent="1" source="6" target="8" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="13" value="不存在账户" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="9" vertex="1" connectable="0">
<mxGeometry x="-0.325" y="-1" relative="1" as="geometry">
<mxPoint x="13" y="-1" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="11" value="" style="edgeStyle=none;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;exitPerimeter=0;" parent="1" source="6" target="10" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="12" value="存在账户" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="11" vertex="1" connectable="0">
<mxGeometry x="0.2667" y="-1" relative="1" as="geometry">
<mxPoint x="1" y="-14" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="6" value="是否存在账户" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.decision;whiteSpace=wrap;" parent="1" vertex="1">
<mxGeometry x="59" y="170" width="100" height="100" as="geometry"/>
</mxCell>
<mxCell id="18" value="" style="edgeStyle=none;html=1;" parent="1" source="8" target="17" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="22" value="否" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="18" vertex="1" connectable="0">
<mxGeometry x="-0.3667" relative="1" as="geometry">
<mxPoint as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="20" value="" style="edgeStyle=none;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;exitPerimeter=0;" parent="1" source="8" target="10" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="309" y="340" as="targetPoint"/>
<Array as="points">
<mxPoint x="289" y="388"/>
</Array>
</mxGeometry>
</mxCell>
<mxCell id="21" value="是" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="20" vertex="1" connectable="0">
<mxGeometry x="-0.2857" y="-1" relative="1" as="geometry">
<mxPoint x="1" y="-41" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="8" value="是否开启&lt;br&gt;自动注册" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.decision;whiteSpace=wrap;" parent="1" vertex="1">
<mxGeometry x="239" y="170" width="100" height="100" as="geometry"/>
</mxCell>
<mxCell id="24" value="" style="edgeStyle=none;html=1;" parent="1" source="10" target="23" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="10" value="发送&lt;br&gt;登录验证码" style="rounded=1;whiteSpace=wrap;html=1;absoluteArcSize=1;arcSize=14;strokeWidth=2;" parent="1" vertex="1">
<mxGeometry x="59" y="338" width="100" height="100" as="geometry"/>
</mxCell>
<mxCell id="17" value="抛出异常&lt;br&gt;没有该用户" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.terminator;whiteSpace=wrap;" parent="1" vertex="1">
<mxGeometry x="379" y="190" width="100" height="60" as="geometry"/>
</mxCell>
<mxCell id="34" value="" style="edgeStyle=none;html=1;" parent="1" source="23" target="33" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="23" value="用户收到验证码&lt;br&gt;携带验证码调用&lt;br&gt;登录验证接口" style="rounded=1;whiteSpace=wrap;html=1;absoluteArcSize=1;arcSize=14;strokeWidth=2;" parent="1" vertex="1">
<mxGeometry x="59" y="490" width="100" height="100" as="geometry"/>
</mxCell>
<mxCell id="30" value="" style="edgeStyle=none;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;exitPerimeter=0;" parent="1" source="27" target="29" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="109" y="1027"/>
</Array>
</mxGeometry>
</mxCell>
<mxCell id="32" value="是" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="30" vertex="1" connectable="0">
<mxGeometry x="-0.22" y="-2" relative="1" as="geometry">
<mxPoint x="2" y="-25" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="40" value="" style="edgeStyle=none;html=1;" parent="1" source="27" target="31" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="41" value="否" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="40" vertex="1" connectable="0">
<mxGeometry x="-0.3619" y="-4" relative="1" as="geometry">
<mxPoint y="-4" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="27" value="是否存在账户" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.decision;whiteSpace=wrap;" parent="1" vertex="1">
<mxGeometry x="59" y="866.75" width="100" height="100" as="geometry"/>
</mxCell>
<mxCell id="29" value="返回token" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.terminator;whiteSpace=wrap;" parent="1" vertex="1">
<mxGeometry x="189" y="996.75" width="100" height="60" as="geometry"/>
</mxCell>
<mxCell id="42" style="edgeStyle=none;html=1;" parent="1" source="31" target="29" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="379" y="1027"/>
</Array>
</mxGeometry>
</mxCell>
<mxCell id="31" value="创建账户" style="rounded=1;whiteSpace=wrap;html=1;absoluteArcSize=1;arcSize=14;strokeWidth=2;" parent="1" vertex="1">
<mxGeometry x="329" y="866.75" width="100" height="100" as="geometry"/>
</mxCell>
<mxCell id="36" value="" style="edgeStyle=none;html=1;" parent="1" source="33" target="35" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="39" value="否" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="36" vertex="1" connectable="0">
<mxGeometry x="-0.3619" y="-1" relative="1" as="geometry">
<mxPoint as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="37" value="" style="edgeStyle=none;html=1;" parent="1" source="33" target="27" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points"/>
</mxGeometry>
</mxCell>
<mxCell id="38" value="是" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="37" vertex="1" connectable="0">
<mxGeometry x="-0.1238" relative="1" as="geometry">
<mxPoint as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="33" value="验证码&lt;br&gt;是否正确" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.decision;whiteSpace=wrap;" parent="1" vertex="1">
<mxGeometry x="59" y="728" width="100" height="100" as="geometry"/>
</mxCell>
<mxCell id="35" value="抛出异常&lt;br&gt;验证码错误" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.terminator;whiteSpace=wrap;" parent="1" vertex="1">
<mxGeometry x="379" y="748" width="100" height="60" as="geometry"/>
</mxCell>
<mxCell id="45" value="" style="edgeStyle=none;html=1;" parent="1" source="43" target="44" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="43" value="调用注册&lt;br&gt;发送验证码&lt;br&gt;接口" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.start_1;whiteSpace=wrap;" parent="1" vertex="1">
<mxGeometry x="669" y="348" width="100" height="60" as="geometry"/>
</mxCell>
<mxCell id="48" value="" style="edgeStyle=none;html=1;" parent="1" source="44" target="47" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="49" value="是" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="48" vertex="1" connectable="0">
<mxGeometry x="-0.4111" relative="1" as="geometry">
<mxPoint as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="51" value="" style="edgeStyle=none;html=1;" parent="1" source="44" target="50" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="52" value="否" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="51" vertex="1" connectable="0">
<mxGeometry x="-0.5067" y="1" relative="1" as="geometry">
<mxPoint x="-1" y="10" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="44" value="是否存在账户" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.decision;whiteSpace=wrap;" parent="1" vertex="1">
<mxGeometry x="669" y="448" width="100" height="100" as="geometry"/>
</mxCell>
<mxCell id="47" value="抛出异常&lt;br&gt;账户已存在" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.terminator;whiteSpace=wrap;" parent="1" vertex="1">
<mxGeometry x="509" y="468" width="100" height="60" as="geometry"/>
</mxCell>
<mxCell id="54" value="" style="edgeStyle=none;html=1;" parent="1" source="50" target="53" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="50" value="发送&lt;br&gt;注册验证码" style="rounded=1;whiteSpace=wrap;html=1;absoluteArcSize=1;arcSize=14;strokeWidth=2;" parent="1" vertex="1">
<mxGeometry x="669" y="578" width="100" height="100" as="geometry"/>
</mxCell>
<mxCell id="56" value="" style="edgeStyle=none;html=1;" parent="1" source="53" target="55" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="53" value="用户收到验证码&lt;br&gt;携带验证码调用&lt;br&gt;注册验证接口" style="rounded=1;whiteSpace=wrap;html=1;absoluteArcSize=1;arcSize=14;strokeWidth=2;" parent="1" vertex="1">
<mxGeometry x="669" y="728" width="100" height="100" as="geometry"/>
</mxCell>
<mxCell id="60" style="edgeStyle=none;html=1;" parent="1" source="55" target="35" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="61" value="否" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="60" vertex="1" connectable="0">
<mxGeometry x="0.2444" y="5" relative="1" as="geometry">
<mxPoint x="11" y="-5" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="62" style="edgeStyle=none;html=1;" parent="1" source="55" target="81" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="709" y="873" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="63" value="是" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="62" vertex="1" connectable="0">
<mxGeometry x="-0.2358" y="4" relative="1" as="geometry">
<mxPoint x="-4" y="-7" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="55" value="验证码&lt;br&gt;是否正确" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.decision;whiteSpace=wrap;" parent="1" vertex="1">
<mxGeometry x="529" y="728" width="100" height="100" as="geometry"/>
</mxCell>
<mxCell id="70" value="" style="edgeStyle=none;html=1;" parent="1" source="64" target="69" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="64" value="调用重置密码&lt;br&gt;发送验证码&lt;br&gt;接口" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.start_1;whiteSpace=wrap;" parent="1" vertex="1">
<mxGeometry x="516.5" y="60" width="100" height="60" as="geometry"/>
</mxCell>
<mxCell id="72" value="" style="edgeStyle=none;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;" parent="1" source="69" target="17" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="859" y="193" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="73" value="否" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="72" vertex="1" connectable="0">
<mxGeometry x="-0.2519" relative="1" as="geometry">
<mxPoint as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="75" value="" style="edgeStyle=none;html=1;" parent="1" source="69" target="74" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="69" value="是否存在账户" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.decision;whiteSpace=wrap;" parent="1" vertex="1">
<mxGeometry x="516.5" y="170" width="100" height="100" as="geometry"/>
</mxCell>
<mxCell id="77" value="" style="edgeStyle=none;html=1;" parent="1" source="74" target="76" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="74" value="发送&lt;br&gt;重置验证码" style="rounded=1;whiteSpace=wrap;html=1;absoluteArcSize=1;arcSize=14;strokeWidth=2;" parent="1" vertex="1">
<mxGeometry x="516.5" y="338" width="100" height="100" as="geometry"/>
</mxCell>
<mxCell id="79" value="" style="edgeStyle=none;html=1;" parent="1" source="76" target="78" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="76" value="用户收到验证码&lt;br&gt;携带验证码调用&lt;br&gt;重置验证接口" style="rounded=1;whiteSpace=wrap;html=1;absoluteArcSize=1;arcSize=14;strokeWidth=2;" parent="1" vertex="1">
<mxGeometry x="379" y="338" width="100" height="100" as="geometry"/>
</mxCell>
<mxCell id="82" style="edgeStyle=none;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;exitPerimeter=0;" parent="1" source="78" target="35" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="83" value="否" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="82" vertex="1" connectable="0">
<mxGeometry x="-0.35" y="1" relative="1" as="geometry">
<mxPoint as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="85" value="" style="edgeStyle=none;html=1;" parent="1" source="78" target="84" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="86" value="是" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="85" vertex="1" connectable="0">
<mxGeometry x="-0.3905" relative="1" as="geometry">
<mxPoint as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="78" value="验证码&lt;br&gt;是否正确" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.decision;whiteSpace=wrap;" parent="1" vertex="1">
<mxGeometry x="379" y="518" width="100" height="100" as="geometry"/>
</mxCell>
<mxCell id="81" value="创建账户" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.terminator;whiteSpace=wrap;" parent="1" vertex="1">
<mxGeometry x="529" y="874.75" width="100" height="60" as="geometry"/>
</mxCell>
<mxCell id="88" value="" style="edgeStyle=none;html=1;" parent="1" source="84" target="87" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="84" value="修改原有的密码" style="rounded=1;whiteSpace=wrap;html=1;absoluteArcSize=1;arcSize=14;strokeWidth=2;" parent="1" vertex="1">
<mxGeometry x="239" y="518" width="100" height="100" as="geometry"/>
</mxCell>
<mxCell id="87" value="删除当前&lt;br&gt;登录用户&lt;br&gt;凭证信息" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.terminator;whiteSpace=wrap;" parent="1" vertex="1">
<mxGeometry x="239" y="668" width="100" height="60" as="geometry"/>
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

37
doc/限流逻辑.drawio Normal file
View File

@ -0,0 +1,37 @@
<mxfile host="65bd71144e">
<diagram id="PZYBk4sDCuIX38xMYcUk" name="第 1 页">
<mxGraphModel dx="880" dy="766" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
<root>
<mxCell id="0"/>
<mxCell id="1" parent="0"/>
<mxCell id="4" value="" style="edgeStyle=none;html=1;" parent="1" source="2" target="3" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="2" value="标记有RateLimiter注解的方法会被RateLimiterAspect拦截" style="html=1;" parent="1" vertex="1">
<mxGeometry x="224" y="80" width="380" height="50" as="geometry"/>
</mxCell>
<mxCell id="6" value="" style="edgeStyle=none;html=1;" parent="1" source="3" target="5" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="3" value="获取注解上的时间和次数" style="html=1;" parent="1" vertex="1">
<mxGeometry x="224" y="150" width="380" height="50" as="geometry"/>
</mxCell>
<mxCell id="8" value="" style="edgeStyle=none;html=1;" parent="1" source="5" target="7" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="5" value="&amp;nbsp;通过拼接{KEY}-{IP/USERID/DEPTID}-{ClassName}-{MethodName}形成rediskey" style="html=1;" parent="1" vertex="1">
<mxGeometry x="164" y="230" width="500" height="50" as="geometry"/>
</mxCell>
<mxCell id="10" value="" style="edgeStyle=none;html=1;" parent="1" source="7" target="9" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="7" value="通过redisTemplate.excute来检测time时间内最大请求次数" style="html=1;" parent="1" vertex="1">
<mxGeometry x="164" y="310" width="500" height="50" as="geometry"/>
</mxCell>
<mxCell id="9" value="通过返回的number再次确认请求次数" style="html=1;" parent="1" vertex="1">
<mxGeometry x="164" y="390" width="500" height="50" as="geometry"/>
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

361
pom.xml Normal file
View File

@ -0,0 +1,361 @@
<?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.geekxd</groupId>
<artifactId>ruoyi</artifactId>
<version>3.9.0-G</version>
<name>ruoyi</name>
<url>https://gitee.com/geek-xd/ruoyi-geek-springboot3.git</url>
<description>若依Geek管理系统</description>
<properties>
<ruoyi.version>3.9.0-G</ruoyi.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>21</java.version>
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
<spring-boot.version>3.5.6</spring-boot.version>
<druid.version>1.2.27</druid.version>
<jta.version>1.1</jta.version>
<!-- bitwalker目前处于准废弃状态:最近一次更新2018.1.24 -->
<bitwalker.version>1.21</bitwalker.version>
<!-- kaptcha目前处于准废弃状态:最近一次更新2015.5.18 -->
<kaptcha.version>2.3.3</kaptcha.version>
<pagehelper.boot.version>2.1.1</pagehelper.boot.version>
<fastjson.version>2.0.58</fastjson.version>
<jackson.version>2.19.2</jackson.version>
<oshi.version>6.8.3</oshi.version>
<commons-io.version>2.20.0</commons-io.version>
<commons.collections.version>4.5.0</commons.collections.version>
<poi.version>5.4.1</poi.version>
<httpclient5.version>5.5</httpclient5.version>
<velocity.version>2.4.1</velocity.version>
<jwt.version>0.12.6</jwt.version>
<knife4j.version>4.5.0</knife4j.version>
<mybatis-spring-boot.version>3.0.4</mybatis-spring-boot.version>
<mybatis.version>3.5.16</mybatis.version>
<!-- druid 的 xa暂时不支持 mysql 9.0.0+ 连接速度:8.3.0>8.2.0>8.4.0 -->
<mysql.version>8.3.0</mysql.version>
<postgresql.version>42.7.7</postgresql.version>
<jaxb-api.version>4.0.2</jaxb-api.version>
<jaxb.version>4.0.5</jaxb.version>
<springdoc.version>2.8.9</springdoc.version>
</properties>
<!-- 依赖声明 -->
<dependencyManagement>
<dependencies>
<!-- spring-boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- Mysql驱动包 -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>${mysql.version}</version>
<scope>runtime</scope>
</dependency>
<!-- postgresql驱动包 -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>${postgresql.version}</version>
<scope>runtime</scope>
</dependency>
<!-- 阿里数据库连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-3-starter</artifactId>
<version>${druid.version}</version>
</dependency>
<dependency>
<groupId>javax.transaction</groupId>
<artifactId>jta</artifactId>
<version>${jta.version}</version>
</dependency>
<!-- mybatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis-spring-boot.version}</version>
</dependency>
<!-- pagehelper 分页插件 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>${pagehelper.boot.version}</version>
<!-- 排除低版本的 mybatis -->
<exclusions>
<exclusion>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 获取系统信息 -->
<dependency>
<groupId>com.github.oshi</groupId>
<artifactId>oshi-core</artifactId>
<version>${oshi.version}</version>
</dependency>
<!-- io常用工具类 -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons-io.version}</version>
</dependency>
<!-- excel工具 -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>${poi.version}</version>
</dependency>
<!-- velocity代码生成使用模板 -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>${velocity.version}</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
<version>${httpclient5.version}</version>
</dependency>
<!-- collections工具类 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>${commons.collections.version}</version>
</dependency>
<!-- 阿里JSON解析器 -->
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>${fastjson.version}</version>
</dependency>
<!--引入jsr310模块 支持java8的时间序列化 -->
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>${jackson.version}</version>
</dependency>
<!-- Token生成与解析-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>${jwt.version}</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>${jwt.version}</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>${jwt.version}</version>
</dependency>
<!-- jaxb 处理xml -->
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
<version>${jaxb-api.version}</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>${jaxb.version}</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-core</artifactId>
<version>${jaxb.version}</version>
</dependency>
<!-- 解析客户端操作系统、浏览器等 -->
<dependency>
<groupId>eu.bitwalker</groupId>
<artifactId>UserAgentUtils</artifactId>
<version>${bitwalker.version}</version>
</dependency>
<!-- 验证码 -->
<dependency>
<groupId>pro.fessional</groupId>
<artifactId>kaptcha</artifactId>
<version>${kaptcha.version}</version>
</dependency>
<!-- springdoc -->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>${springdoc.version}</version>
</dependency>
<!-- knife4j -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
<version>${knife4j.version}</version>
<exclusions>
<exclusion>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 核心模块-->
<dependency>
<groupId>com.ruoyi.geekxd</groupId>
<artifactId>ruoyi-framework</artifactId>
<version>${ruoyi.version}</version>
</dependency>
<!-- 系统模块-->
<dependency>
<groupId>com.ruoyi.geekxd</groupId>
<artifactId>ruoyi-system</artifactId>
<version>${ruoyi.version}</version>
</dependency>
<!-- 通用工具-->
<dependency>
<groupId>com.ruoyi.geekxd</groupId>
<artifactId>ruoyi-common</artifactId>
<version>${ruoyi.version}</version>
</dependency>
<!-- 第三方登录启动器 -->
<dependency>
<groupId>com.ruoyi.geekxd</groupId>
<artifactId>ruoyi-auth-starter</artifactId>
<version>${ruoyi.version}</version>
</dependency>
<!-- 支付管理启动器 -->
<dependency>
<groupId>com.ruoyi.geekxd</groupId>
<artifactId>ruoyi-pay-starter</artifactId>
<version>${ruoyi.version}</version>
</dependency>
<!-- 文件管理启动器 -->
<dependency>
<groupId>com.ruoyi.geekxd</groupId>
<artifactId>ruoyi-file-starter</artifactId>
<version>${ruoyi.version}</version>
</dependency>
<!-- 插件启动器 -->
<dependency>
<groupId>com.ruoyi.geekxd</groupId>
<artifactId>ruoyi-plugins-starter</artifactId>
<version>${ruoyi.version}</version>
</dependency>
<!-- 模块启动器 -->
<dependency>
<groupId>com.ruoyi.geekxd</groupId>
<artifactId>ruoyi-models-starter</artifactId>
<version>${ruoyi.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<modules>
<module>ruoyi-admin</module>
<module>ruoyi-framework</module>
<module>ruoyi-system</module>
<module>ruoyi-common</module>
<module>ruoyi-scene-auth</module>
<module>ruoyi-scene-pay</module>
<module>ruoyi-scene-file</module>
<module>ruoyi-plugins</module>
<module>ruoyi-models</module>
<module>ruoyi-models/ruoyi-labMeter</module>
</modules>
<packaging>pom</packaging>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
<configuration>
<mainClass>com.ruoyi.RuoYiApplication</mainClass>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
<configuration>
<parameters>true</parameters>
<source>${java.version}</source>
<target>${java.version}</target>
<encoding>${project.build.sourceEncoding}</encoding>
</configuration>
</plugin>
</plugins>
</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>

114
ruoyi-admin/pom.xml Normal file
View File

@ -0,0 +1,114 @@
<?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</artifactId>
<groupId>com.ruoyi.geekxd</groupId>
<version>3.9.0-G</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>
<artifactId>ruoyi-admin</artifactId>
<description>
web服务入口
</description>
<dependencies>
<!-- spring-boot-actuator -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- 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>
<!-- Pgsql驱动包 -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
</dependency>
<!-- knife4j -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
</dependency>
<!-- 核心模块-->
<dependency>
<groupId>com.ruoyi.geekxd</groupId>
<artifactId>ruoyi-framework</artifactId>
</dependency>
<!-- 集成第三方认证场景 -->
<dependency>
<groupId>com.ruoyi.geekxd</groupId>
<artifactId>ruoyi-auth-starter</artifactId>
</dependency>
<!-- 集成第三方支付场景 -->
<dependency>
<groupId>com.ruoyi.geekxd</groupId>
<artifactId>ruoyi-pay-starter</artifactId>
</dependency>
<!-- 集成文件管理场景 -->
<dependency>
<groupId>com.ruoyi.geekxd</groupId>
<artifactId>ruoyi-file-starter</artifactId>
</dependency>
<!-- 集成插件启动器 -->
<dependency>
<groupId>com.ruoyi.geekxd</groupId>
<artifactId>ruoyi-plugins-starter</artifactId>
</dependency>
<!-- 集成业务启动器 -->
<dependency>
<groupId>com.ruoyi.geekxd</groupId>
<artifactId>ruoyi-models-starter</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
<warName>${project.artifactId}</warName>
</configuration>
</plugin>
</plugins>
<finalName>${project.artifactId}</finalName>
</build>
</project>

View File

@ -0,0 +1,45 @@
package com.ruoyi;
import java.net.InetAddress;
import java.net.UnknownHostException;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.Environment;
/**
* 启动程序
*
* @author ruoyi
*/
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
public class RuoYiApplication {
public static void main(String[] args) throws UnknownHostException {
// System.setProperty("spring.devtools.restart.enabled", "false");
ConfigurableApplicationContext application = SpringApplication.run(RuoYiApplication.class, args);
System.out.println("(♥◠‿◠)ノ゙ 若依极客启动成功 ლ(´ڡ`ლ)゙ \n" +
" .-------. ____ __ \n" +
" | _ _ \\ \\ \\ / / \n" +
" | ( ' ) | \\ _. / ' \n" +
" |(_ o _) / _( )_ .' \n" +
" | (_,_).' __ ___(_ o _)' " + " ____ _ " + "\n" +
" | |\\ \\ | || |(_,_)' " + " / ___| ___ ___| | __ " + "\n" +
" | | \\ `' /| `-' / " + "| | _ / _ \\/ _ \\ |/ / " + "\n" +
" | | \\ / \\ / " + " | |_| | __/ __/ < " + "\n" +
" ''-' `'-' `-..-' " + "\\____|\\___|\\___|_|\\_\\");
Environment env = application.getEnvironment();
String ip = InetAddress.getLocalHost().getHostAddress();
String port = env.getProperty("server.port");
System.out.println("\n----------------------------------------------------------\n" +
" Application Ruoyi-Geek is running! Access URLs:\n" +
" Local: http://localhost:" + port + "/\n" +
" External: http://" + ip + ":" + port + "/\n" +
" Swagger文档: http://" + ip + ":" + port + "/swagger-ui/index.html\n" +
" Knife4j文档: http://" + ip + ":" + port + "/doc.html" + "" + "\n" +
"----------------------------------------------------------");
}
}

View File

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

View File

@ -0,0 +1,81 @@
package com.ruoyi.web.controller.common;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import javax.imageio.ImageIO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.FastByteArrayOutputStream;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import com.google.code.kaptcha.Producer;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.CacheUtils;
import com.ruoyi.common.utils.sign.Base64;
import com.ruoyi.common.utils.uuid.IdUtils;
import com.ruoyi.system.service.ISysConfigService;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
/**
* 验证码操作处理
*
* @author ruoyi
*/
@RestController
public class CaptchaController {
@Resource(name = "captchaProducer")
private Producer captchaProducer;
@Resource(name = "captchaProducerMath")
private Producer captchaProducerMath;
@Autowired
private ISysConfigService configService;
/**
* 生成验证码
*/
@GetMapping("/captchaImage")
public AjaxResult getCode(HttpServletResponse response) throws IOException {
AjaxResult ajax = AjaxResult.success();
boolean captchaEnabled = configService.selectCaptchaEnabled();
ajax.put("captchaEnabled", captchaEnabled);
if (!captchaEnabled) {
return ajax;
}
// 保存验证码信息
String uuid = IdUtils.simpleUUID(), capStr = null, code = null;
BufferedImage image = null;
// 生成验证码
String captchaType = RuoYiConfig.getCaptchaType();
if ("math".equals(captchaType)) {
String capText = captchaProducerMath.createText();
capStr = capText.substring(0, capText.lastIndexOf("@"));
code = capText.substring(capText.lastIndexOf("@") + 1);
image = captchaProducerMath.createImage(capStr);
} else if ("char".equals(captchaType)) {
capStr = code = captchaProducer.createText();
image = captchaProducer.createImage(capStr);
}
CacheUtils.put(CacheConstants.CAPTCHA_CODE_KEY, uuid, code, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES);
// 转换流信息写出
FastByteArrayOutputStream os = new FastByteArrayOutputStream();
try {
ImageIO.write(image, "jpg", os);
} catch (IOException e) {
return AjaxResult.error(e.getMessage());
}
ajax.put("uuid", uuid);
ajax.put("img", Base64.encode(os.toByteArray()));
return ajax;
}
}

View File

@ -0,0 +1,162 @@
package com.ruoyi.web.controller.common;
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import com.ruoyi.common.annotation.Anonymous;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.file.FileUtils;
import com.ruoyi.file.utils.FileOperateUtils;
import com.ruoyi.framework.config.ServerConfig;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.parameters.RequestBody;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
/**
* 通用请求处理
*
* @author ruoyi
*/
@Tag(name = "通用请求处理")
@RestController
@RequestMapping("/common")
public class CommonController {
private static final Logger log = LoggerFactory.getLogger(CommonController.class);
private static final String FILE_DELIMETER = ",";
@Autowired
private ServerConfig serverConfig;
/**
* 通用下载请求
*
* @param fileName 文件名称
* @param delete 是否删除
*/
@Operation(summary = "通用下载请求")
@Parameters({
@Parameter(name = "fileName", description = "文件名称"),
@Parameter(name = "delete", description = "是否删除")
})
@GetMapping("/download")
@Anonymous
public void fileDownload(
@RequestParam("fileName") String fileName,
@RequestParam("delete") Boolean delete,
HttpServletResponse response,
HttpServletRequest request) {
try {
if (!FileUtils.checkAllowDownload(fileName)) {
throw new Exception(StringUtils.format("文件名称({})非法,不允许下载。 ", fileName));
}
String realFileName = System.currentTimeMillis() + fileName.substring(fileName.indexOf("_") + 1);
String filePath = RuoYiConfig.getDownloadPath() + fileName;
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
FileUtils.setAttachmentResponseHeader(response, realFileName);
// FileUtils.writeBytes(filePath, response.getOutputStream());
FileOperateUtils.downLoad(filePath, response.getOutputStream());
if (delete) {
FileOperateUtils.deleteFile(fileName);
}
} catch (Exception e) {
log.error("下载文件失败", e);
}
}
/**
* 通用上传请求单个
*/
@Operation(summary = "通用上传请求(单个)")
@PostMapping("/upload")
@Anonymous
public AjaxResult uploadFile(@RequestBody MultipartFile file) throws Exception {
try {
String url = FileOperateUtils.upload(file);
AjaxResult ajax = AjaxResult.success();
ajax.put("url", url);
ajax.put("fileName", file.getOriginalFilename());
ajax.put("newFileName", FileUtils.getName(file.getOriginalFilename()));
ajax.put("originalFilename", file.getOriginalFilename());
return ajax;
} catch (Exception e) {
return AjaxResult.error(e.getMessage());
}
}
/**
* 通用上传请求多个
*/
@Operation(summary = "通用上传请求(多个)")
@PostMapping("/uploads")
@Anonymous
public AjaxResult uploadFiles(@RequestBody List<MultipartFile> files)
throws Exception {
try {
// 上传文件路径
String filePath = RuoYiConfig.getUploadPath();
List<String> urls = new ArrayList<String>();
List<String> fileNames = new ArrayList<String>();
List<String> newFileNames = new ArrayList<String>();
List<String> originalFilenames = new ArrayList<String>();
for (MultipartFile file : files) {
// 上传并返回新文件名称
String fileName = FileOperateUtils.upload(filePath, file);
String url = serverConfig.getUrl() + fileName;
urls.add(url);
fileNames.add(fileName);
newFileNames.add(FileUtils.getName(fileName));
originalFilenames.add(file.getOriginalFilename());
}
AjaxResult ajax = AjaxResult.success();
ajax.put("urls", StringUtils.join(urls, FILE_DELIMETER));
ajax.put("fileNames", StringUtils.join(fileNames, FILE_DELIMETER));
ajax.put("newFileNames", StringUtils.join(newFileNames, FILE_DELIMETER));
ajax.put("originalFilenames", StringUtils.join(originalFilenames, FILE_DELIMETER));
return ajax;
} catch (Exception e) {
return AjaxResult.error(e.getMessage());
}
}
/**
* 本地资源通用下载
*/
@Operation(summary = "本地资源通用下载")
@GetMapping("/download/resource")
@Anonymous
public void resourceDownload(@Parameter(name = "resource", description = "资源名称") String resource,
HttpServletRequest request, HttpServletResponse response)
throws Exception {
try {
if (!FileUtils.checkAllowDownload(resource)) {
throw new Exception(StringUtils.format("资源文件({})非法,不允许下载。 ", resource));
}
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
FileUtils.setAttachmentResponseHeader(response, resource);
FileOperateUtils.downLoad(resource, response.getOutputStream());
} catch (Exception e) {
log.error("下载文件失败", e);
}
}
}

View File

@ -0,0 +1,122 @@
package com.ruoyi.web.controller.monitor;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import org.springframework.cache.Cache.ValueWrapper;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.text.Convert;
import com.ruoyi.common.utils.CacheUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.domain.SysCache;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.tags.Tag;
/**
* 缓存监控
*
* @author ruoyi
*/
@Tag(name = "缓存监控")
@RestController
@RequestMapping("/monitor/cache")
public class CacheController {
private static String tmpCacheName = "";
private final static List<SysCache> caches = new ArrayList<SysCache>();
{
caches.add(new SysCache(CacheConstants.LOGIN_TOKEN_KEY, "用户信息"));
caches.add(new SysCache(CacheConstants.SYS_CONFIG_KEY, "配置信息"));
caches.add(new SysCache(CacheConstants.SYS_DICT_KEY, "数据字典"));
caches.add(new SysCache(CacheConstants.CAPTCHA_CODE_KEY, "验证码"));
caches.add(new SysCache(CacheConstants.PHONE_CODES, "短信验证码"));
caches.add(new SysCache(CacheConstants.EMAIL_CODES, "邮箱验证码"));
caches.add(new SysCache(CacheConstants.REPEAT_SUBMIT_KEY, "防重提交"));
caches.add(new SysCache(CacheConstants.RATE_LIMIT_KEY, "限流处理"));
caches.add(new SysCache(CacheConstants.PWD_ERR_CNT_KEY, "密码错误次数"));
caches.add(new SysCache(CacheConstants.IP_ERR_CNT_KEY, "IP错误次数"));
caches.add(new SysCache(CacheConstants.FILE_MD5_PATH_KEY, "path-md5"));
caches.add(new SysCache(CacheConstants.FILE_PATH_MD5_KEY, "md5-path"));
}
@Operation(summary = "获取缓存名列表")
@PreAuthorize("@ss.hasPermi('monitor:cache:list')")
@GetMapping("/getNames")
public AjaxResult cache() {
return AjaxResult.success(caches);
}
@Operation(summary = "获取缓存键列表")
@Parameters({
@Parameter(name = "cacheName", description = "缓存名称", required = true),
})
@PreAuthorize("@ss.hasPermi('monitor:cache:list')")
@GetMapping("/getKeys/{cacheName}")
public AjaxResult getCacheKeys(@PathVariable String cacheName) {
tmpCacheName = cacheName;
Set<String> keyset = CacheUtils.getkeys(cacheName);
return AjaxResult.success(keyset);
}
@Operation(summary = "获取缓存值列表")
@Parameters({
@Parameter(name = "cacheName", description = "缓存名称", required = true),
@Parameter(name = "cacheKey", description = "缓存键名", required = true)
})
@PreAuthorize("@ss.hasPermi('monitor:cache:list')")
@GetMapping("/getValue/{cacheName}/{cacheKey}")
public AjaxResult getCacheValue(@PathVariable String cacheName, @PathVariable String cacheKey) {
ValueWrapper valueWrapper = CacheUtils.get(cacheName, cacheKey);
SysCache sysCache = new SysCache();
sysCache.setCacheName(cacheName);
sysCache.setCacheKey(cacheKey);
if (StringUtils.isNotNull(valueWrapper)) {
sysCache.setCacheValue(Convert.toStr(valueWrapper.get(), ""));
}
return AjaxResult.success(sysCache);
}
@Operation(summary = "清除缓存")
@Parameters({
@Parameter(name = "cacheName", description = "缓存名称", required = true)
})
@PreAuthorize("@ss.hasPermi('monitor:cache:list')")
@DeleteMapping("/clearCacheName/{cacheName}")
public AjaxResult clearCacheName(@PathVariable String cacheName) {
CacheUtils.clear(cacheName);
return AjaxResult.success();
}
@Operation(summary = "清除缓存值")
@Parameters({
@Parameter(name = "cacheKey", description = "缓存键名", required = true)
})
@PreAuthorize("@ss.hasPermi('monitor:cache:list')")
@DeleteMapping("/clearCacheKey/{cacheKey}")
public AjaxResult clearCacheKey(@PathVariable String cacheKey) {
CacheUtils.removeIfPresent(tmpCacheName, cacheKey);
return AjaxResult.success();
}
@PreAuthorize("@ss.hasPermi('monitor:cache:list')")
@DeleteMapping("/clearCacheAll")
public AjaxResult clearCacheAll() {
for (String cacheName : CacheUtils.getCacheManager().getCacheNames()) {
CacheUtils.clear(cacheName);
}
return AjaxResult.success();
}
}

View File

@ -0,0 +1,32 @@
package com.ruoyi.web.controller.monitor;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.framework.web.domain.Server;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
/**
* 服务器监控
*
* @author ruoyi
*/
@Tag(name = "服务器监控")
@RestController
@RequestMapping("/monitor/server")
public class ServerController {
@Operation(summary = "获取服务器监控信息")
@PreAuthorize("@ss.hasPermi('monitor:server:list')")
@GetMapping()
public AjaxResult getInfo() throws Exception {
Server server = new Server();
server.copyTo();
return AjaxResult.success(server);
}
}

View File

@ -0,0 +1,96 @@
package com.ruoyi.web.controller.monitor;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.framework.web.service.SysPasswordService;
import com.ruoyi.system.domain.SysLogininfor;
import com.ruoyi.system.service.ISysLogininforService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletResponse;
/**
* 系统访问记录
*
* @author ruoyi
*/
@Tag(name = "系统访问记录")
@RestController
@RequestMapping("/monitor/logininfor")
public class SysLogininforController extends BaseController {
@Autowired
private ISysLogininforService logininforService;
@Autowired
private SysPasswordService passwordService;
@Operation(summary = "获取系统访问记录列表")
@PreAuthorize("@ss.hasPermi('monitor:logininfor:list')")
@GetMapping("/list")
public TableDataInfo list(SysLogininfor logininfor) {
startPage();
List<SysLogininfor> list = logininforService.selectLogininforList(logininfor);
return getDataTable(list);
}
@Operation(summary = "导出系统访问记录列表")
@Log(title = "登录日志", businessType = BusinessType.EXPORT)
@PreAuthorize("@ss.hasPermi('monitor:logininfor:export')")
@PostMapping("/export")
public void export(HttpServletResponse response, SysLogininfor logininfor) {
List<SysLogininfor> list = logininforService.selectLogininforList(logininfor);
ExcelUtil<SysLogininfor> util = new ExcelUtil<SysLogininfor>(SysLogininfor.class);
util.exportExcel(response, list, "登录日志");
}
@Operation(summary = "删除系统访问记录")
@Parameters({
@Parameter(name = "infoIds", description = "记录id数组", required = true),
})
@PreAuthorize("@ss.hasPermi('monitor:logininfor:remove')")
@Log(title = "登录日志", businessType = BusinessType.DELETE)
@DeleteMapping("/{infoIds}")
public AjaxResult remove(@PathVariable(name = "infoIds") Long[] infoIds) {
return toAjax(logininforService.deleteLogininforByIds(infoIds));
}
@Operation(summary = "清除系统访问记录")
@PreAuthorize("@ss.hasPermi('monitor:logininfor:remove')")
@Log(title = "登录日志", businessType = BusinessType.CLEAN)
@DeleteMapping("/clean")
public AjaxResult clean() {
logininforService.cleanLogininfor();
return success();
}
@Operation(summary = "账户解锁")
@Parameters({
@Parameter(name = "userName", description = "用户名", required = true),
})
@PreAuthorize("@ss.hasPermi('monitor:logininfor:unlock')")
@Log(title = "账户解锁", businessType = BusinessType.OTHER)
@GetMapping("/unlock/{userName}")
public AjaxResult unlock(@PathVariable("userName") String userName) {
passwordService.clearLoginRecordCache(userName);
return success();
}
}

View File

@ -0,0 +1,102 @@
package com.ruoyi.web.controller.monitor;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
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 io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletResponse;
/**
* 操作日志记录
*
* @author ruoyi
*/
@Tag(name = "操作日志记录")
@RestController
@RequestMapping("/monitor/operlog")
public class SysOperlogController extends BaseController {
@Autowired
private ISysOperLogService operLogService;
@Operation(summary = "获取操作日志记录列表")
@PreAuthorize("@ss.hasPermi('monitor:operlog:list')")
@GetMapping("/list")
public TableDataInfo list(SysOperLog operLog) {
startPage();
List<SysOperLog> list = operLogService.selectOperLogList(operLog);
return getDataTable(list);
}
@Operation(summary = "导出操作日志记录列表")
@Log(title = "操作日志", businessType = BusinessType.EXPORT)
@PreAuthorize("@ss.hasPermi('monitor:operlog:export')")
@PostMapping("/export")
public void export(HttpServletResponse response, SysOperLog operLog) {
List<SysOperLog> list = operLogService.selectOperLogList(operLog);
ExcelUtil<SysOperLog> util = new ExcelUtil<SysOperLog>(SysOperLog.class);
util.exportExcel(response, list, "操作日志");
}
@Operation(summary = "删除操作日志记录")
@Parameters({
@Parameter(name = "operIds", description = "记录id数组", required = true),
})
@Log(title = "操作日志", businessType = BusinessType.DELETE)
@PreAuthorize("@ss.hasPermi('monitor:operlog:remove')")
@DeleteMapping("/{operIds}")
public AjaxResult remove(@PathVariable(name = "operIds") Long[] operIds) {
return toAjax(operLogService.deleteOperLogByIds(operIds));
}
@Operation(summary = "清除操作日志记录")
@Log(title = "操作日志", businessType = BusinessType.CLEAN)
@PreAuthorize("@ss.hasPermi('monitor:operlog:remove')")
@DeleteMapping("/clean")
public AjaxResult clean() {
operLogService.cleanOperLog();
return success();
}
@Operation(summary = "业务监控")
@GetMapping("/business")
public R<Map<String, Object>> business(SysOperLog operLog) {
// 查询并获取统计数据
List<Map<String, Object>> successStats = operLogService.getSuccessOperationStats(operLog);
List<Map<String, Object>> failureStats = operLogService.getFailureOperationStats(operLog);
List<Map<String, Object>> statusStats = operLogService.getStatusStats(operLog);
List<Map<String, Object>> moduleOperationStats = operLogService.getModuleOperationStats(operLog);
// 创建一个新的 Map 来组织数据
Map<String, Object> result = new LinkedHashMap<>();
result.put("successStats", successStats);
result.put("failureStats", failureStats);
result.put("statusStats", statusStats);
result.put("moduleOperationStats", moduleOperationStats);
result.put("total",
successStats.size() + failureStats.size() + statusStats.size() + moduleOperationStats.size());
return R.ok(result);
}
}

View File

@ -0,0 +1,72 @@
package com.ruoyi.web.controller.monitor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
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.AjaxResult;
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.utils.CacheUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.domain.SysUserOnline;
import com.ruoyi.system.service.ISysUserOnlineService;
/**
* 在线用户监控
*
* @author ruoyi
*/
@RestController
@RequestMapping("/monitor/online")
public class SysUserOnlineController extends BaseController {
@Autowired
private ISysUserOnlineService userOnlineService;
@PreAuthorize("@ss.hasPermi('monitor:online:list')")
@GetMapping("/list")
public TableDataInfo list(String ipaddr, String userName) {
Collection<String> keys = CacheUtils.getkeys(CacheConstants.LOGIN_TOKEN_KEY);
List<SysUserOnline> userOnlineList = new ArrayList<SysUserOnline>();
for (String key : keys) {
LoginUser user = CacheUtils.get(CacheConstants.LOGIN_TOKEN_KEY, key, LoginUser.class);
if (StringUtils.isNotEmpty(ipaddr) && StringUtils.isNotEmpty(userName)) {
userOnlineList.add(userOnlineService.selectOnlineByInfo(ipaddr, userName, user));
} else if (StringUtils.isNotEmpty(ipaddr)) {
userOnlineList.add(userOnlineService.selectOnlineByIpaddr(ipaddr, user));
} else if (StringUtils.isNotEmpty(userName) && StringUtils.isNotNull(user.getUser())) {
userOnlineList.add(userOnlineService.selectOnlineByUserName(userName, user));
} else {
userOnlineList.add(userOnlineService.loginUserToUserOnline(user));
}
}
Collections.reverse(userOnlineList);
userOnlineList.removeAll(Collections.singleton(null));
return getDataTable(userOnlineList);
}
/**
* 强退用户
*/
@PreAuthorize("@ss.hasPermi('monitor:online:forceLogout')")
@Log(title = "在线用户", businessType = BusinessType.FORCE)
@DeleteMapping("/{tokenId}")
public AjaxResult forceLogout(@PathVariable String tokenId) {
CacheUtils.removeIfPresent(CacheConstants.LOGIN_TOKEN_KEY, tokenId);
return success();
}
}

View File

@ -0,0 +1,139 @@
package com.ruoyi.web.controller.system;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.annotation.Anonymous;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
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 io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletResponse;
/**
* 参数配置 信息操作处理
*
* @author ruoyi
*/
@Tag(name = "参数配置")
@RestController
@RequestMapping("/system/config")
public class SysConfigController extends BaseController {
@Autowired
private ISysConfigService configService;
/**
* 获取参数配置列表
*/
@Operation(summary = "获取参数配置列表")
@PreAuthorize("@ss.hasPermi('system:config:list')")
@GetMapping("/list")
public TableDataInfo list(SysConfig config) {
startPage();
List<SysConfig> list = configService.selectConfigList(config);
return getDataTable(list);
}
@Operation(summary = "参数管理")
@Log(title = "参数管理", businessType = BusinessType.EXPORT)
@PreAuthorize("@ss.hasPermi('system:config:export')")
@PostMapping("/export")
public void export(HttpServletResponse response, SysConfig config) {
List<SysConfig> list = configService.selectConfigList(config);
ExcelUtil<SysConfig> util = new ExcelUtil<SysConfig>(SysConfig.class);
util.exportExcel(response, list, "参数数据");
}
/**
* 根据参数编号获取详细信息
*/
@Operation(summary = "根据参数编号获取详细信息")
@PreAuthorize("@ss.hasPermi('system:config:query')")
@GetMapping(value = "/{configId}")
public AjaxResult getInfo(@PathVariable(name = "configId") Long configId) {
return success(configService.selectConfigById(configId));
}
/**
* 根据参数键名查询参数值
*/
@Operation(summary = "根据参数键名查询参数值")
@GetMapping(value = "/configKey/{configKey}")
@Anonymous
public AjaxResult getConfigKey(@PathVariable(name = "configKey") String configKey) {
return success(configService.selectConfigByKey(configKey));
}
/**
* 新增参数配置
*/
@Operation(summary = "新增参数配置")
@PreAuthorize("@ss.hasPermi('system:config:add')")
@Log(title = "参数管理", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@Validated @RequestBody SysConfig config) {
if (!configService.checkConfigKeyUnique(config)) {
return error("新增参数'" + config.getConfigName() + "'失败,参数键名已存在");
}
config.setCreateBy(getUsername());
return toAjax(configService.insertConfig(config));
}
/**
* 修改参数配置
*/
@Operation(summary = "修改参数配置")
@PreAuthorize("@ss.hasPermi('system:config:edit')")
@Log(title = "参数管理", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@Validated @RequestBody SysConfig config) {
if (!configService.checkConfigKeyUnique(config)) {
return error("修改参数'" + config.getConfigName() + "'失败,参数键名已存在");
}
config.setUpdateBy(getUsername());
return toAjax(configService.updateConfig(config));
}
/**
* 删除参数配置
*/
@Operation(summary = "删除参数配置")
@PreAuthorize("@ss.hasPermi('system:config:remove')")
@Log(title = "参数管理", businessType = BusinessType.DELETE)
@DeleteMapping("/{configIds}")
public AjaxResult remove(@PathVariable(name = "configIds") Long[] configIds) {
configService.deleteConfigByIds(configIds);
return success();
}
/**
* 刷新参数缓存
*/
@Operation(summary = "刷新参数缓存")
@PreAuthorize("@ss.hasPermi('system:config:remove')")
@Log(title = "参数管理", businessType = BusinessType.CLEAN)
@DeleteMapping("/refreshCache")
public AjaxResult refreshCache() {
configService.resetConfigCache();
return success();
}
}

View File

@ -0,0 +1,132 @@
package com.ruoyi.web.controller.system;
import java.util.List;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
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.AjaxResult;
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 io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
/**
* 部门信息
*
* @author ruoyi
*/
@Tag(name = "部门信息")
@RestController
@RequestMapping("/system/dept")
public class SysDeptController extends BaseController {
@Autowired
private ISysDeptService deptService;
/**
* 获取部门列表
*/
@Operation(summary = "获取部门列表")
@PreAuthorize("@ss.hasPermi('system:dept:list')")
@GetMapping("/list")
public AjaxResult list(SysDept dept) {
List<SysDept> depts = deptService.selectDeptList(dept);
return success(depts);
}
/**
* 查询部门列表排除节点
*/
@Operation(summary = "查询部门列表", description = "排除节点")
@PreAuthorize("@ss.hasPermi('system:dept:list')")
@GetMapping("/list/exclude/{deptId}")
public AjaxResult excludeChild(@PathVariable(value = "deptId", required = false) Long deptId) {
List<SysDept> depts = deptService.selectDeptList(new SysDept());
depts.removeIf(d -> d.getDeptId().intValue() == deptId
|| ArrayUtils.contains(StringUtils.split(d.getAncestors(), ","), deptId + ""));
return success(depts);
}
/**
* 根据部门编号获取详细信息
*/
@Operation(summary = "根据部门编号获取详细信息")
@PreAuthorize("@ss.hasPermi('system:dept:query')")
@GetMapping(value = "/{deptId}")
public AjaxResult getInfo(@PathVariable(name = "deptId") Long deptId) {
deptService.checkDeptDataScope(deptId);
return success(deptService.selectDeptById(deptId));
}
/**
* 新增部门
*/
@Operation(summary = "新增部门")
@PreAuthorize("@ss.hasPermi('system:dept:add')")
@Log(title = "部门管理", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@Validated @RequestBody SysDept dept) {
if (!deptService.checkDeptNameUnique(dept)) {
return error("新增部门'" + dept.getDeptName() + "'失败,部门名称已存在");
}
dept.setCreateBy(getUsername());
return toAjax(deptService.insertDept(dept));
}
/**
* 修改部门
*/
@Operation(summary = "修改部门")
@PreAuthorize("@ss.hasPermi('system:dept:edit')")
@Log(title = "部门管理", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@Validated @RequestBody SysDept dept) {
Long deptId = dept.getDeptId();
deptService.checkDeptDataScope(deptId);
if (!deptService.checkDeptNameUnique(dept)) {
return error("修改部门'" + dept.getDeptName() + "'失败,部门名称已存在");
} else if (dept.getParentId().equals(deptId)) {
return error("修改部门'" + dept.getDeptName() + "'失败,上级部门不能是自己");
} else if (StringUtils.equals(UserConstants.DEPT_DISABLE, dept.getStatus())
&& deptService.selectNormalChildrenDeptById(deptId) > 0) {
return error("该部门包含未停用的子部门!");
}
dept.setUpdateBy(getUsername());
return toAjax(deptService.updateDept(dept));
}
/**
* 删除部门
*/
@Operation(summary = "删除部门")
@PreAuthorize("@ss.hasPermi('system:dept:remove')")
@Log(title = "部门管理", businessType = BusinessType.DELETE)
@DeleteMapping("/{deptId}")
public AjaxResult remove(@PathVariable(name = "deptId") Long deptId) {
if (deptService.hasChildByDeptId(deptId)) {
return warn("存在下级部门,不允许删除");
}
if (deptService.checkDeptExistUser(deptId)) {
return warn("部门存在用户,不允许删除");
}
deptService.checkDeptDataScope(deptId);
return toAjax(deptService.deleteDeptById(deptId));
}
}

View File

@ -0,0 +1,126 @@
package com.ruoyi.web.controller.system;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
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.StringUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.system.service.ISysDictDataService;
import com.ruoyi.system.service.ISysDictTypeService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletResponse;
/**
* 数据字典信息
*
* @author ruoyi
*/
@Tag(name = "数据字典信息(数据)")
@RestController
@RequestMapping("/system/dict/data")
public class SysDictDataController extends BaseController {
@Autowired
private ISysDictDataService dictDataService;
@Autowired
private ISysDictTypeService dictTypeService;
@Operation(summary = "查询字典数据列表")
@PreAuthorize("@ss.hasPermi('system:dict:list')")
@GetMapping("/list")
public TableDataInfo list(SysDictData dictData) {
startPage();
List<SysDictData> list = dictDataService.selectDictDataList(dictData);
return getDataTable(list);
}
@Operation(summary = "导出字典数据列表")
@Log(title = "字典数据", businessType = BusinessType.EXPORT)
@PreAuthorize("@ss.hasPermi('system:dict:export')")
@PostMapping("/export")
public void export(HttpServletResponse response, SysDictData dictData) {
List<SysDictData> list = dictDataService.selectDictDataList(dictData);
ExcelUtil<SysDictData> util = new ExcelUtil<SysDictData>(SysDictData.class);
util.exportExcel(response, list, "字典数据");
}
/**
* 查询字典数据详细
*/
@Operation(summary = "查询字典数据详细")
@PreAuthorize("@ss.hasPermi('system:dict:query')")
@GetMapping(value = "/{dictCode}")
public AjaxResult getInfo(@PathVariable(name = "dictCode") Long dictCode) {
return success(dictDataService.selectDictDataById(dictCode));
}
/**
* 根据字典类型查询字典数据信息
*/
@Operation(summary = "根据字典类型查询字典数据信息")
@GetMapping(value = "/type/{dictType}")
public AjaxResult dictType(@PathVariable(name = "dictType") String dictType) {
List<SysDictData> data = dictTypeService.selectDictDataByType(dictType);
if (StringUtils.isNull(data)) {
data = new ArrayList<SysDictData>();
}
return success(data);
}
/**
* 新增字典类型
*/
@Operation(summary = "新增字典类型")
@PreAuthorize("@ss.hasPermi('system:dict:add')")
@Log(title = "字典数据", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@Validated @RequestBody SysDictData dict) {
dict.setCreateBy(getUsername());
return toAjax(dictDataService.insertDictData(dict));
}
/**
* 修改保存字典类型
*/
@Operation(summary = "修改保存字典类型")
@PreAuthorize("@ss.hasPermi('system:dict:edit')")
@Log(title = "字典数据", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@Validated @RequestBody SysDictData dict) {
dict.setUpdateBy(getUsername());
return toAjax(dictDataService.updateDictData(dict));
}
/**
* 删除字典类型
*/
@Operation(summary = "删除字典类型")
@PreAuthorize("@ss.hasPermi('system:dict:remove')")
@Log(title = "字典类型", businessType = BusinessType.DELETE)
@DeleteMapping("/{dictCodes}")
public AjaxResult remove(@PathVariable(name = "dictCodes") Long[] dictCodes) {
dictDataService.deleteDictDataByIds(dictCodes);
return success();
}
}

View File

@ -0,0 +1,135 @@
package com.ruoyi.web.controller.system;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
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 io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletResponse;
/**
* 数据字典信息
*
* @author ruoyi
*/
@Tag(name = "数据字典信息(类型)")
@RestController
@RequestMapping("/system/dict/type")
public class SysDictTypeController extends BaseController {
@Autowired
private ISysDictTypeService dictTypeService;
@Operation(summary = "查询字典类型列表")
@PreAuthorize("@ss.hasPermi('system:dict:list')")
@GetMapping("/list")
public TableDataInfo list(SysDictType dictType) {
startPage();
List<SysDictType> list = dictTypeService.selectDictTypeList(dictType);
return getDataTable(list);
}
@Operation(summary = "导出字典类型列表")
@Log(title = "字典类型", businessType = BusinessType.EXPORT)
@PreAuthorize("@ss.hasPermi('system:dict:export')")
@PostMapping("/export")
public void export(HttpServletResponse response, SysDictType dictType) {
List<SysDictType> list = dictTypeService.selectDictTypeList(dictType);
ExcelUtil<SysDictType> util = new ExcelUtil<SysDictType>(SysDictType.class);
util.exportExcel(response, list, "字典类型");
}
/**
* 查询字典类型详细
*/
@Operation(summary = "查询字典类型详细")
@PreAuthorize("@ss.hasPermi('system:dict:query')")
@GetMapping(value = "/{dictId}")
public AjaxResult getInfo(@PathVariable(name = "dictId") Long dictId) {
return success(dictTypeService.selectDictTypeById(dictId));
}
/**
* 新增字典类型
*/
@Operation(summary = "新增字典类型")
@PreAuthorize("@ss.hasPermi('system:dict:add')")
@Log(title = "字典类型", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@Validated @RequestBody SysDictType dict) {
if (!dictTypeService.checkDictTypeUnique(dict)) {
return error("新增字典'" + dict.getDictName() + "'失败,字典类型已存在");
}
dict.setCreateBy(getUsername());
return toAjax(dictTypeService.insertDictType(dict));
}
/**
* 修改字典类型
*/
@Operation(summary = "修改字典类型")
@PreAuthorize("@ss.hasPermi('system:dict:edit')")
@Log(title = "字典类型", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@Validated @RequestBody SysDictType dict) {
if (!dictTypeService.checkDictTypeUnique(dict)) {
return error("修改字典'" + dict.getDictName() + "'失败,字典类型已存在");
}
dict.setUpdateBy(getUsername());
return toAjax(dictTypeService.updateDictType(dict));
}
/**
* 删除字典类型
*/
@Operation(summary = "删除字典类型")
@PreAuthorize("@ss.hasPermi('system:dict:remove')")
@Log(title = "字典类型", businessType = BusinessType.DELETE)
@DeleteMapping("/{dictIds}")
public AjaxResult remove(@PathVariable(name = "dictIds") Long[] dictIds) {
dictTypeService.deleteDictTypeByIds(dictIds);
return success();
}
/**
* 刷新字典缓存
*/
@Operation(summary = "刷新字典缓存")
@PreAuthorize("@ss.hasPermi('system:dict:remove')")
@Log(title = "字典类型", businessType = BusinessType.CLEAN)
@DeleteMapping("/refreshCache")
public AjaxResult refreshCache() {
dictTypeService.resetDictCache();
return success();
}
/**
* 获取字典选择框列表
*/
@Operation(summary = "获取字典选择框列表")
@GetMapping("/optionselect")
public AjaxResult optionselect() {
List<SysDictType> dictTypes = dictTypeService.selectDictTypeAll();
return success(dictTypes);
}
}

View File

@ -0,0 +1,34 @@
package com.ruoyi.web.controller.system;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.utils.StringUtils;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
/**
* 首页
*
* @author ruoyi
*/
@Tag(name = "首页")
@RestController
public class SysIndexController {
/** 系统基础配置 */
@Autowired
private RuoYiConfig ruoyiConfig;
/**
* 访问首页提示语
*/
@Operation(summary = "访问首页", description = "提示语")
@RequestMapping("/")
public String index() {
return StringUtils.format("欢迎使用{}后台管理框架当前版本v{},请通过前端地址访问。", ruoyiConfig.getName(), ruoyiConfig.getVersion());
}
}

View File

@ -0,0 +1,143 @@
package com.ruoyi.web.controller.system;
import java.util.Date;
import java.util.List;
import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired;
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 com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.SysMenu;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.LoginBody;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.core.text.Convert;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.file.utils.FileOperateUtils;
import com.ruoyi.framework.web.service.SysLoginService;
import com.ruoyi.framework.web.service.SysPermissionService;
import com.ruoyi.framework.web.service.TokenService;
import com.ruoyi.system.service.ISysConfigService;
import com.ruoyi.system.service.ISysMenuService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
/**
* 登录验证
*
* @author ruoyi
*/
@Tag(name = "登录验证")
@RestController
public class SysLoginController {
@Autowired
private SysLoginService loginService;
@Autowired
private ISysMenuService menuService;
@Autowired
private SysPermissionService permissionService;
@Autowired
private TokenService tokenService;
@Autowired
private ISysConfigService configService;
/**
* 登录方法
*
* @param loginBody 登录信息
* @return 结果
*/
@Operation(summary = "登录方法")
@PostMapping("/login")
public AjaxResult login(@RequestBody LoginBody loginBody) {
AjaxResult ajax = AjaxResult.success();
// 生成令牌
String token = loginService.login(
loginBody.getUsername(),
loginBody.getPassword(),
loginBody.getCode(),
loginBody.getUuid());
ajax.put(Constants.TOKEN, token);
return ajax;
}
/**
* 获取用户信息
*
* @return 用户信息
*/
@Operation(summary = "获取用户信息")
@GetMapping("getInfo")
public AjaxResult getInfo() {
LoginUser loginUser = SecurityUtils.getLoginUser();
SysUser user = loginUser.getUser();
// 角色集合
Set<String> roles = permissionService.getRolePermission(user);
// 权限集合
Set<String> permissions = permissionService.getMenuPermission(user);
if (!loginUser.getPermissions().equals(permissions)) {
loginUser.setPermissions(permissions);
tokenService.refreshToken(loginUser);
}
if (user.getAvatar() != null) {
try {
user.setAvatar(FileOperateUtils.getURL(user.getAvatar()));
} catch (Exception e) {
}
}
AjaxResult ajax = AjaxResult.success();
ajax.put("user", user);
ajax.put("roles", roles);
ajax.put("permissions", permissions);
ajax.put("isDefaultModifyPwd", initPasswordIsModify(user.getPwdUpdateDate()));
ajax.put("isPasswordExpired", passwordIsExpiration(user.getPwdUpdateDate()));
return ajax;
}
// 检查初始密码是否提醒修改
public boolean initPasswordIsModify(Date pwdUpdateDate) {
Integer initPasswordModify = Convert.toInt(configService.selectConfigByKey("sys.account.initPasswordModify"));
return initPasswordModify != null && initPasswordModify == 1 && pwdUpdateDate == null;
}
// 检查密码是否过期
public boolean passwordIsExpiration(Date pwdUpdateDate) {
Integer passwordValidateDays = Convert
.toInt(configService.selectConfigByKey("sys.account.passwordValidateDays"));
if (passwordValidateDays != null && passwordValidateDays > 0) {
if (StringUtils.isNull(pwdUpdateDate)) {
// 如果从未修改过初始密码直接提醒过期
return true;
}
Date nowDate = DateUtils.getNowDate();
return DateUtils.differentDaysByMillisecond(nowDate, pwdUpdateDate) > passwordValidateDays;
}
return false;
}
/**
* 获取路由信息
*
* @return 路由信息
*/
@Operation(summary = "获取路由信息")
@GetMapping("getRouters")
public AjaxResult getRouters() {
Long userId = SecurityUtils.getUserId();
List<SysMenu> menus = menuService.selectMenuTreeByUserId(userId);
return AjaxResult.success(menuService.buildMenus(menus));
}
}

View File

@ -0,0 +1,138 @@
package com.ruoyi.web.controller.system;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
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.AjaxResult;
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 io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
/**
* 菜单信息
*
* @author ruoyi
*/
@Tag(name = "菜单信息")
@RestController
@RequestMapping("/system/menu")
public class SysMenuController extends BaseController {
@Autowired
private ISysMenuService menuService;
/**
* 获取菜单列表
*/
@Operation(summary = "获取菜单列表")
@PreAuthorize("@ss.hasPermi('system:menu:list')")
@GetMapping("/list")
public AjaxResult list(SysMenu menu) {
List<SysMenu> menus = menuService.selectMenuList(menu, getUserId());
return success(menus);
}
/**
* 根据菜单编号获取详细信息
*/
@Operation(summary = "根据菜单编号获取详细信息")
@PreAuthorize("@ss.hasPermi('system:menu:query')")
@GetMapping(value = "/{menuId}")
public AjaxResult getInfo(@PathVariable(name = "menuId") Long menuId) {
return success(menuService.selectMenuById(menuId));
}
/**
* 获取菜单下拉树列表
*/
@Operation(summary = "获取菜单下拉树列表")
@GetMapping("/treeselect")
public AjaxResult treeselect(SysMenu menu) {
List<SysMenu> menus = menuService.selectMenuList(menu, getUserId());
return success(menuService.buildMenuTreeSelect(menus));
}
/**
* 加载对应角色菜单列表树
*/
@Operation(summary = "加载对应角色菜单列表树")
@GetMapping(value = "/roleMenuTreeselect/{roleId}")
public AjaxResult roleMenuTreeselect(@PathVariable("roleId") Long roleId) {
List<SysMenu> menus = menuService.selectMenuList(getUserId());
AjaxResult ajax = AjaxResult.success();
ajax.put("checkedKeys", menuService.selectMenuListByRoleId(roleId));
ajax.put("menus", menuService.buildMenuTreeSelect(menus));
return ajax;
}
/**
* 新增菜单
*/
@Operation(summary = "新增菜单")
@PreAuthorize("@ss.hasPermi('system:menu:add')")
@Log(title = "菜单管理", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@Validated @RequestBody SysMenu menu) {
if (!menuService.checkMenuNameUnique(menu)) {
return error("新增菜单'" + menu.getMenuName() + "'失败,菜单名称已存在");
} else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath())) {
return error("新增菜单'" + menu.getMenuName() + "'失败地址必须以http(s)://开头");
}
menu.setCreateBy(getUsername());
return toAjax(menuService.insertMenu(menu));
}
/**
* 修改菜单
*/
@Operation(summary = "修改菜单")
@PreAuthorize("@ss.hasPermi('system:menu:edit')")
@Log(title = "菜单管理", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@Validated @RequestBody SysMenu menu) {
if (!menuService.checkMenuNameUnique(menu)) {
return error("修改菜单'" + menu.getMenuName() + "'失败,菜单名称已存在");
} else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath())) {
return error("修改菜单'" + menu.getMenuName() + "'失败地址必须以http(s)://开头");
} else if (menu.getMenuId().equals(menu.getParentId())) {
return error("修改菜单'" + menu.getMenuName() + "'失败,上级菜单不能选择自己");
}
menu.setUpdateBy(getUsername());
return toAjax(menuService.updateMenu(menu));
}
/**
* 删除菜单
*/
@Operation(summary = "删除菜单")
@PreAuthorize("@ss.hasPermi('system:menu:remove')")
@Log(title = "菜单管理", businessType = BusinessType.DELETE)
@DeleteMapping("/{menuId}")
public AjaxResult remove(@PathVariable("menuId") Long menuId) {
if (menuService.hasChildByMenuId(menuId)) {
return warn("存在子菜单,不允许删除");
}
if (menuService.checkMenuExistRole(menuId)) {
return warn("菜单已分配,不允许删除");
}
return toAjax(menuService.deleteMenuById(menuId));
}
}

View File

@ -0,0 +1,100 @@
package com.ruoyi.web.controller.system;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.system.domain.SysNotice;
import com.ruoyi.system.service.ISysNoticeService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
/**
* 公告 信息操作处理
*
* @author ruoyi
*/
@Tag(name = "公告", description = "信息操作处理")
@RestController
@RequestMapping("/system/notice")
public class SysNoticeController extends BaseController {
@Autowired
private ISysNoticeService noticeService;
/**
* 获取通知公告列表
*/
@Operation(summary = "获取通知公告列表")
@GetMapping("/list")
public TableDataInfo list(SysNotice notice) {
startPage();
if (!SecurityUtils.hasPermi("system:notice:list")) {
notice.setStatus("0");
}
List<SysNotice> list = noticeService.selectNoticeList(notice);
return getDataTable(list);
}
/**
* 根据通知公告编号获取详细信息
*/
@Operation(summary = "根据通知公告编号获取详细信息")
@PreAuthorize("@ss.hasPermi('system:notice:query')")
@GetMapping(value = "/{noticeId}")
public AjaxResult getInfo(@PathVariable(name = "noticeId") Long noticeId) {
return success(noticeService.selectNoticeById(noticeId));
}
/**
* 新增通知公告
*/
@Operation(summary = "新增通知公告")
@PreAuthorize("@ss.hasPermi('system:notice:add')")
@Log(title = "通知公告", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@Validated @RequestBody SysNotice notice) {
notice.setCreateBy(getUsername());
return toAjax(noticeService.insertNotice(notice));
}
/**
* 修改通知公告
*/
@Operation(summary = "修改通知公告")
@PreAuthorize("@ss.hasPermi('system:notice:edit')")
@Log(title = "通知公告", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@Validated @RequestBody SysNotice notice) {
notice.setUpdateBy(getUsername());
return toAjax(noticeService.updateNotice(notice));
}
/**
* 删除通知公告
*/
@Operation(summary = "删除通知公告")
@PreAuthorize("@ss.hasPermi('system:notice:remove')")
@Log(title = "通知公告", businessType = BusinessType.DELETE)
@DeleteMapping("/{noticeIds}")
public AjaxResult remove(@PathVariable(name = "noticeIds") Long[] noticeIds) {
return toAjax(noticeService.deleteNoticeByIds(noticeIds));
}
}

View File

@ -0,0 +1,129 @@
package com.ruoyi.web.controller.system;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
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 io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletResponse;
/**
* 岗位信息操作处理
*
* @author ruoyi
*/
@Tag(name = "岗位信息操作处理")
@RestController
@RequestMapping("/system/post")
public class SysPostController extends BaseController {
@Autowired
private ISysPostService postService;
/**
* 获取岗位列表
*/
@Operation(summary = "获取岗位列表")
@PreAuthorize("@ss.hasPermi('system:post:list')")
@GetMapping("/list")
public TableDataInfo list(SysPost post) {
startPage();
List<SysPost> list = postService.selectPostList(post);
return getDataTable(list);
}
@Operation(summary = "导出岗位列表")
@Log(title = "岗位管理", businessType = BusinessType.EXPORT)
@PreAuthorize("@ss.hasPermi('system:post:export')")
@PostMapping("/export")
public void export(HttpServletResponse response, SysPost post) {
List<SysPost> list = postService.selectPostList(post);
ExcelUtil<SysPost> util = new ExcelUtil<SysPost>(SysPost.class);
util.exportExcel(response, list, "岗位数据");
}
/**
* 根据岗位编号获取详细信息
*/
@Operation(summary = "根据岗位编号获取详细信息")
@PreAuthorize("@ss.hasPermi('system:post:query')")
@GetMapping(value = "/{postId}")
public AjaxResult getInfo(@PathVariable(name = "postId") Long postId) {
return success(postService.selectPostById(postId));
}
/**
* 新增岗位
*/
@Operation(summary = "新增岗位")
@PreAuthorize("@ss.hasPermi('system:post:add')")
@Log(title = "岗位管理", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@Validated @RequestBody SysPost post) {
if (!postService.checkPostNameUnique(post)) {
return error("新增岗位'" + post.getPostName() + "'失败,岗位名称已存在");
} else if (!postService.checkPostCodeUnique(post)) {
return error("新增岗位'" + post.getPostName() + "'失败,岗位编码已存在");
}
post.setCreateBy(getUsername());
return toAjax(postService.insertPost(post));
}
/**
* 修改岗位
*/
@Operation(summary = "修改岗位")
@PreAuthorize("@ss.hasPermi('system:post:edit')")
@Log(title = "岗位管理", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@Validated @RequestBody SysPost post) {
if (!postService.checkPostNameUnique(post)) {
return error("修改岗位'" + post.getPostName() + "'失败,岗位名称已存在");
} else if (!postService.checkPostCodeUnique(post)) {
return error("修改岗位'" + post.getPostName() + "'失败,岗位编码已存在");
}
post.setUpdateBy(getUsername());
return toAjax(postService.updatePost(post));
}
/**
* 删除岗位
*/
@Operation(summary = "删除岗位")
@PreAuthorize("@ss.hasPermi('system:post:remove')")
@Log(title = "岗位管理", businessType = BusinessType.DELETE)
@DeleteMapping("/{postIds}")
public AjaxResult remove(@PathVariable(name = "postIds") Long[] postIds) {
return toAjax(postService.deletePostByIds(postIds));
}
/**
* 获取岗位选择框列表
*/
@Operation(summary = "获取岗位选择框列表")
@GetMapping("/optionselect")
public AjaxResult optionselect() {
List<SysPost> posts = postService.selectPostAll();
return success(posts);
}
}

View File

@ -0,0 +1,144 @@
package com.ruoyi.web.controller.system;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.file.FileUtils;
import com.ruoyi.common.utils.file.MimeTypeUtils;
import com.ruoyi.file.utils.FileOperateUtils;
import com.ruoyi.framework.web.service.TokenService;
import com.ruoyi.system.service.ISysUserService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
/**
* 个人信息 业务处理
*
* @author ruoyi
*/
@Tag(name = "个人信息", description = "业务处理")
@RestController
@RequestMapping("/system/user/profile")
public class SysProfileController extends BaseController {
@Autowired
private ISysUserService userService;
@Autowired
private TokenService tokenService;
/**
* 个人信息
*/
@Operation(summary = "个人信息")
@GetMapping
public AjaxResult profile() {
LoginUser loginUser = getLoginUser();
SysUser user = loginUser.getUser();
if (user.getAvatar() != null) {
try {
user.setAvatar(FileOperateUtils.getURL(user.getAvatar()));
} catch (Exception e) {
}
}
AjaxResult ajax = AjaxResult.success(user);
ajax.put("roleGroup", userService.selectUserRoleGroup(loginUser.getUsername()));
ajax.put("postGroup", userService.selectUserPostGroup(loginUser.getUsername()));
return ajax;
}
/**
* 修改用户
*/
@Operation(summary = "修改用户")
@Log(title = "个人信息", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult updateProfile(@RequestBody SysUser user) {
LoginUser loginUser = getLoginUser();
SysUser sysUser = loginUser.getUser();
sysUser.setNickName(user.getNickName());
sysUser.setEmail(user.getEmail());
sysUser.setPhonenumber(user.getPhonenumber());
sysUser.setSex(user.getSex());
if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) {
return error("修改用户'" + user.getUserName() + "'失败,手机号码已存在");
}
if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user)) {
return error("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在");
}
if (userService.updateUserProfile(user) > 0) {
tokenService.setLoginUser(loginUser);
return success();
}
return error("修改个人信息异常,请联系管理员");
}
/**
* 重置密码
*/
@Operation(summary = "重置密码")
@Log(title = "个人信息", businessType = BusinessType.UPDATE)
@PutMapping("/updatePwd")
public AjaxResult updatePwd(String oldPassword, String newPassword) {
LoginUser loginUser = getLoginUser();
String userName = loginUser.getUsername();
String password = loginUser.getPassword();
if (!SecurityUtils.matchesPassword(oldPassword, password)) {
return error("修改密码失败,旧密码错误");
}
if (SecurityUtils.matchesPassword(newPassword, password)) {
return error("新密码不能与旧密码相同");
}
newPassword = SecurityUtils.encryptPassword(newPassword);
if (userService.resetUserPwd(userName, newPassword) > 0) {
// 更新缓存用户密码
loginUser.getUser().setPwdUpdateDate(DateUtils.getNowDate());
loginUser.getUser().setPassword(newPassword);
tokenService.setLoginUser(loginUser);
return success();
}
return error("修改密码异常,请联系管理员");
}
/**
* 头像上传
*/
@Operation(summary = "头像上传")
@Log(title = "用户头像", businessType = BusinessType.UPDATE)
@PostMapping("/avatar")
public AjaxResult avatar(@RequestParam("avatarfile") MultipartFile file) throws Exception {
if (!file.isEmpty()) {
LoginUser loginUser = getLoginUser();
String extractPath = loginUser.getUsername() + "/" + loginUser.getUserId() + "";
String fileName = DateUtils.dateTimeNow() + "-avatar." + FileUtils.getExtension(file);
String filePath = "avatar/" + extractPath + "/" + fileName;
String url = FileOperateUtils.upload(filePath, file, MimeTypeUtils.IMAGE_EXTENSION);
if (userService.updateUserAvatar(loginUser.getUsername(), filePath)) {
AjaxResult ajax = AjaxResult.success();
ajax.put("imgUrl", url);
// 更新缓存用户头像
loginUser.getUser().setAvatar(filePath);
tokenService.setLoginUser(loginUser);
return ajax;
}
}
return error("上传图片异常,请联系管理员");
}
}

View File

@ -0,0 +1,42 @@
package com.ruoyi.web.controller.system;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.model.RegisterBody;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.framework.web.service.SysRegisterService;
import com.ruoyi.system.service.ISysConfigService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
/**
* 注册验证
*
* @author ruoyi
*/
@Tag(name = "注册验证")
@RestController
public class SysRegisterController extends BaseController {
@Autowired
private SysRegisterService registerService;
@Autowired
private ISysConfigService configService;
@Operation(summary = "注册方法")
@PostMapping("/register")
public AjaxResult register(@RequestBody RegisterBody user) {
if (!("true".equals(configService.selectConfigByKey("sys.account.registerUser")))) {
return error("当前系统没有开启注册功能!");
}
String msg = registerService.register(user);
return StringUtils.isEmpty(msg) ? success() : error(msg);
}
}

View File

@ -0,0 +1,260 @@
package com.ruoyi.web.controller.system;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
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.utils.StringUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.framework.web.service.SysPermissionService;
import com.ruoyi.framework.web.service.TokenService;
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 io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletResponse;
/**
* 角色信息
*
* @author ruoyi
*/
@Tag(name = "角色信息")
@RestController
@RequestMapping("/system/role")
public class SysRoleController extends BaseController {
@Autowired
private ISysRoleService roleService;
@Autowired
private TokenService tokenService;
@Autowired
private SysPermissionService permissionService;
@Autowired
private ISysUserService userService;
@Autowired
private ISysDeptService deptService;
@Operation(summary = "获取角色列表")
@PreAuthorize("@ss.hasPermi('system:role:list')")
@GetMapping("/list")
public TableDataInfo list(SysRole role) {
startPage();
List<SysRole> list = roleService.selectRoleList(role);
return getDataTable(list);
}
@Operation(summary = "导出角色列表")
@Log(title = "角色管理", businessType = BusinessType.EXPORT)
@PreAuthorize("@ss.hasPermi('system:role:export')")
@PostMapping("/export")
public void export(HttpServletResponse response, SysRole role) {
List<SysRole> list = roleService.selectRoleList(role);
ExcelUtil<SysRole> util = new ExcelUtil<SysRole>(SysRole.class);
util.exportExcel(response, list, "角色数据");
}
/**
* 根据角色编号获取详细信息
*/
@Operation(summary = "根据角色编号获取详细信息")
@PreAuthorize("@ss.hasPermi('system:role:query')")
@GetMapping(value = "/{roleId}")
public AjaxResult getInfo(@PathVariable(name = "roleId") Long roleId) {
roleService.checkRoleDataScope(roleId);
return success(roleService.selectRoleById(roleId));
}
/**
* 新增角色
*/
@Operation(summary = "新增角色")
@PreAuthorize("@ss.hasPermi('system:role:add')")
@Log(title = "角色管理", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@Validated @RequestBody SysRole role) {
if (!roleService.checkRoleNameUnique(role)) {
return error("新增角色'" + role.getRoleName() + "'失败,角色名称已存在");
} else if (!roleService.checkRoleKeyUnique(role)) {
return error("新增角色'" + role.getRoleName() + "'失败,角色权限已存在");
}
role.setCreateBy(getUsername());
return toAjax(roleService.insertRole(role));
}
/**
* 修改保存角色
*/
@Operation(summary = "修改保存角色")
@PreAuthorize("@ss.hasPermi('system:role:edit')")
@Log(title = "角色管理", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@Validated @RequestBody SysRole role) {
roleService.checkRoleAllowed(role);
roleService.checkRoleDataScope(role.getRoleId());
if (!roleService.checkRoleNameUnique(role)) {
return error("修改角色'" + role.getRoleName() + "'失败,角色名称已存在");
} else if (!roleService.checkRoleKeyUnique(role)) {
return error("修改角色'" + role.getRoleName() + "'失败,角色权限已存在");
}
role.setUpdateBy(getUsername());
if (roleService.updateRole(role) > 0) {
// 更新缓存用户权限
LoginUser loginUser = getLoginUser();
if (StringUtils.isNotNull(loginUser.getUser()) && !loginUser.getUser().isAdmin()) {
loginUser.setUser(userService.selectUserByUserName(loginUser.getUser().getUserName()));
loginUser.setPermissions(permissionService.getMenuPermission(loginUser.getUser()));
tokenService.setLoginUser(loginUser);
}
return success();
}
return error("修改角色'" + role.getRoleName() + "'失败,请联系管理员");
}
/**
* 修改保存数据权限
*/
@Operation(summary = "修改保存数据权限")
@PreAuthorize("@ss.hasPermi('system:role:edit')")
@Log(title = "角色管理", businessType = BusinessType.UPDATE)
@PutMapping("/dataScope")
public AjaxResult dataScope(@RequestBody SysRole role) {
roleService.checkRoleAllowed(role);
roleService.checkRoleDataScope(role.getRoleId());
return toAjax(roleService.authDataScope(role));
}
/**
* 状态修改
*/
@Operation(summary = "状态修改")
@PreAuthorize("@ss.hasPermi('system:role:edit')")
@Log(title = "角色管理", businessType = BusinessType.UPDATE)
@PutMapping("/changeStatus")
public AjaxResult changeStatus(@RequestBody SysRole role) {
roleService.checkRoleAllowed(role);
roleService.checkRoleDataScope(role.getRoleId());
role.setUpdateBy(getUsername());
return toAjax(roleService.updateRoleStatus(role));
}
/**
* 删除角色
*/
@Operation(summary = "删除角色")
@PreAuthorize("@ss.hasPermi('system:role:remove')")
@Log(title = "角色管理", businessType = BusinessType.DELETE)
@DeleteMapping("/{roleIds}")
public AjaxResult remove(@PathVariable(name = "roleIds") Long[] roleIds) {
return toAjax(roleService.deleteRoleByIds(roleIds));
}
/**
* 获取角色选择框列表
*/
@Operation(summary = "获取角色选择框列表")
@PreAuthorize("@ss.hasPermi('system:role:query')")
@GetMapping("/optionselect")
public AjaxResult optionselect() {
return success(roleService.selectRoleAll());
}
/**
* 查询已分配用户角色列表
*/
@Operation(summary = "查询已分配用户角色列表")
@PreAuthorize("@ss.hasPermi('system:role:list')")
@GetMapping("/authUser/allocatedList")
public TableDataInfo allocatedList(SysUser user) {
startPage();
List<SysUser> list = userService.selectAllocatedList(user);
return getDataTable(list);
}
/**
* 查询未分配用户角色列表
*/
@Operation(summary = "查询未分配用户角色列表")
@PreAuthorize("@ss.hasPermi('system:role:list')")
@GetMapping("/authUser/unallocatedList")
public TableDataInfo unallocatedList(SysUser user) {
startPage();
List<SysUser> list = userService.selectUnallocatedList(user);
return getDataTable(list);
}
/**
* 取消授权用户
*/
@Operation(summary = "取消授权用户")
@PreAuthorize("@ss.hasPermi('system:role:edit')")
@Log(title = "角色管理", businessType = BusinessType.GRANT)
@PutMapping("/authUser/cancel")
public AjaxResult cancelAuthUser(@RequestBody SysUserRole userRole) {
return toAjax(roleService.deleteAuthUser(userRole));
}
/**
* 批量取消授权用户
*/
@Operation(summary = "批量取消授权用户")
@PreAuthorize("@ss.hasPermi('system:role:edit')")
@Log(title = "角色管理", businessType = BusinessType.GRANT)
@PutMapping("/authUser/cancelAll")
public AjaxResult cancelAuthUserAll(Long roleId, Long[] userIds) {
return toAjax(roleService.deleteAuthUsers(roleId, userIds));
}
/**
* 批量选择用户授权
*/
@Operation(summary = "批量选择用户授权")
@PreAuthorize("@ss.hasPermi('system:role:edit')")
@Log(title = "角色管理", businessType = BusinessType.GRANT)
@PutMapping("/authUser/selectAll")
public AjaxResult selectAuthUserAll(Long roleId, Long[] userIds) {
roleService.checkRoleDataScope(roleId);
return toAjax(roleService.insertAuthUsers(roleId, userIds));
}
/**
* 获取对应角色部门树列表
*/
@Operation(summary = "获取对应角色部门树列表")
@PreAuthorize("@ss.hasPermi('system:role:query')")
@GetMapping(value = "/deptTree/{roleId}")
public AjaxResult deptTree(@PathVariable("roleId") Long roleId) {
AjaxResult ajax = AjaxResult.success();
ajax.put("checkedKeys", deptService.selectDeptListByRoleId(roleId));
ajax.put("depts", deptService.selectDeptTreeList(new SysDept()));
return ajax;
}
}

View File

@ -0,0 +1,247 @@
package com.ruoyi.web.controller.system;
import java.util.List;
import java.util.stream.Collectors;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
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.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
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 io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletResponse;
/**
* 用户信息
*
* @author ruoyi
*/
@Tag(name = "用户信息")
@RestController
@RequestMapping("/system/user")
public class SysUserController extends BaseController {
@Autowired
private ISysUserService userService;
@Autowired
private ISysRoleService roleService;
@Autowired
private ISysDeptService deptService;
@Autowired
private ISysPostService postService;
/**
* 获取用户列表
*/
@Operation(summary = "获取用户列表")
@PreAuthorize("@ss.hasPermi('system:user:list')")
@GetMapping("/list")
public TableDataInfo list(SysUser user) {
startPage();
List<SysUser> list = userService.selectUserList(user);
return getDataTable(list);
}
@Operation(summary = "导出用户列表")
@Log(title = "用户管理", businessType = BusinessType.EXPORT)
@PreAuthorize("@ss.hasPermi('system:user:export')")
@PostMapping("/export")
public void export(HttpServletResponse response, SysUser user) {
List<SysUser> list = userService.selectUserList(user);
ExcelUtil<SysUser> util = new ExcelUtil<SysUser>(SysUser.class);
util.exportExcel(response, list, "用户数据");
}
@Operation(summary = "导入用户列表")
@Log(title = "用户管理", businessType = BusinessType.IMPORT)
@PreAuthorize("@ss.hasPermi('system:user:import')")
@PostMapping("/importData")
public AjaxResult importData(MultipartFile file, boolean updateSupport) throws Exception {
ExcelUtil<SysUser> util = new ExcelUtil<SysUser>(SysUser.class);
List<SysUser> userList = util.importExcel(file.getInputStream());
String operName = getUsername();
String message = userService.importUser(userList, updateSupport, operName);
return success(message);
}
@Operation(summary = "获取导入用户模板")
@PostMapping("/importTemplate")
public void importTemplate(HttpServletResponse response) {
ExcelUtil<SysUser> util = new ExcelUtil<SysUser>(SysUser.class);
util.importTemplateExcel(response, "用户数据");
}
/**
* 根据用户编号获取详细信息
*/
@Operation(summary = "根据用户编号获取详细信息")
@PreAuthorize("@ss.hasPermi('system:user:query')")
@GetMapping(value = { "/", "/{userId}" })
public AjaxResult getInfo(@PathVariable(value = "userId", required = false) Long userId) {
AjaxResult ajax = AjaxResult.success();
if (StringUtils.isNotNull(userId)) {
userService.checkUserDataScope(userId);
SysUser sysUser = userService.selectUserById(userId);
ajax.put(AjaxResult.DATA_TAG, sysUser);
ajax.put("postIds", postService.selectPostListByUserId(userId));
ajax.put("roleIds", sysUser.getRoles().stream().map(SysRole::getRoleId).collect(Collectors.toList()));
}
List<SysRole> roles = roleService.selectRoleAll();
ajax.put("roles", SysUser.isAdmin(userId) ? roles
: roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList()));
ajax.put("posts", postService.selectPostAll());
return ajax;
}
/**
* 新增用户
*/
@Operation(summary = "新增用户")
@PreAuthorize("@ss.hasPermi('system:user:add')")
@Log(title = "用户管理", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@Validated @RequestBody SysUser user) {
if (!userService.checkUserNameUnique(user)) {
return error("新增用户'" + user.getUserName() + "'失败,登录账号已存在");
} else if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) {
return error("新增用户'" + user.getUserName() + "'失败,手机号码已存在");
} else if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user)) {
return error("新增用户'" + user.getUserName() + "'失败,邮箱账号已存在");
}
user.setCreateBy(getUsername());
user.setPassword(SecurityUtils.encryptPassword(user.getPassword()));
return toAjax(userService.insertUser(user));
}
/**
* 修改用户
*/
@Operation(summary = "修改用户")
@PreAuthorize("@ss.hasPermi('system:user:edit')")
@Log(title = "用户管理", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@Validated @RequestBody SysUser user) {
userService.checkUserAllowed(user);
userService.checkUserDataScope(user.getUserId());
if (!userService.checkUserNameUnique(user)) {
return error("修改用户'" + user.getUserName() + "'失败,登录账号已存在");
} else if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) {
return error("修改用户'" + user.getUserName() + "'失败,手机号码已存在");
} else if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user)) {
return error("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在");
}
user.setUpdateBy(getUsername());
return toAjax(userService.updateUser(user));
}
/**
* 删除用户
*/
@Operation(summary = "删除用户")
@PreAuthorize("@ss.hasPermi('system:user:remove')")
@Log(title = "用户管理", businessType = BusinessType.DELETE)
@DeleteMapping("/{userIds}")
public AjaxResult remove(@PathVariable(name = "userIds") Long[] userIds) {
if (ArrayUtils.contains(userIds, getUserId())) {
return error("当前用户不能删除");
}
return toAjax(userService.deleteUserByIds(userIds));
}
/**
* 重置密码
*/
@Operation(summary = "重置密码")
@PreAuthorize("@ss.hasPermi('system:user:resetPwd')")
@Log(title = "用户管理", businessType = BusinessType.UPDATE)
@PutMapping("/resetPwd")
public AjaxResult resetPwd(@RequestBody SysUser user) {
userService.checkUserAllowed(user);
userService.checkUserDataScope(user.getUserId());
user.setPassword(SecurityUtils.encryptPassword(user.getPassword()));
user.setUpdateBy(getUsername());
return toAjax(userService.resetPwd(user));
}
/**
* 状态修改
*/
@Operation(summary = "状态修改")
@PreAuthorize("@ss.hasPermi('system:user:edit')")
@Log(title = "用户管理", businessType = BusinessType.UPDATE)
@PutMapping("/changeStatus")
public AjaxResult changeStatus(@RequestBody SysUser user) {
userService.checkUserAllowed(user);
userService.checkUserDataScope(user.getUserId());
user.setUpdateBy(getUsername());
return toAjax(userService.updateUserStatus(user));
}
/**
* 根据用户编号获取授权角色
*/
@Operation(summary = "根据用户编号获取授权角色")
@PreAuthorize("@ss.hasPermi('system:user:query')")
@GetMapping("/authRole/{userId}")
public AjaxResult authRole(@PathVariable("userId") Long userId) {
AjaxResult ajax = AjaxResult.success();
SysUser user = userService.selectUserById(userId);
List<SysRole> roles = roleService.selectRolesByUserId(userId);
ajax.put("user", user);
ajax.put("roles", SysUser.isAdmin(userId) ? roles
: roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList()));
return ajax;
}
/**
* 用户授权角色
*/
@Operation(summary = "用户授权角色")
@PreAuthorize("@ss.hasPermi('system:user:edit')")
@Log(title = "用户管理", businessType = BusinessType.GRANT)
@PutMapping("/authRole")
public AjaxResult insertAuthRole(Long userId, Long[] roleIds) {
userService.checkUserDataScope(userId);
userService.insertUserAuth(userId, roleIds);
return success();
}
/**
* 获取部门树列表
*/
@Operation(summary = "获取部门树列表")
@PreAuthorize("@ss.hasPermi('system:user:list')")
@GetMapping("/deptTree")
public AjaxResult deptTree(SysDept dept) {
return success(deptService.selectDeptTreeList(dept));
}
}

View File

@ -0,0 +1,93 @@
package com.ruoyi.web.core.config;
import org.springdoc.core.models.GroupedOpenApi;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.ruoyi.common.constant.HttpStatus;
import com.ruoyi.common.core.domain.AjaxResult;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.ExternalDocumentation;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.info.License;
import io.swagger.v3.oas.models.media.IntegerSchema;
import io.swagger.v3.oas.models.media.ObjectSchema;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.media.StringSchema;
/**
* 验证码操作处理
*
* @author Dftre
*/
@Configuration
public class SwaggerConfig {
@Bean
public OpenAPI springShopOpenApi() {
Schema<?> codeSchema = new IntegerSchema().example(HttpStatus.SUCCESS); // 示例状态码
Schema<?> msgSchema = new StringSchema().example("操作成功"); // 示例消息
ObjectSchema dataSchema = new ObjectSchema(); // 数据可以是任意类型这里简单定义为ObjectSchema
// 定义AjaxResult的Schema
ObjectSchema ajaxResultSchema = new ObjectSchema();
ajaxResultSchema.addProperty(AjaxResult.CODE_TAG, codeSchema);
ajaxResultSchema.addProperty(AjaxResult.MSG_TAG, msgSchema);
ajaxResultSchema.addProperty(AjaxResult.DATA_TAG, dataSchema);
Components components = new Components();
components.addSchemas("AjaxResult", ajaxResultSchema);
return new OpenAPI()
.components(components)
.info(new Info().title("RuoYi Geek")
.description("RuoYi Geek API文档")
.version("v1")
.license(new License().name("Apache 2.0").url("http://springdoc.org")))
.externalDocs(new ExternalDocumentation()
.description("外部文档")
.url("/doc.html"));
}
@Bean
public GroupedOpenApi sysApi() {
return GroupedOpenApi.builder()
.group("sys系统模块")
.packagesToScan("com.ruoyi.web.controller.system")
.build();
}
@Bean
public GroupedOpenApi commonApi() {
return GroupedOpenApi.builder()
.group("基础模块")
.packagesToScan("com.ruoyi.web.controller.common")
.build();
}
@Bean
public GroupedOpenApi payApi() {
return GroupedOpenApi.builder()
.group("支付模块")
.pathsToMatch("/pay/**")
.build();
}
@Bean
public GroupedOpenApi fileApi() {
return GroupedOpenApi.builder()
.group("文件模块")
.packagesToScan("com.ruoyi.file.controller")
.build();
}
@Bean
public GroupedOpenApi authApi() {
return GroupedOpenApi.builder()
.group("认证模块")
.packagesToScan("com.ruoyi.auth.controller")
.build();
}
}

View File

@ -0,0 +1 @@
restart.include.json=/com.alibaba.fastjson2.*.jar

View File

@ -0,0 +1,49 @@
# 请输入自己的appid和appsecret
oauth:
wx:
miniapp:
appId: appId
appSecret: appSecret
url: https://api.weixin.qq.com/sns/jscode2session
pub:
appId: appId
appSecret: appSecret
url: https://api.weixin.qq.com/sns/oauth2/access_token
tfa:
phone:
dysms:
# 阿里云 AccessKey ID
accessKeyId: appId
# 阿里云 AccessKey Secret
accessKeySecret: appSecret
# 短信模板
template:
VerificationCode:
# 短信模板编码
templateCode: SMS_123456789
# 短信签名
signName: 阿里云短信测试
# 短信模板必需的数据名称多个key以逗号分隔此处配置作为校验
keys: code
justauth:
sources:
gitee:
clientId: 00636b75552921c3ab87161f146d41a678f1d50f3dc29ca1f0902e0c1507d755
clientSecret: 4cd38fe8b5e79c39a90548934f294f850763f1123bf86c08448e8d4c46cbeddf
redirectUri: http://127.0.0.1:8080/system/auth/social-login/gitee
github:
clientId: Iv1.1be0cdcd71aca63b
clientSecret: 0d59d28b43152bc8906011624db37b0fed88d154
redirectUri: http://127.0.0.1:8080/system/auth/social-login/github
spring:
mail:
# 邮箱配置 smtp.qq.com smtp.163.com
host: smtp.163.com
# 邮箱地址
username: email
# 授权码
password: password

View File

@ -0,0 +1,88 @@
# 数据源配置
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: com.mysql.cj.jdbc.Driver
# driverClassName: org.mariadb.jdbc.Driver
dynamic:
primary: MASTER
xa: true
datasource:
# 主库数据源
MASTER:
url: jdbc:mysql://192.168.3.246/ruoyi-geek?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true
# url: jdbc:mariadb://192.168.3.154:3306/ruoyi-geek?useSSL=false&characterEncoding=utf8&serverTimezone=Asia/Shanghai
username: root
password: 4877017Ldy.
# MASTER:
# url: jdbc:postgresql://127.0.0.1/ry
# username: postgres
# password: 123456
# 从库数据源
# SLAVE:
# url: jdbc:mysql://127.0.0.1/ruoyi?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
# username: root
# password: 123456
druid:
# 初始连接数
initialSize: 5
# 最小连接池数量
minIdle: 10
# 最大连接池数量
maxActive: 20
# 配置获取连接等待超时的时间
maxWait: 60000
# 配置连接超时时间
connectTimeout: 30000
# 配置网络超时时间
socketTimeout: 60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
timeBetweenEvictionRunsMillis: 60000
# 配置一个连接在池中最小生存的时间,单位是毫秒
minEvictableIdleTimeMillis: 300000
# 配置一个连接在池中最大生存的时间,单位是毫秒
maxEvictableIdleTimeMillis: 900000
# 配置检测连接是否有效
validationQuery: SELECT 1
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
# 配置监控统计拦截的filters stat=>监控统计 wall=>防SQL注入 slf4j log4j2=>日志记录
filters: stat,wall,slf4j,log4j2
# 通过connectProperties属性来打开mergeSql功能慢SQL记录
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
# 配置spring监控的匹配类
aop-patterns: com.ruoyi.*.service.*,com.ruoyi.*.mapper.*
# Web应用 和 URL监控 配置
web-stat-filter:
enabled: true
url-pattern: /*
exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"
session-stat-enable: false # 是否开启session统计功能
# 监控视图配置
stat-view-servlet:
enabled: true
url-pattern: /druid/*
reset-enable: true # 是否可以重置日志
login-username: ruoyi # 用户名
login-password: 123456 # 密码
allow: "" # IP白名单 (没有配置或者为空,则允许所有访问)
deny: "" # IP黑名单 (存在共同时deny优先于allow)
filter:
stat:
enabled: true
# 慢SQL记录
log-slow-sql: true
slow-sql-millis: 1000
merge-sql: true
wall:
config:
multi-statement-allow: true
# 是否开启分布式事务如不开启请删除atomikos插件否则atomikos相关驱动虽不生效但仍会启动
atomikos:
enabled: false

View File

@ -0,0 +1,53 @@
# local配置
local:
enable: true
primary: MASTER
client:
MASTER:
permission: public
path: /data/files/master
# 建议public权限的api以 /profile开头就不需要再从SecurityConfig配置权限了
api: /profile/files/master
# SLAVE:
# permission: private
# path: /data/files/slave
# private需要自己编写一个api例如FileController中的/file/resource
# api: /file/resource
# Minio配置
minio:
enable: false
primary: MASTER
client:
MASTER:
permission: public
url: http://localhost:9000
accessKey:
secretKey:
bucketName: ruoyi
# SLAVE:
# permission: private
# url: http://localhost:9000
# accessKey: minioadmin
# secretKey: minioadmin
# bucketName: ry
# oss配置
oss:
enable: false
primary: MASTER
client:
MASTER:
permission: public
accessKeyId: accessKeyId
accessKeySecret: accessKeySecret
bucketName: ruoyi
endpoint: oss-cn-beijing.aliyuncs.com
# SLAVE:
# permission: private
# accessKeyId:
# accessKeySecret:
# bucketName:
# endpoint:

View File

@ -0,0 +1,17 @@
# generator(代码生成器)模块: 基础配置
gen:
# 作者
author: ruoyi
# 默认生成包路径 system 需改成自己的模块名称 如 system monitor tool
packageName: com.ruoyi.system
# 自动去除表前缀默认是false
autoRemovePre: false
# 表前缀(生成类名不会包含表前缀,多个用逗号分隔)
tablePrefix: sys_
# flowable(工作流)模块: 基础配置
flowable:
# 开启定时任务JOB
async-executor-activate: false
# 在引擎启动时,会自动更新数据库架构
database-schema-update: true

View File

@ -0,0 +1,34 @@
# 回调地址使用的内网穿透https://natapp.cn/
# 回调地址根据退款还是支付的回调会自动在后面拼接 {订单ID}/{pay|refund}, 部分支付方式可能不支持退款回调
# 证书文件必须为UTF-8编码,不然会导致报错。
pay:
# https://doc.shouqianba.com/zh-cn/
sqb:
enabled: false
appId: "appId"
apiDomain: "apiDomain"
terminalSn: "terminalSn"
terminalKey: "terminalKey"
vendorSn: "vendorSn"
vendorKey: "vendorKey"
publicKey: classpath:pay/sqb/sqb_public_key.pem
notifyUrl: http://e2vca6.natappfree.cc/pay/sqb/notify
# https://opendocs.alipay.com/open/02np95
alipay:
enabled: false
appId: appid
# 文件内容没有换行符也没有PEM起始标记和结束标记
appPrivateKey: classpath:pay/alipay/alipay_private_key.pem
# 文件内容没有换行符也没有PEM起始标记和结束标记
alipayPublicKey: classpath:pay/alipay/alipay_public_key.pem
notifyUrl: http://e2vca6.natappfree.cc/alipay/notify
# https://github.com/wechatpay-apiv3/wechatpay-java
wechat:
enabled: false
appId: appid
apiV3Key: apiV3Key
# 文件内容应该有PEM起始标记和结束标记
privateKeyPath: classpath:pay/wx/apiclient_key.pem
merchantId: merchantId
merchantSerialNumber: merchantSerialNumber
notifyUrl: http://e2vca6.natappfree.cc/pay/wechat/notify

View File

@ -0,0 +1,41 @@
spring:
data:
# redis 配置
redis:
# 地址
host: 192.168.3.246
# 端口默认为6379
port: 6379
# 数据库索引
database: 0
# 密码
password: 4877017Ldy
# 连接超时时间
timeout: 10s
lettuce:
pool:
# 连接池中的最小空闲连接
min-idle: 0
# 连接池中的最大空闲连接
max-idle: 8
# 连接池的最大数据库连接数
max-active: 8
# #连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1ms
#配置rabbitMq 服务器
rabbitmq:
enable: false
host: 127.0.0.1
port: 5672
username: guest
password: guest
netty:
websocket:
maxMessageSize: 65536
bossThreads: 4
workerThreads: 16
port: 8081
enable: true

View File

@ -0,0 +1,163 @@
# 项目相关配置
ruoyi:
# 名称
name: RuoYi
# 版本
version: 3.8.5
# 版权年份
copyrightYear: 2024
# 文件路径 示例( Windows配置D:/ruoyi/uploadPathLinux配置 /home/ruoyi/uploadPath
profile: D:/ruoyi/uploadPath
# 获取ip地址开关
addressEnabled: false
# 验证码类型 math 数组计算 char 字符验证
captchaType: math
# 指定默认文件服务类型(值为local代表使用本地作为文件操作服务,minio代表使用minio作为文件操作服务,oss代表使用oss作为文件操作服务)
fileServer: local
# 指定默认文件上传方法最大文件大小(MB)
fileMaxSize: 50
# 开发环境配置
server:
# 服务器的HTTP端口默认为8080
port: 8080
servlet:
# 应用的访问路径
context-path: /
tomcat:
# tomcat的URI编码
uri-encoding: UTF-8
# 连接数满后的排队数默认为100
accept-count: 1000
threads:
# tomcat最大线程数默认为200
max: 800
# Tomcat启动初始化的线程数默认值10
min-spare: 100
# 日志配置
logging:
level:
"[com.ruoyi]": DEBUG
"[org]": WARN
"[org.springframework]": WARN
"[org.apache]": WARN
"[org.springframework.context.support.PostProcessorRegistrationDelegate]": ERROR
"[com.alibaba.druid.spring.boot3.autoconfigure.stat.DruidSpringAopConfiguration]": ERROR
"[com.alibaba.druid.spring.boot3.autoconfigure.properties.DruidStatProperties]": ERROR
# 用户配置
user:
password:
# 密码最大错误次数
maxRetryCount: 5
# 密码锁定时间默认10分钟
lockTime: 10
ip:
maxRetryCount: 15
lockTime: 15
# Spring配置
spring:
#给项目来个名字
application:
name: ruoyi
cache:
# 指定缓存类型 jcache 本地缓存 redis 缓存
type: redis
redis:
# 指定存活时间ms
time-to-live: 86400000
# 指定前缀
use-key-prefix: true
# 是否缓存空值,可以防止缓存穿透
cache-null-values: true
mvc:
pathmatch:
matching-strategy: ANT_PATH_MATCHER
# 资源信息
messages:
# 国际化资源文件路径
basename: i18n/messages
profiles:
active: druid,file,auth,pay,plugins,model
# 文件上传
servlet:
multipart:
# 单个文件大小
max-file-size: 10MB
# 设置总上传的文件大小
max-request-size: 20MB
# 服务模块
devtools:
restart:
# 热部署开关
enabled: true
# token配置
token:
# 令牌自定义标识
header: Authorization
# 令牌密钥
secret: abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz
# 令牌有效期默认1天
expireTime: 1440
# Swagger配置
swagger:
# 是否开启swagger
enabled: true
# 请求前缀
pathMapping: /dev-api
# 防盗链配置
referer:
# 防盗链开关
enabled: false
# 允许的域名列表
allowed-domains: localhost,127.0.0.1,ruoyi.vip,www.ruoyi.vip
# 防止XSS攻击
xss:
# 过滤开关
enabled: true
# 排除链接(多个用逗号分隔)
excludes: /system/notice
# 匹配链接
urlPatterns: /system/*,/monitor/*,/tool/*
createSqlSessionFactory:
# 选择MyBatis配置方式mybatis / mybatis-plus
use: mybatis-plus
datascope:
# default 原生数据范围注解 plus 增强数据范围注解
type: default
pageutils:
# default 使用PageHelper分页插件 custom 自研分页插件
type: default
# PageHelper分页插件
pagehelper:
helperDialect: mysql
supportMethodsArguments: true
params: count=countSql
# MyBatis配置
mybatis:
# 搜索指定包别名
typeAliasesPackage: com.ruoyi.**.domain
# 配置mapper的扫描找到所有的mapper.xml映射文件
mapperLocations: classpath*:mapper/**/*Mapper.xml
# 加载全局的配置文件
configLocation: classpath:mybatis/mybatis-config.xml
# MyBatis Plus配置
mybatis-plus:
# 搜索指定包别名
typeAliasesPackage: com.ruoyi.**.domain
# 配置mapper的扫描找到所有的mapper.xml映射文件
mapperLocations: classpath*:mapper/**/*Mapper.xml
# 加载全局的配置文件
configLocation: classpath:mybatis/mybatis-config.xml

View File

@ -0,0 +1,24 @@
Application Version: ${ruoyi.version}
Spring Boot Version: ${spring-boot.version}
////////////////////////////////////////////////////////////////////
// _ooOoo_ //
// o8888888o //
// 88" . "88 //
// (| ^_^ |) //
// O\ = /O //
// ____/`---'\____ //
// .' \\| |// `. //
// / \\||| : |||// \ //
// / _||||| -:- |||||- \ //
// | | \\\ - /// | | //
// | \_| ''\---/'' | | //
// \ .-\__ `-` ___/-. / //
// ___`. .' /--.--\ `. . ___ //
// ."" '< `.___\_<|>_/___.' >'"". //
// | | : `- \`.;`\ _ /`;.`/ - ` : | | //
// \ \ `-. \_ __\ /__ _/ .-` / / //
// ========`-.____`-.___\_____/___.-`____.-'======== //
// `=---=' //
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //
// 佛祖保佑 永不宕机 永无BUG //
////////////////////////////////////////////////////////////////////

View File

@ -0,0 +1,38 @@
#错误消息
not.null=* 必须填写
user.jcaptcha.error=验证码错误
user.jcaptcha.expire=验证码已失效
user.not.exists=用户不存在/密码错误
user.password.not.match=用户不存在/密码错误
user.password.retry.limit.count=密码输入错误{0}次
user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定{1}分钟
user.password.delete=对不起,您的账号已被删除
user.blocked=用户已封禁,请联系管理员
role.blocked=角色已封禁,请联系管理员
login.blocked=很遗憾访问IP已被列入系统黑名单
user.logout.success=退出成功
length.not.valid=长度必须在{min}到{max}个字符之间
user.username.not.valid=* 2到20个汉字、字母、数字或下划线组成且必须以非数字开头
user.password.not.valid=* 5-50个字符
user.email.not.valid=邮箱格式错误
user.mobile.phone.number.not.valid=手机号格式错误
user.login.success=登录成功
user.register.success=注册成功
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}]

View File

@ -0,0 +1,95 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 日志存放路径 -->
<property name="log.path" value="/home/ruoyi/logs" />
<!-- 日志输出格式 -->
<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />
<!-- 彩色控制台日志输出格式 -->
<property name="console.pattern" value="%blue(%d{HH:mm:ss.SSS}) %gray([%thread]) %highlight(%-5level %logger{20}) - %cyan([%method,%line]) - %msg%n" />
<!-- 控制台输出 -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${console.pattern}</pattern>
</encoder>
</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>
<!-- 用户访问日志输出 -->
<appender name="sys-user" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/sys-user.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 按天回滚 daily -->
<fileNamePattern>${log.path}/sys-user.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>60</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
</appender>
<!-- 系统模块日志级别控制 -->
<logger name="com.ruoyi" level="info" />
<!-- Spring日志级别控制 -->
<logger name="org.springframework" level="warn" />
<root level="info">
<appender-ref ref="console" />
</root>
<!--系统操作日志-->
<root level="info">
<appender-ref ref="file_info" />
<appender-ref ref="file_error" />
</root>
<!--系统用户操作日志-->
<logger name="sys-user" level="info">
<appender-ref ref="sys-user"/>
</logger>
</configuration>

View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 全局参数 -->
<settings>
<!-- 使全局的映射器启用或禁用缓存 -->
<setting name="cacheEnabled" value="true" />
<!-- 允许JDBC 支持自动生成主键 -->
<setting name="useGeneratedKeys" value="true" />
<!-- 配置默认的执行器.SIMPLE就是普通执行器;REUSE执行器会重用预处理语句(prepared statements);BATCH执行器将重用语句并执行批量更新 -->
<setting name="defaultExecutorType" value="SIMPLE" />
<!-- 指定 MyBatis 所用日志的具体实现 -->
<setting name="logImpl" value="SLF4J" />
<!-- 使用驼峰命名法转换字段 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!-- 打印sql日志 -->
<setting name="logImpl" value="STDOUT_LOGGING" />
</settings>
<!-- <plugins> -->
<!-- <plugin interceptor="com.ruoyi.mybatisinterceptor.interceptor.PageInercetor" /> -->
<!-- <plugin interceptor="com.ruoyi.mybatisinterceptor.interceptor.DataScopeInterceptor" /> -->
<!-- </plugins> -->
</configuration>

145
ruoyi-common/pom.xml Normal file
View File

@ -0,0 +1,145 @@
<?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</artifactId>
<groupId>com.ruoyi.geekxd</groupId>
<version>3.9.0-G</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>ruoyi-common</artifactId>
<description>
common通用工具
</description>
<dependencies>
<!-- SpringWeb模块 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- spring security 安全认证 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- pagehelper 分页插件 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</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>
<!--引入jsr310模块 支持java8的时间序列化 -->
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
<!-- 阿里JSON解析器 -->
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
</dependency>
<!-- io常用工具类 -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
</dependency>
<!-- excel工具 -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
</dependency>
<!-- Token生成与解析-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
</dependency>
<!-- Jaxb -->
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-core</artifactId>
</dependency>
<!-- pool 对象池 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
</dependency>
<!-- 解析客户端操作系统、浏览器等 -->
<dependency>
<groupId>eu.bitwalker</groupId>
<artifactId>UserAgentUtils</artifactId>
</dependency>
<!-- mybatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- SpringCache的依赖配置 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>javax.cache</groupId>
<artifactId>cache-api</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,19 @@
package com.ruoyi.common.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 匿名访问不鉴权注解
*
* @author ruoyi
*/
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Anonymous
{
}

View File

@ -0,0 +1,33 @@
package com.ruoyi.common.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 数据权限过滤注解
*
* @author ruoyi
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataScope
{
/**
* 部门表的别名
*/
public String deptAlias() default "";
/**
* 用户表的别名
*/
public String userAlias() default "";
/**
* 权限字符用于多个角色匹配符合要求的权限默认根据权限注解@ss获取多个权限用逗号分隔开来
*/
public String permission() default "";
}

View File

@ -0,0 +1,34 @@
package com.ruoyi.common.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.ruoyi.common.enums.DataSourceType;
/**
* 自定义多数据源切换注解
*
* 优先级先方法后类如果方法覆盖了类上的数据源类型以方法的为准否则以类上的为准
*
* @author ruoyi
*/
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface DataSource {
/**
* 切换数据源名称 - 枚举方式
*/
public DataSourceType value() default DataSourceType.MASTER;
/**
* 切换数据源名称 - 字符串方式
*/
public String name() default "";
}

View File

@ -0,0 +1,206 @@
package com.ruoyi.common.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.math.RoundingMode;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;
import com.ruoyi.common.utils.poi.ExcelHandlerAdapter;
/**
* 自定义导出Excel数据注解
*
* @author ruoyi
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Excel {
/**
* 导出时在excel中排序
*/
public int sort() default Integer.MAX_VALUE;
/**
* 导出到Excel中的名字.
*/
public String name() default "";
/**
* 日期格式, : yyyy-MM-dd
*/
public String dateFormat() default "";
/**
* 如果是字典类型请设置字典的type值 (: sys_user_sex)
*/
public String dictType() default "";
/**
* 读取内容转表达式 (: 0=,1=,2=未知)
*/
public String readConverterExp() default "";
/**
* 分隔符读取字符串组内容
*/
public String separator() default ",";
/**
* BigDecimal 精度 默认:-1(默认不开启BigDecimal格式化)
*/
public int scale() default -1;
/**
* BigDecimal 舍入规则 默认:BigDecimal.ROUND_HALF_EVEN
*/
public RoundingMode roundingMode() default RoundingMode.HALF_EVEN;
/**
* 导出时在excel中每个列的高度
*/
public double height() default 14;
/**
* 导出时在excel中每个列的宽度
*/
public double width() default 16;
/**
* 文字后缀,% 90 变成90%
*/
public String suffix() default "";
/**
* 当值为空时,字段的默认值
*/
public String defaultValue() default "";
/**
* 提示信息
*/
public String prompt() default "";
/**
* 是否允许内容换行
*/
public boolean wrapText() default false;
/**
* 设置只能选择不能输入的列内容.
*/
public String[] combo() default {};
/**
* 是否从字典读数据到combo,默认不读取,如读取需要设置dictType注解.
*/
public boolean comboReadDict() default false;
/**
* 是否需要纵向合并单元格,应对需求:含有list集合单元格)
*/
public boolean needMerge() default false;
/**
* 是否导出数据,应对需求:有时我们需要导出一份模板,这是标题需要但内容需要用户手工填写.
*/
public boolean isExport() default true;
/**
* 另一个类中的属性名称,支持多级获取,以小数点隔开
*/
public String targetAttr() default "";
/**
* 是否自动统计数据,在最后追加一行统计数据总和
*/
public boolean isStatistics() default false;
/**
* 导出类型0数字 1字符串 2图片
*/
public ColumnType cellType() default ColumnType.STRING;
/**
* 导出列头背景颜色
*/
public IndexedColors headerBackgroundColor() default IndexedColors.GREY_50_PERCENT;
/**
* 导出列头字体颜色
*/
public IndexedColors headerColor() default IndexedColors.WHITE;
/**
* 导出单元格背景颜色
*/
public IndexedColors backgroundColor() default IndexedColors.WHITE;
/**
* 导出单元格字体颜色
*/
public IndexedColors color() default IndexedColors.BLACK;
/**
* 导出字段对齐方式
*/
public HorizontalAlignment align() default HorizontalAlignment.CENTER;
/**
* 自定义数据处理器
*/
public Class<?> handler() default ExcelHandlerAdapter.class;
/**
* 自定义数据处理器参数
*/
public String[] args() default {};
/**
* 字段类型0导出导入1仅导出2仅导入
*/
Type type() default Type.ALL;
public enum Type {
/** 导出或导入 */
ALL(0),
/** 仅导出 */
EXPORT(1),
/** 仅导入 */
IMPORT(2);
private final int value;
Type(int value) {
this.value = value;
}
public int value() {
return this.value;
}
}
public enum ColumnType {
/** 数字 */
NUMERIC(0),
/** 字符串 */
STRING(1),
/** 图片 */
IMAGE(2),
/** 文本 */
TEXT(3);
private final int value;
ColumnType(int value) {
this.value = value;
}
public int value() {
return this.value;
}
}
}

View File

@ -0,0 +1,18 @@
package com.ruoyi.common.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Excel注解集
*
* @author ruoyi
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Excels
{
public Excel[] value();
}

View File

@ -0,0 +1,51 @@
package com.ruoyi.common.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.enums.OperatorType;
/**
* 自定义操作日志记录注解
*
* @author ruoyi
*
*/
@Target({ ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log
{
/**
* 模块
*/
public String title() default "";
/**
* 功能
*/
public BusinessType businessType() default BusinessType.OTHER;
/**
* 操作人类别
*/
public OperatorType operatorType() default OperatorType.MANAGE;
/**
* 是否保存请求的参数
*/
public boolean isSaveRequestData() default true;
/**
* 是否保存响应的参数
*/
public boolean isSaveResponseData() default true;
/**
* 排除指定的请求参数
*/
public String[] excludeParamNames() default {};
}

View File

@ -0,0 +1,31 @@
package com.ruoyi.common.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 自定义注解防止表单重复提交
*
* @author ruoyi
*
*/
@Inherited
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RepeatSubmit
{
/**
* 间隔时间(ms)小于此时间视为重复提交
*/
public int interval() default 5000;
/**
* 提示消息
*/
public String message() default "不允许重复提交,请稍候再试";
}

View File

@ -0,0 +1,25 @@
package com.ruoyi.common.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.ruoyi.common.config.serializer.SensitiveJsonSerializer;
import com.ruoyi.common.enums.DesensitizedType;
/**
* 数据脱敏注解
*
* @author ruoyi
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@JacksonAnnotationsInside
@JsonSerialize(using = SensitiveJsonSerializer.class)
public @interface Sensitive
{
DesensitizedType desensitizedType();
}

View File

@ -0,0 +1,135 @@
package com.ruoyi.common.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* 读取项目相关配置
*
* @author ruoyi
*/
@Component
@ConfigurationProperties(prefix = "ruoyi")
public class RuoYiConfig
{
/** 项目名称 */
private String name;
/** 版本 */
private String version;
/** 版权年份 */
private String copyrightYear;
/** 上传路径 */
private static String profile;
private static String fileServer;
/** 获取地址开关 */
private static boolean addressEnabled;
/** 验证码类型 */
private static String captchaType;
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public String getVersion()
{
return version;
}
public void setVersion(String version)
{
this.version = version;
}
public String getCopyrightYear()
{
return copyrightYear;
}
public void setCopyrightYear(String copyrightYear)
{
this.copyrightYear = copyrightYear;
}
public static String getProfile()
{
return profile;
}
public void setProfile(String profile)
{
RuoYiConfig.profile = profile;
}
public static boolean isAddressEnabled()
{
return addressEnabled;
}
public void setAddressEnabled(boolean addressEnabled)
{
RuoYiConfig.addressEnabled = addressEnabled;
}
public static String getCaptchaType() {
return captchaType;
}
public void setCaptchaType(String captchaType) {
RuoYiConfig.captchaType = captchaType;
}
public static String getFileServer() {
return fileServer;
}
public void setFileServer(String fileServer) {
RuoYiConfig.fileServer = fileServer;
}
/**
* 获取导入上传路径
*/
public static String getImportPath()
{
return getProfile() + "/import";
}
/**
* 获取头像上传路径
*/
public static String getAvatarPath()
{
return getProfile() + "/avatar";
}
/**
* 获取下载路径
*/
public static String getDownloadPath()
{
return getProfile() + "/download/";
}
/**
* 获取上传路径
*/
public static String getUploadPath()
{
return getProfile() + "/upload";
}
}

View File

@ -0,0 +1,68 @@
package com.ruoyi.common.config.serializer;
import java.io.IOException;
import java.util.Objects;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
import com.ruoyi.common.annotation.Sensitive;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.enums.DesensitizedType;
import com.ruoyi.common.utils.SecurityUtils;
/**
* 数据脱敏序列化过滤
*
* @author ruoyi
*/
public class SensitiveJsonSerializer extends JsonSerializer<String> implements ContextualSerializer
{
private DesensitizedType desensitizedType;
@Override
public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException
{
if (desensitization())
{
gen.writeString(desensitizedType.desensitizer().apply(value));
}
else
{
gen.writeString(value);
}
}
@Override
public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property)
throws JsonMappingException
{
Sensitive annotation = property.getAnnotation(Sensitive.class);
if (Objects.nonNull(annotation) && Objects.equals(String.class, property.getType().getRawClass()))
{
this.desensitizedType = annotation.desensitizedType();
return this;
}
return prov.findValueSerializer(property.getType(), property);
}
/**
* 是否需要脱敏处理
*/
private boolean desensitization()
{
try
{
LoginUser securityUser = SecurityUtils.getLoginUser();
// 管理员不脱敏
return !securityUser.getUser().isAdmin();
}
catch (Exception e)
{
return true;
}
}
}

View File

@ -0,0 +1,68 @@
package com.ruoyi.common.constant;
/**
* 缓存的key 常量
*
* @author ruoyi
*/
public class CacheConstants {
/**
* 登录用户 redis key
*/
public static final String LOGIN_TOKEN_KEY = "login_tokens";
/**
* 验证码 redis key
*/
public static final String CAPTCHA_CODE_KEY = "captcha_codes";
/**
* 参数管理 cache key
*/
public static final String SYS_CONFIG_KEY = "sys_config";
/**
* 字典管理 cache key
*/
public static final String SYS_DICT_KEY = "sys_dict";
/**
* 防重提交 redis key
*/
public static final String REPEAT_SUBMIT_KEY = "repeat_submit";
/**
* 限流 redis key
*/
public static final String RATE_LIMIT_KEY = "rate_limit";
/**
* 登录账户密码错误次数 redis key
*/
public static final String PWD_ERR_CNT_KEY = "pwd_err_cnt";
/**
* 登录ip错误次数 redis key
*/
public static final String IP_ERR_CNT_KEY = "ip_err_cnt_key";
/**
* 手机号验证码 phone codes
*/
public static final String PHONE_CODES = "phone_codes";
/**
* 邮箱验证码
*/
public static final String EMAIL_CODES = "email_codes";
/**
* 文件的md5 redis key
*/
public static final String FILE_MD5_PATH_KEY = "file_md5_path";
/**
* 文件路径 redis key
*/
public static final String FILE_PATH_MD5_KEY = "file_path_md5";
}

View File

@ -0,0 +1,175 @@
package com.ruoyi.common.constant;
import java.util.Locale;
import io.jsonwebtoken.Claims;
/**
* 通用常量信息
*
* @author ruoyi
*/
public class Constants {
/**
* UTF-8 字符集
*/
public static final String UTF8 = "UTF-8";
/**
* GBK 字符集
*/
public static final String GBK = "GBK";
/**
* 系统语言
*/
public static final Locale DEFAULT_LOCALE = Locale.SIMPLIFIED_CHINESE;
/**
* www主域
*/
public static final String WWW = "www.";
/**
* http请求
*/
public static final String HTTP = "http://";
/**
* https请求
*/
public static final String HTTPS = "https://";
/**
* 通用成功标识
*/
public static final String SUCCESS = "0";
/**
* 通用失败标识
*/
public static final String FAIL = "1";
/**
* 登录成功
*/
public static final String LOGIN_SUCCESS = "Success";
/**
* 注销
*/
public static final String LOGOUT = "Logout";
/**
* 注册
*/
public static final String REGISTER = "Register";
/**
* 登录失败
*/
public static final String LOGIN_FAIL = "Error";
/**
* 所有权限标识
*/
public static final String ALL_PERMISSION = "*:*:*";
/**
* 管理员角色权限标识
*/
public static final String SUPER_ADMIN = "admin";
/**
* 角色权限分隔符
*/
public static final String ROLE_DELIMETER = ",";
/**
* 权限标识分隔符
*/
public static final String PERMISSION_DELIMETER = ",";
/**
* 验证码有效期分钟
*/
public static final Integer CAPTCHA_EXPIRATION = 2;
/**
* 令牌
*/
public static final String TOKEN = "token";
/**
* 令牌前缀
*/
public static final String TOKEN_PREFIX = "Bearer ";
/**
* 令牌前缀
*/
public static final String LOGIN_USER_KEY = "login_user_key";
/**
* 用户ID
*/
public static final String JWT_USERID = "userid";
/**
* 用户名称
*/
public static final String JWT_USERNAME = Claims.SUBJECT;
/**
* 用户头像
*/
public static final String JWT_AVATAR = "avatar";
/**
* 创建时间
*/
public static final String JWT_CREATED = "created";
/**
* 用户权限
*/
public static final String JWT_AUTHORITIES = "authorities";
/**
* 资源映射路径 前缀
*/
public static final String RESOURCE_PREFIX = "/profile";
/**
* RMI 远程方法调用
*/
public static final String LOOKUP_RMI = "rmi:";
/**
* LDAP 远程方法调用
*/
public static final String LOOKUP_LDAP = "ldap:";
/**
* LDAPS 远程方法调用
*/
public static final String LOOKUP_LDAPS = "ldaps:";
/**
* 自动识别json对象白名单配置仅允许解析的包名范围越小越安全
*/
public static final String[] JSON_WHITELIST_STR = { "org.springframework", "com.ruoyi" };
/**
* 定时任务白名单配置仅允许访问的包名如其他需要可以自行添加
*/
public static final String[] JOB_WHITELIST_STR = { "com.ruoyi.quartz.task" };
/**
* 定时任务违规的字符
*/
public static final String[] JOB_ERROR_STR = {
"java.net.URL", "javax.naming.InitialContext",
"org.yaml.snakeyaml", "org.springframework", "org.apache",
"com.ruoyi.common.utils.file", "com.ruoyi.common.config", "com.ruoyi.generator" };
}

View File

@ -0,0 +1,94 @@
package com.ruoyi.common.constant;
/**
* 返回状态码
*
* @author ruoyi
*/
public class HttpStatus
{
/**
* 操作成功
*/
public static final int SUCCESS = 200;
/**
* 对象创建成功
*/
public static final int CREATED = 201;
/**
* 请求已经被接受
*/
public static final int ACCEPTED = 202;
/**
* 操作已经执行成功但是没有返回数据
*/
public static final int NO_CONTENT = 204;
/**
* 资源已被移除
*/
public static final int MOVED_PERM = 301;
/**
* 重定向
*/
public static final int SEE_OTHER = 303;
/**
* 资源没有被修改
*/
public static final int NOT_MODIFIED = 304;
/**
* 参数列表错误缺少格式不匹配
*/
public static final int BAD_REQUEST = 400;
/**
* 未授权
*/
public static final int UNAUTHORIZED = 401;
/**
* 访问受限授权过期
*/
public static final int FORBIDDEN = 403;
/**
* 资源服务未找到
*/
public static final int NOT_FOUND = 404;
/**
* 不允许的http方法
*/
public static final int BAD_METHOD = 405;
/**
* 资源冲突或者资源被锁
*/
public static final int CONFLICT = 409;
/**
* 不支持的数据媒体类型
*/
public static final int UNSUPPORTED_TYPE = 415;
/**
* 系统内部错误
*/
public static final int ERROR = 500;
/**
* 接口未实现
*/
public static final int NOT_IMPLEMENTED = 501;
/**
* 系统警告消息
*/
public static final int WARN = 601;
}

View File

@ -0,0 +1,50 @@
package com.ruoyi.common.constant;
/**
* 任务调度通用常量
*
* @author ruoyi
*/
public class ScheduleConstants
{
public static final String TASK_CLASS_NAME = "TASK_CLASS_NAME";
/** 执行目标key */
public static final String TASK_PROPERTIES = "TASK_PROPERTIES";
/** 默认 */
public static final String MISFIRE_DEFAULT = "0";
/** 立即触发执行 */
public static final String MISFIRE_IGNORE_MISFIRES = "1";
/** 触发一次执行 */
public static final String MISFIRE_FIRE_AND_PROCEED = "2";
/** 不触发立即执行 */
public static final String MISFIRE_DO_NOTHING = "3";
public enum Status
{
/**
* 正常
*/
NORMAL("0"),
/**
* 暂停
*/
PAUSE("1");
private String value;
private Status(String value)
{
this.value = value;
}
public String getValue()
{
return value;
}
}
}

View File

@ -0,0 +1,80 @@
package com.ruoyi.common.constant;
/**
* 用户常量信息
*
* @author ruoyi
*/
public class UserConstants {
/**
* 平台内系统用户的唯一标志
*/
public static final String SYS_USER = "SYS_USER";
/** 正常状态 */
public static final String NORMAL = "0";
/** 异常状态 */
public static final String EXCEPTION = "1";
/** 用户封禁状态 */
public static final String USER_DISABLE = "1";
/** 角色正常状态 */
public static final String ROLE_NORMAL = "0";
/** 角色封禁状态 */
public static final String ROLE_DISABLE = "1";
/** 部门正常状态 */
public static final String DEPT_NORMAL = "0";
/** 部门停用状态 */
public static final String DEPT_DISABLE = "1";
/** 字典正常状态 */
public static final String DICT_NORMAL = "0";
/** 是否为系统默认(是) */
public static final String YES = "Y";
/** 是否菜单外链(是) */
public static final String YES_FRAME = "0";
/** 是否菜单外链(否) */
public static final String NO_FRAME = "1";
/** 菜单类型(目录) */
public static final String TYPE_DIR = "M";
/** 菜单类型(菜单) */
public static final String TYPE_MENU = "C";
/** 菜单类型(按钮) */
public static final String TYPE_BUTTON = "F";
/** Layout组件标识 */
public final static String LAYOUT = "Layout";
/** ParentView组件标识 */
public final static String PARENT_VIEW = "ParentView";
/** InnerLink组件标识 */
public final static String INNER_LINK = "InnerLink";
/** 校验是否唯一的返回标识 */
public final static boolean UNIQUE = true;
public final static boolean NOT_UNIQUE = false;
/**
* 用户名长度限制
*/
public static final int USERNAME_MIN_LENGTH = 2;
public static final int USERNAME_MAX_LENGTH = 20;
/**
* 密码长度限制
*/
public static final int PASSWORD_MIN_LENGTH = 5;
public static final int PASSWORD_MAX_LENGTH = 20;
}

View File

@ -0,0 +1,201 @@
package com.ruoyi.common.core.controller;
import java.beans.PropertyEditorSupport;
import java.util.Date;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import com.ruoyi.common.constant.HttpStatus;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.core.page.PageDomain;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.core.page.TableSupport;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.PageUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.sql.SqlUtil;
/**
* web层通用数据处理
*
* @author ruoyi
*/
public class BaseController
{
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
/**
* 将前台传递过来的日期格式的字符串自动转化为Date类型
*/
@InitBinder
public void initBinder(WebDataBinder binder)
{
// Date 类型转换
binder.registerCustomEditor(Date.class, new PropertyEditorSupport()
{
@Override
public void setAsText(String text)
{
setValue(DateUtils.parseDate(text));
}
});
}
/**
* 设置请求分页数据
*/
protected void startPage()
{
PageUtils.startPage();
}
/**
* 设置请求排序数据
*/
protected void startOrderBy()
{
PageDomain pageDomain = TableSupport.buildPageRequest();
if (StringUtils.isNotEmpty(pageDomain.getOrderBy()))
{
String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy());
PageUtils.orderBy(orderBy);
}
}
/**
* 清理分页的线程变量
*/
protected void clearPage()
{
PageUtils.clearPage();
}
/**
* 响应请求分页数据
*/
protected TableDataInfo getDataTable(List<?> list)
{
TableDataInfo rspData = new TableDataInfo();
rspData.setCode(HttpStatus.SUCCESS);
rspData.setMsg("查询成功");
rspData.setRows(list);
rspData.setTotal(PageUtils.getTotal(list));
return rspData;
}
/**
* 返回成功
*/
public AjaxResult success()
{
return AjaxResult.success();
}
/**
* 返回失败消息
*/
public AjaxResult error()
{
return AjaxResult.error();
}
/**
* 返回成功消息
*/
public AjaxResult success(String message)
{
return AjaxResult.success(message);
}
/**
* 返回成功消息
*/
public AjaxResult success(Object data)
{
return AjaxResult.success(data);
}
/**
* 返回失败消息
*/
public AjaxResult error(String message)
{
return AjaxResult.error(message);
}
/**
* 返回警告消息
*/
public AjaxResult warn(String message)
{
return AjaxResult.warn(message);
}
/**
* 响应返回结果
*
* @param rows 影响行数
* @return 操作结果
*/
protected AjaxResult toAjax(int rows)
{
return rows > 0 ? AjaxResult.success() : AjaxResult.error();
}
/**
* 响应返回结果
*
* @param result 结果
* @return 操作结果
*/
protected AjaxResult toAjax(boolean result)
{
return result ? success() : error();
}
/**
* 页面跳转
*/
public String redirect(String url)
{
return StringUtils.format("redirect:{}", url);
}
/**
* 获取用户缓存信息
*/
public LoginUser getLoginUser()
{
return SecurityUtils.getLoginUser();
}
/**
* 获取登录用户id
*/
public Long getUserId()
{
return getLoginUser().getUserId();
}
/**
* 获取登录部门id
*/
public Long getDeptId()
{
return getLoginUser().getDeptId();
}
/**
* 获取登录用户名
*/
public String getUsername()
{
return getLoginUser().getUsername();
}
}

View File

@ -0,0 +1,185 @@
package com.ruoyi.common.core.domain;
import java.util.HashMap;
import com.ruoyi.common.constant.HttpStatus;
import com.ruoyi.common.utils.StringUtils;
/**
* 操作消息提醒
*
* @author ruoyi
*/
public class AjaxResult extends HashMap<String, Object>
{
private static final long serialVersionUID = 1L;
/** 状态码 */
public static final String CODE_TAG = "code";
/** 返回内容 */
public static final String MSG_TAG = "msg";
/** 数据对象 */
public static final String DATA_TAG = "data";
/**
* 初始化一个新创建的 AjaxResult 对象使其表示一个空消息
*/
public AjaxResult()
{
}
/**
* 初始化一个新创建的 AjaxResult 对象
*
* @param code 状态码
* @param msg 返回内容
*/
public AjaxResult(int code, String msg)
{
super.put(CODE_TAG, code);
super.put(MSG_TAG, msg);
}
/**
* 初始化一个新创建的 AjaxResult 对象
*
* @param code 状态码
* @param msg 返回内容
* @param data 数据对象
*/
public AjaxResult(int code, String msg, Object data)
{
super.put(CODE_TAG, code);
super.put(MSG_TAG, msg);
if (StringUtils.isNotNull(data))
{
super.put(DATA_TAG, data);
}
}
/**
* 返回成功消息
*
* @return 成功消息
*/
public static AjaxResult success()
{
return AjaxResult.success("操作成功");
}
/**
* 返回成功数据
*
* @return 成功消息
*/
public static AjaxResult success(Object data)
{
return AjaxResult.success("操作成功", data);
}
/**
* 返回成功消息
*
* @param msg 返回内容
* @return 成功消息
*/
public static AjaxResult success(String msg)
{
return AjaxResult.success(msg, null);
}
/**
* 返回成功消息
*
* @param msg 返回内容
* @param data 数据对象
* @return 成功消息
*/
public static AjaxResult success(String msg, Object data)
{
return new AjaxResult(HttpStatus.SUCCESS, msg, data);
}
/**
* 返回警告消息
*
* @param msg 返回内容
* @return 警告消息
*/
public static AjaxResult warn(String msg)
{
return AjaxResult.warn(msg, null);
}
/**
* 返回警告消息
*
* @param msg 返回内容
* @param data 数据对象
* @return 警告消息
*/
public static AjaxResult warn(String msg, Object data)
{
return new AjaxResult(HttpStatus.WARN, msg, data);
}
/**
* 返回错误消息
*
* @return 错误消息
*/
public static AjaxResult error()
{
return AjaxResult.error("操作失败");
}
/**
* 返回错误消息
*
* @param msg 返回内容
* @return 错误消息
*/
public static AjaxResult error(String msg)
{
return AjaxResult.error(msg, null);
}
/**
* 返回错误消息
*
* @param msg 返回内容
* @param data 数据对象
* @return 错误消息
*/
public static AjaxResult error(String msg, Object data)
{
return new AjaxResult(HttpStatus.ERROR, msg, data);
}
/**
* 返回错误消息
*
* @param code 状态码
* @param msg 返回内容
* @return 错误消息
*/
public static AjaxResult error(int code, String msg)
{
return new AjaxResult(code, msg, null);
}
/**
* 方便链式调用
*
* @param key
* @param value
* @return 数据对象
*/
@Override
public AjaxResult put(String key, Object value)
{
super.put(key, value);
return this;
}
}

View File

@ -0,0 +1,129 @@
package com.ruoyi.common.core.domain;
import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.v3.oas.annotations.media.Schema;
/**
* Entity基类
*
* @author ruoyi
*/
@Schema(title = "基类")
public class BaseEntity implements Serializable
{
private static final long serialVersionUID = 1L;
/** 搜索值 */
@Schema(title = "搜索值")
@JsonIgnore
private String searchValue;
/** 创建者 */
@Schema(title = "创建者")
private String createBy;
/** 创建时间 */
@Schema(title = "创建时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
/** 更新者 */
@Schema(title = "更新者")
private String updateBy;
/** 更新时间 */
@Schema(title = "更新时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date updateTime;
/** 备注 */
@Schema(title = "备注")
private String remark;
/** 请求参数 */
@Schema(title = "请求参数")
@JsonInclude(JsonInclude.Include.NON_EMPTY)
private Map<String, Object> params;
public String getSearchValue()
{
return searchValue;
}
public void setSearchValue(String searchValue)
{
this.searchValue = searchValue;
}
public String getCreateBy()
{
return createBy;
}
public void setCreateBy(String createBy)
{
this.createBy = createBy;
}
public Date getCreateTime()
{
return createTime;
}
public void setCreateTime(Date createTime)
{
this.createTime = createTime;
}
public String getUpdateBy()
{
return updateBy;
}
public void setUpdateBy(String updateBy)
{
this.updateBy = updateBy;
}
public Date getUpdateTime()
{
return updateTime;
}
public void setUpdateTime(Date updateTime)
{
this.updateTime = updateTime;
}
public String getRemark()
{
return remark;
}
public void setRemark(String remark)
{
this.remark = remark;
}
public Map<String, Object> getParams()
{
if (params == null)
{
params = new HashMap<>();
}
return params;
}
public void setParams(Map<String, Object> params)
{
this.params = params;
}
}

View File

@ -0,0 +1,186 @@
package com.ruoyi.common.core.domain;
import java.time.Instant;
import java.util.Map;
import java.util.UUID;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.ruoyi.common.enums.MessageType;
public class Message {
/** 消息唯一标识符 */
private String messageId;
/** 发送者标识 */
private String sender;
/** 接收者标识 */
private String receiver;
/** 消息时间戳 */
private Instant timestamp;
/** 消息类型(如命令、聊天、日志、事件等) */
private MessageType type;
/** 消息主题或事件名称 */
private String subject;
/** 消息数据内容 */
private String content;
/** 消息数据负载 */
private Map<String, Object> payload;
/** 元数据,用于存储额外的信息 */
private Map<String, Object> metadata;
/** 消息状态(如成功、失败、重试等) */
private String status;
/** 重试次数 */
private int retryCount;
/** 最大重试次数 */
private int maxRetries;
/** 重试间隔 */
private String retryInterval;
// 构造函数
public Message() {
this.messageId = UUID.randomUUID().toString();
this.timestamp = Instant.now();
}
public static Message create() {
return new Message();
}
public String getMessageId() {
return messageId;
}
public Message setMessageId(String messageId) {
this.messageId = messageId;
return this;
}
public String getSender() {
return sender;
}
public Message setSender(String sender) {
this.sender = sender;
return this;
}
public String getReceiver() {
return receiver;
}
public Message setReceiver(String receiver) {
this.receiver = receiver;
return this;
}
public Instant getTimestamp() {
return timestamp;
}
public Message setTimestamp(Instant timestamp) {
this.timestamp = timestamp;
return this;
}
public MessageType getType() {
return type;
}
public Message setType(MessageType type) {
this.type = type;
return this;
}
public String getSubject() {
return subject;
}
public Message setSubject(String subject) {
this.subject = subject;
return this;
}
public Map<String, Object> getPayload() {
return payload;
}
public Message setPayload(Map<String, Object> payload) {
this.payload = payload;
return this;
}
public Map<String, Object> getMetadata() {
return metadata;
}
public Message setMetadata(Map<String, Object> metadata) {
this.metadata = metadata;
return this;
}
public String getStatus() {
return status;
}
public Message setStatus(String status) {
this.status = status;
return this;
}
public int getRetryCount() {
return retryCount;
}
public Message setRetryCount(int retryCount) {
this.retryCount = retryCount;
return this;
}
public int getMaxRetries() {
return maxRetries;
}
public Message setMaxRetries(int maxRetries) {
this.maxRetries = maxRetries;
return this;
}
public String getRetryInterval() {
return retryInterval;
}
public Message setRetryInterval(String retryInterval) {
this.retryInterval = retryInterval;
return this;
}
public String getContent() {
return content;
}
public Message setContent(String content) {
this.content = content;
return this;
}
@Override
public String toString() {
return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
.append("messageId", getMessageId())
.append("sender", getSender())
.append("receiver", getReceiver())
.append("timestamp", getTimestamp())
.append("type", getType())
.append("subject", getSubject())
.append("content", getContent())
.append("payload", getPayload())
.append("metadata", getMetadata())
.append("status", getStatus())
.append("retryCount", getRetryCount())
.append("maxRetries", getMaxRetries())
.append("retryInterval", getRetryInterval())
.toString();
}
}

View File

@ -0,0 +1,122 @@
package com.ruoyi.common.core.domain;
import java.io.Serializable;
import com.ruoyi.common.constant.HttpStatus;
import io.swagger.v3.oas.annotations.media.Schema;
/**
* 响应信息主体
*
* @author ruoyi
*/
@Schema(title = "响应信息主体")
public class R<T> implements Serializable
{
private static final long serialVersionUID = 1L;
/** 成功 */
public static final int SUCCESS = HttpStatus.SUCCESS;
/** 失败 */
public static final int FAIL = HttpStatus.ERROR;
@Schema(title = "响应码")
private int code;
@Schema(title = "响应信息")
private String msg;
@Schema(title = "响应数据")
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(T data, String msg)
{
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(T data, String msg)
{
return restResult(data, FAIL, msg);
}
public static <T> R<T> fail(int code, String msg)
{
return restResult(null, code, msg);
}
private static <T> R<T> restResult(T data, int code, String msg)
{
R<T> apiResult = new R<>();
apiResult.setCode(code);
apiResult.setData(data);
apiResult.setMsg(msg);
return apiResult;
}
public int getCode()
{
return code;
}
public void setCode(int code)
{
this.code = code;
}
public String getMsg()
{
return msg;
}
public void setMsg(String msg)
{
this.msg = msg;
}
public T getData()
{
return data;
}
public void setData(T data)
{
this.data = data;
}
public static <T> Boolean isError(R<T> ret)
{
return !isSuccess(ret);
}
public static <T> Boolean isSuccess(R<T> ret)
{
return R.SUCCESS == ret.getCode();
}
}

View File

@ -0,0 +1,79 @@
package com.ruoyi.common.core.domain;
import java.util.ArrayList;
import java.util.List;
/**
* Tree基类
*
* @author ruoyi
*/
public class TreeEntity extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** 父菜单名称 */
private String parentName;
/** 父菜单ID */
private Long parentId;
/** 显示顺序 */
private Integer orderNum;
/** 祖级列表 */
private String ancestors;
/** 子部门 */
private List<?> children = new ArrayList<>();
public String getParentName()
{
return parentName;
}
public void setParentName(String parentName)
{
this.parentName = parentName;
}
public Long getParentId()
{
return parentId;
}
public void setParentId(Long parentId)
{
this.parentId = parentId;
}
public Integer getOrderNum()
{
return orderNum;
}
public void setOrderNum(Integer orderNum)
{
this.orderNum = orderNum;
}
public String getAncestors()
{
return ancestors;
}
public void setAncestors(String ancestors)
{
this.ancestors = ancestors;
}
public List<?> getChildren()
{
return children;
}
public void setChildren(List<?> children)
{
this.children = children;
}
}

View File

@ -0,0 +1,77 @@
package com.ruoyi.common.core.domain;
import java.io.Serializable;
import java.util.List;
import java.util.stream.Collectors;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.common.core.domain.entity.SysMenu;
/**
* Treeselect树结构实体类
*
* @author ruoyi
*/
public class TreeSelect implements Serializable
{
private static final long serialVersionUID = 1L;
/** 节点ID */
private Long id;
/** 节点名称 */
private String label;
/** 子节点 */
@JsonInclude(JsonInclude.Include.NON_EMPTY)
private List<TreeSelect> children;
public TreeSelect()
{
}
public TreeSelect(SysDept dept)
{
this.id = dept.getDeptId();
this.label = dept.getDeptName();
this.children = dept.getChildren().stream().map(TreeSelect::new).collect(Collectors.toList());
}
public TreeSelect(SysMenu menu)
{
this.id = menu.getMenuId();
this.label = menu.getMenuName();
this.children = menu.getChildren().stream().map(TreeSelect::new).collect(Collectors.toList());
}
public Long getId()
{
return id;
}
public void setId(Long id)
{
this.id = id;
}
public String getLabel()
{
return label;
}
public void setLabel(String label)
{
this.label = label;
}
public List<TreeSelect> getChildren()
{
return children;
}
public void setChildren(List<TreeSelect> children)
{
this.children = children;
}
}

View File

@ -0,0 +1,220 @@
package com.ruoyi.common.core.domain.entity;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.ruoyi.common.core.domain.BaseEntity;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
/**
* 部门表 sys_dept
*
* @author ruoyi
*/
@Schema(title = "部门")
public class SysDept extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** 部门ID */
@Schema(title = "部门ID")
private Long deptId;
/** 父部门ID */
@Schema(title = "父部门ID")
private Long parentId;
/** 祖级列表 */
@Schema(title = "祖级列表")
private String ancestors;
/** 部门名称 */
@Schema(title = "部门名称")
private String deptName;
/** 显示顺序 */
@Schema(title = "显示顺序")
private Integer orderNum;
/** 负责人 */
@Schema(title = "负责人")
private String leader;
/** 联系电话 */
@Schema(title = "联系电话")
private String phone;
/** 邮箱 */
@Schema(title = "邮箱")
private String email;
/** 部门状态:0正常,1停用 */
@Schema(title = "部门表",description = "0正常,1停用")
private String status;
/** 删除标志0代表存在 2代表删除 */
@Schema(title = "删除标志",description = "0代表存在 2代表删除")
private String delFlag;
/** 父部门名称 */
@Schema(title = "父部门名称")
private String parentName;
/** 子部门 */
@Schema(title = "子部门")
private List<SysDept> children = new ArrayList<SysDept>();
public Long getDeptId()
{
return deptId;
}
public void setDeptId(Long deptId)
{
this.deptId = deptId;
}
public Long getParentId()
{
return parentId;
}
public void setParentId(Long parentId)
{
this.parentId = parentId;
}
public String getAncestors()
{
return ancestors;
}
public void setAncestors(String ancestors)
{
this.ancestors = ancestors;
}
@NotBlank(message = "部门名称不能为空")
@Size(min = 0, max = 30, message = "部门名称长度不能超过30个字符")
public String getDeptName()
{
return deptName;
}
public void setDeptName(String deptName)
{
this.deptName = deptName;
}
@NotNull(message = "显示顺序不能为空")
public Integer getOrderNum()
{
return orderNum;
}
public void setOrderNum(Integer orderNum)
{
this.orderNum = orderNum;
}
public String getLeader()
{
return leader;
}
public void setLeader(String leader)
{
this.leader = leader;
}
@Size(min = 0, max = 11, message = "联系电话长度不能超过11个字符")
public String getPhone()
{
return phone;
}
public void setPhone(String phone)
{
this.phone = phone;
}
@Email(message = "邮箱格式不正确")
@Size(min = 0, max = 50, message = "邮箱长度不能超过50个字符")
public String getEmail()
{
return email;
}
public void setEmail(String email)
{
this.email = email;
}
public String getStatus()
{
return status;
}
public void setStatus(String status)
{
this.status = status;
}
public String getDelFlag()
{
return delFlag;
}
public void setDelFlag(String delFlag)
{
this.delFlag = delFlag;
}
public String getParentName()
{
return parentName;
}
public void setParentName(String parentName)
{
this.parentName = parentName;
}
public List<SysDept> getChildren()
{
return children;
}
public void setChildren(List<SysDept> children)
{
this.children = children;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
.append("deptId", getDeptId())
.append("parentId", getParentId())
.append("ancestors", getAncestors())
.append("deptName", getDeptName())
.append("orderNum", getOrderNum())
.append("leader", getLeader())
.append("phone", getPhone())
.append("email", getEmail())
.append("status", getStatus())
.append("delFlag", getDelFlag())
.append("createBy", getCreateBy())
.append("createTime", getCreateTime())
.append("updateBy", getUpdateBy())
.append("updateTime", getUpdateTime())
.toString();
}
}

View File

@ -0,0 +1,189 @@
package com.ruoyi.common.core.domain.entity;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.annotation.Excel.ColumnType;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.domain.BaseEntity;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
/**
* 字典数据表 sys_dict_data
*
* @author ruoyi
*/
@Schema(title = "字典数据")
public class SysDictData extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** 字典编码 */
@Schema(title = "字典编码")
@Excel(name = "字典编码", cellType = ColumnType.NUMERIC)
private Long dictCode;
/** 字典排序 */
@Schema(title = "字典排序")
@Excel(name = "字典排序", cellType = ColumnType.NUMERIC)
private Long dictSort;
/** 字典标签 */
@Schema(title = "字典标签")
@Excel(name = "字典标签")
private String dictLabel;
/** 字典键值 */
@Schema(title = "字典键值")
@Excel(name = "字典键值")
private String dictValue;
/** 字典类型 */
@Schema(title = "字典类型")
@Excel(name = "字典类型")
private String dictType;
/** 样式属性(其他样式扩展) */
@Schema(title = "样式属性", description = "其他样式扩展")
private String cssClass;
/** 表格字典样式 */
@Schema(title = "表格字典样式")
private String listClass;
/** 是否默认Y是 N否 */
@Schema(title = "是否默认", description = "Y=是,N=否")
@Excel(name = "是否默认", readConverterExp = "Y=是,N=否")
private String isDefault;
/** 状态0正常 1停用 */
@Schema(title = "状态", description = "0=正常,1=停用")
@Excel(name = "状态", readConverterExp = "0=正常,1=停用")
private String status;
public Long getDictCode()
{
return dictCode;
}
public void setDictCode(Long dictCode)
{
this.dictCode = dictCode;
}
public Long getDictSort()
{
return dictSort;
}
public void setDictSort(Long dictSort)
{
this.dictSort = dictSort;
}
@NotBlank(message = "字典标签不能为空")
@Size(min = 0, max = 100, message = "字典标签长度不能超过100个字符")
public String getDictLabel()
{
return dictLabel;
}
public void setDictLabel(String dictLabel)
{
this.dictLabel = dictLabel;
}
@NotBlank(message = "字典键值不能为空")
@Size(min = 0, max = 100, message = "字典键值长度不能超过100个字符")
public String getDictValue()
{
return dictValue;
}
public void setDictValue(String dictValue)
{
this.dictValue = dictValue;
}
@NotBlank(message = "字典类型不能为空")
@Size(min = 0, max = 100, message = "字典类型长度不能超过100个字符")
public String getDictType()
{
return dictType;
}
public void setDictType(String dictType)
{
this.dictType = dictType;
}
@Size(min = 0, max = 100, message = "样式属性长度不能超过100个字符")
public String getCssClass()
{
return cssClass;
}
public void setCssClass(String cssClass)
{
this.cssClass = cssClass;
}
public String getListClass()
{
return listClass;
}
public void setListClass(String listClass)
{
this.listClass = listClass;
}
public boolean getDefault()
{
return UserConstants.YES.equals(this.isDefault);
}
public String getIsDefault()
{
return isDefault;
}
public void setIsDefault(String isDefault)
{
this.isDefault = isDefault;
}
public String getStatus()
{
return status;
}
public void setStatus(String status)
{
this.status = status;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
.append("dictCode", getDictCode())
.append("dictSort", getDictSort())
.append("dictLabel", getDictLabel())
.append("dictValue", getDictValue())
.append("dictType", getDictType())
.append("cssClass", getCssClass())
.append("listClass", getListClass())
.append("isDefault", getIsDefault())
.append("status", getStatus())
.append("createBy", getCreateBy())
.append("createTime", getCreateTime())
.append("updateBy", getUpdateBy())
.append("updateTime", getUpdateTime())
.append("remark", getRemark())
.toString();
}
}

View File

@ -0,0 +1,104 @@
package com.ruoyi.common.core.domain.entity;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.annotation.Excel.ColumnType;
import com.ruoyi.common.core.domain.BaseEntity;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size;
/**
* 字典类型表 sys_dict_type
*
* @author ruoyi
*/
@Schema(title = "字典类型")
public class SysDictType extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** 字典主键 */
@Schema(title = "字典主键")
@Excel(name = "字典主键", cellType = ColumnType.NUMERIC)
private Long dictId;
/** 字典名称 */
@Schema(title = "字典名称")
@Excel(name = "字典名称")
private String dictName;
/** 字典类型 */
@Schema(title = "字典类型")
@Excel(name = "字典类型")
private String dictType;
/** 状态0正常 1停用 */
@Schema(title = "状态", description = "0正常 1停用")
@Excel(name = "状态", readConverterExp = "0=正常,1=停用")
private String status;
public Long getDictId()
{
return dictId;
}
public void setDictId(Long dictId)
{
this.dictId = dictId;
}
@NotBlank(message = "字典名称不能为空")
@Size(min = 0, max = 100, message = "字典类型名称长度不能超过100个字符")
public String getDictName()
{
return dictName;
}
public void setDictName(String dictName)
{
this.dictName = dictName;
}
@NotBlank(message = "字典类型不能为空")
@Size(min = 0, max = 100, message = "字典类型类型长度不能超过100个字符")
@Pattern(regexp = "^[a-z][a-z0-9_]*$", message = "字典类型必须以字母开头,且只能为(小写字母,数字,下滑线)")
public String getDictType()
{
return dictType;
}
public void setDictType(String dictType)
{
this.dictType = dictType;
}
public String getStatus()
{
return status;
}
public void setStatus(String status)
{
this.status = status;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
.append("dictId", getDictId())
.append("dictName", getDictName())
.append("dictType", getDictType())
.append("status", getStatus())
.append("createBy", getCreateBy())
.append("createTime", getCreateTime())
.append("updateBy", getUpdateBy())
.append("updateTime", getUpdateTime())
.append("remark", getRemark())
.toString();
}
}

View File

@ -0,0 +1,260 @@
package com.ruoyi.common.core.domain.entity;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.ruoyi.common.core.domain.BaseEntity;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
/**
* 菜单权限表 sys_menu
*
* @author ruoyi
*/
@Schema(title = "菜单权限")
public class SysMenu extends BaseEntity {
private static final long serialVersionUID = 1L;
/** 菜单ID */
@Schema(title = "菜单ID")
private Long menuId;
/** 菜单名称 */
@Schema(title = "菜单名称")
private String menuName;
/** 父菜单名称 */
@Schema(title = "父菜单名称")
private String parentName;
/** 父菜单ID */
@Schema(title = "父菜单ID")
private Long parentId;
/** 显示顺序 */
@Schema(title = "显示顺序")
private Integer orderNum;
/** 路由地址 */
@Schema(title = "路由地址")
private String path;
/** 组件路径 */
@Schema(title = "组件路径")
private String component;
/** 路由参数 */
@Schema(title = "路由参数")
private String query;
/** 路由名称默认和路由地址相同的驼峰格式注意因为vue3版本的router会删除名称相同路由为避免名字的冲突特殊情况可以自定义 */
private String routeName;
/** 是否为外链0是 1否 */
@Schema(title = "是否为外链", description = "0是 1否")
private String isFrame;
/** 是否缓存0缓存 1不缓存 */
@Schema(title = "是否缓存", description = "0缓存 1不缓存")
private String isCache;
/** 类型M目录 C菜单 F按钮 */
@Schema(title = "类型", description = "M目录 C菜单 F按钮")
private String menuType;
/** 显示状态0显示 1隐藏 */
@Schema(title = "显示状态", description = "0显示 1隐藏")
private String visible;
/** 菜单状态0正常 1停用 */
@Schema(title = "菜单状态", description = "0正常 1停用")
private String status;
/** 权限字符串 */
@Schema(title = "权限字符串")
private String perms;
/** 菜单图标 */
@Schema(title = "菜单图标")
private String icon;
/** 子菜单 */
@Schema(title = "子菜单")
private List<SysMenu> children = new ArrayList<SysMenu>();
public Long getMenuId() {
return menuId;
}
public void setMenuId(Long menuId) {
this.menuId = menuId;
}
@NotBlank(message = "菜单名称不能为空")
@Size(min = 0, max = 50, message = "菜单名称长度不能超过50个字符")
public String getMenuName() {
return menuName;
}
public void setMenuName(String menuName) {
this.menuName = menuName;
}
public String getParentName() {
return parentName;
}
public void setParentName(String parentName) {
this.parentName = parentName;
}
public Long getParentId() {
return parentId;
}
public void setParentId(Long parentId) {
this.parentId = parentId;
}
@NotNull(message = "显示顺序不能为空")
public Integer getOrderNum() {
return orderNum;
}
public void setOrderNum(Integer orderNum) {
this.orderNum = orderNum;
}
@Size(min = 0, max = 200, message = "路由地址不能超过200个字符")
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
@Size(min = 0, max = 200, message = "组件路径不能超过255个字符")
public String getComponent() {
return component;
}
public void setComponent(String component) {
this.component = component;
}
public String getQuery() {
return query;
}
public void setQuery(String query) {
this.query = query;
}
public String getRouteName() {
return routeName;
}
public void setRouteName(String routeName) {
this.routeName = routeName;
}
public String getIsFrame() {
return isFrame;
}
public void setIsFrame(String isFrame) {
this.isFrame = isFrame;
}
public String getIsCache() {
return isCache;
}
public void setIsCache(String isCache) {
this.isCache = isCache;
}
@NotBlank(message = "菜单类型不能为空")
public String getMenuType() {
return menuType;
}
public void setMenuType(String menuType) {
this.menuType = menuType;
}
public String getVisible() {
return visible;
}
public void setVisible(String visible) {
this.visible = visible;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
@Size(min = 0, max = 100, message = "权限标识长度不能超过100个字符")
public String getPerms() {
return perms;
}
public void setPerms(String perms) {
this.perms = perms;
}
public String getIcon() {
return icon;
}
public void setIcon(String icon) {
this.icon = icon;
}
public List<SysMenu> getChildren() {
return children;
}
public void setChildren(List<SysMenu> children) {
this.children = children;
}
@Override
public String toString() {
return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
.append("menuId", getMenuId())
.append("menuName", getMenuName())
.append("parentId", getParentId())
.append("orderNum", getOrderNum())
.append("path", getPath())
.append("component", getComponent())
.append("query", getQuery())
.append("routeName", getRouteName())
.append("isFrame", getIsFrame())
.append("IsCache", getIsCache())
.append("menuType", getMenuType())
.append("visible", getVisible())
.append("status ", getStatus())
.append("perms", getPerms())
.append("icon", getIcon())
.append("createBy", getCreateBy())
.append("createTime", getCreateTime())
.append("updateBy", getUpdateBy())
.append("updateTime", getUpdateTime())
.append("remark", getRemark())
.toString();
}
}

View File

@ -0,0 +1,259 @@
package com.ruoyi.common.core.domain.entity;
import java.util.Set;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.annotation.Excel.ColumnType;
import com.ruoyi.common.core.domain.BaseEntity;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
/**
* 角色表 sys_role
*
* @author ruoyi
*/
@Schema(title = "角色表")
public class SysRole extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** 角色ID */
@Schema(title = "角色ID")
@Excel(name = "角色序号", cellType = ColumnType.NUMERIC)
private Long roleId;
/** 角色名称 */
@Schema(title = "角色名称")
@Excel(name = "角色名称")
private String roleName;
/** 角色权限 */
@Schema(title = "角色权限")
@Excel(name = "角色权限")
private String roleKey;
/** 角色排序 */
@Schema(title = "角色排序")
@Excel(name = "角色排序")
private Integer roleSort;
/** 数据范围1所有数据权限2自定义数据权限3本部门数据权限4本部门及以下数据权限5仅本人数据权限 */
@Schema(title = "数据范围", description = "1=所有数据权限,2=自定义数据权限,3=本部门数据权限,4=本部门及以下数据权限,5=仅本人数据权限")
@Excel(name = "数据范围", readConverterExp = "1=所有数据权限,2=自定义数据权限,3=本部门数据权限,4=本部门及以下数据权限,5=仅本人数据权限")
private String dataScope;
/** 菜单树选择项是否关联显示( 0父子不互相关联显示 1父子互相关联显示 */
@Schema(title = "菜单树选择项是否关联显示", description = "0父子不互相关联显示 1父子互相关联显示")
private boolean menuCheckStrictly;
/** 部门树选择项是否关联显示0父子不互相关联显示 1父子互相关联显示 */
@Schema(title = "部门树选择项是否关联显示", description = "0父子不互相关联显示 1父子互相关联显示 ")
private boolean deptCheckStrictly;
/** 角色状态0正常 1停用 */
@Schema(title = "角色状态", description = "0正常 1停用")
@Excel(name = "角色状态", readConverterExp = "0=正常,1=停用")
private String status;
/** 删除标志0代表存在 2代表删除 */
@Schema(title = "删除标志", description = "0代表存在 2代表删除")
private String delFlag;
/** 用户是否存在此角色标识 默认不存在 */
@Schema(title = "用户是否存在此角色标识", description = "默认不存在")
private boolean flag = false;
/** 菜单组 */
@Schema(title = "菜单组")
private Long[] menuIds;
/** 部门组(数据权限) */
@Schema(title = "部门组", description = "数据权限")
private Long[] deptIds;
/** 角色菜单权限 */
@Schema(title = "角色菜单权限")
private Set<String> permissions;
public SysRole()
{
}
public SysRole(Long roleId)
{
this.roleId = roleId;
}
public Long getRoleId()
{
return roleId;
}
public void setRoleId(Long roleId)
{
this.roleId = roleId;
}
public boolean isAdmin()
{
return isAdmin(this.roleId);
}
public static boolean isAdmin(Long roleId)
{
return roleId != null && 1L == roleId;
}
@NotBlank(message = "角色名称不能为空")
@Size(min = 0, max = 30, message = "角色名称长度不能超过30个字符")
public String getRoleName()
{
return roleName;
}
public void setRoleName(String roleName)
{
this.roleName = roleName;
}
@NotBlank(message = "权限字符不能为空")
@Size(min = 0, max = 100, message = "权限字符长度不能超过100个字符")
public String getRoleKey()
{
return roleKey;
}
public void setRoleKey(String roleKey)
{
this.roleKey = roleKey;
}
@NotNull(message = "显示顺序不能为空")
public Integer getRoleSort()
{
return roleSort;
}
public void setRoleSort(Integer roleSort)
{
this.roleSort = roleSort;
}
public String getDataScope()
{
return dataScope;
}
public void setDataScope(String dataScope)
{
this.dataScope = dataScope;
}
public boolean isMenuCheckStrictly()
{
return menuCheckStrictly;
}
public void setMenuCheckStrictly(boolean menuCheckStrictly)
{
this.menuCheckStrictly = menuCheckStrictly;
}
public boolean isDeptCheckStrictly()
{
return deptCheckStrictly;
}
public void setDeptCheckStrictly(boolean deptCheckStrictly)
{
this.deptCheckStrictly = deptCheckStrictly;
}
public String getStatus()
{
return status;
}
public void setStatus(String status)
{
this.status = status;
}
public String getDelFlag()
{
return delFlag;
}
public void setDelFlag(String delFlag)
{
this.delFlag = delFlag;
}
public boolean isFlag()
{
return flag;
}
public void setFlag(boolean flag)
{
this.flag = flag;
}
public Long[] getMenuIds()
{
return menuIds;
}
public void setMenuIds(Long[] menuIds)
{
this.menuIds = menuIds;
}
public Long[] getDeptIds()
{
return deptIds;
}
public void setDeptIds(Long[] deptIds)
{
this.deptIds = deptIds;
}
public Set<String> getPermissions()
{
return permissions;
}
public void setPermissions(Set<String> permissions)
{
this.permissions = permissions;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
.append("roleId", getRoleId())
.append("roleName", getRoleName())
.append("roleKey", getRoleKey())
.append("roleSort", getRoleSort())
.append("dataScope", getDataScope())
.append("menuCheckStrictly", isMenuCheckStrictly())
.append("deptCheckStrictly", isDeptCheckStrictly())
.append("status", getStatus())
.append("delFlag", getDelFlag())
.append("createBy", getCreateBy())
.append("createTime", getCreateTime())
.append("updateBy", getUpdateBy())
.append("updateTime", getUpdateTime())
.append("remark", getRemark())
.toString();
}
}

View File

@ -0,0 +1,363 @@
package com.ruoyi.common.core.domain.entity;
import java.util.Date;
import java.util.List;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.annotation.Excel.ColumnType;
import com.ruoyi.common.annotation.Excel.Type;
import com.ruoyi.common.annotation.Excels;
import com.ruoyi.common.core.domain.BaseEntity;
import com.ruoyi.common.xss.Xss;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
/**
* 用户对象 sys_user
*
* @author ruoyi
*/
@Schema(title = "用户")
public class SysUser extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** 用户ID */
@Schema(title = "用户序号")
@Excel(name = "用户序号", cellType = ColumnType.NUMERIC, prompt = "用户编号")
private Long userId;
/** 部门ID */
@Schema(title = "部门编号")
@Excel(name = "部门编号", type = Type.IMPORT)
private Long deptId;
/** 用户账号 */
@Schema(title = "登录名称")
@Excel(name = "登录名称")
private String userName;
/** 用户昵称 */
@Schema(title = "用户名称")
@Excel(name = "用户名称")
private String nickName;
/** 用户邮箱 */
@Schema(title = "用户邮箱")
@Excel(name = "用户邮箱")
private String email;
/** 手机号码 */
@Schema(title = "手机号码")
@Excel(name = "手机号码")
private String phonenumber;
/** 用户性别 */
@Schema(title = "用户性别", description = "0=男,1=女,2=未知")
@Excel(name = "用户性别", readConverterExp = "0=男,1=女,2=未知")
private String sex;
/** 用户头像 */
@Schema(title = "用户头像")
private String avatar;
/** 密码 */
@Schema(title = "密码")
private String password;
/** 账号状态0正常 1停用 */
@Schema(title = "账号状态", description = "0正常 1停用")
@Excel(name = "账号状态", readConverterExp = "0=正常,1=停用")
private String status;
/** 删除标志0代表存在 2代表删除 */
@Schema(title = "删除标志", description = "0代表存在 2代表删除")
private String delFlag;
/** 最后登录IP */
@Schema(title = "最后登录IP")
@Excel(name = "最后登录IP", type = Type.EXPORT)
private String loginIp;
/** 最后登录时间 */
@Schema(title = "最后登录时间")
@Excel(name = "最后登录时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss", type = Type.EXPORT)
private Date loginDate;
/** 密码最后更新时间 */
private Date pwdUpdateDate;
/** 部门对象 */
@Schema(title = "部门对象")
@Excels({
@Excel(name = "部门名称", targetAttr = "deptName", type = Type.EXPORT),
@Excel(name = "部门负责人", targetAttr = "leader", type = Type.EXPORT)
})
private SysDept dept;
/** 角色对象 */
@Schema(title = "角色对象")
private List<SysRole> roles;
/** 角色组 */
@Schema(title = "角色组")
private Long[] roleIds;
/** 岗位组 */
@Schema(title = "岗位组")
private Long[] postIds;
/** 角色ID */
@Schema(title = "角色ID")
private Long roleId;
public SysUser()
{
}
public SysUser(Long userId)
{
this.userId = userId;
}
public Long getUserId()
{
return userId;
}
public void setUserId(Long userId)
{
this.userId = userId;
}
public boolean isAdmin()
{
return isAdmin(this.userId);
}
public static boolean isAdmin(Long userId)
{
return userId != null && 1L == userId;
}
public Long getDeptId()
{
return deptId;
}
public void setDeptId(Long deptId)
{
this.deptId = deptId;
}
@Xss(message = "用户昵称不能包含脚本字符")
@Size(min = 0, max = 30, message = "用户昵称长度不能超过30个字符")
public String getNickName()
{
return nickName;
}
public void setNickName(String nickName)
{
this.nickName = nickName;
}
@Xss(message = "用户账号不能包含脚本字符")
@NotBlank(message = "用户账号不能为空")
@Size(min = 0, max = 30, message = "用户账号长度不能超过30个字符")
public String getUserName()
{
return userName;
}
public void setUserName(String userName)
{
this.userName = userName;
}
@Email(message = "邮箱格式不正确")
@Size(min = 0, max = 50, message = "邮箱长度不能超过50个字符")
public String getEmail()
{
return email;
}
public void setEmail(String email)
{
this.email = email;
}
@Size(min = 0, max = 11, message = "手机号码长度不能超过11个字符")
public String getPhonenumber()
{
return phonenumber;
}
public void setPhonenumber(String phonenumber)
{
this.phonenumber = phonenumber;
}
public String getSex()
{
return sex;
}
public void setSex(String sex)
{
this.sex = sex;
}
public String getAvatar()
{
return avatar;
}
public void setAvatar(String avatar)
{
this.avatar = avatar;
}
public String getPassword()
{
return password;
}
public void setPassword(String password)
{
this.password = password;
}
public String getStatus()
{
return status;
}
public void setStatus(String status)
{
this.status = status;
}
public String getDelFlag()
{
return delFlag;
}
public void setDelFlag(String delFlag)
{
this.delFlag = delFlag;
}
public String getLoginIp()
{
return loginIp;
}
public void setLoginIp(String loginIp)
{
this.loginIp = loginIp;
}
public Date getLoginDate()
{
return loginDate;
}
public void setLoginDate(Date loginDate)
{
this.loginDate = loginDate;
}
public Date getPwdUpdateDate()
{
return pwdUpdateDate;
}
public void setPwdUpdateDate(Date pwdUpdateDate)
{
this.pwdUpdateDate = pwdUpdateDate;
}
public SysDept getDept()
{
return dept;
}
public void setDept(SysDept dept)
{
this.dept = dept;
}
public List<SysRole> getRoles()
{
return roles;
}
public void setRoles(List<SysRole> roles)
{
this.roles = roles;
}
public Long[] getRoleIds()
{
return roleIds;
}
public void setRoleIds(Long[] roleIds)
{
this.roleIds = roleIds;
}
public Long[] getPostIds()
{
return postIds;
}
public void setPostIds(Long[] postIds)
{
this.postIds = postIds;
}
public Long getRoleId()
{
return roleId;
}
public void setRoleId(Long roleId)
{
this.roleId = roleId;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
.append("userId", getUserId())
.append("deptId", getDeptId())
.append("userName", getUserName())
.append("nickName", getNickName())
.append("email", getEmail())
.append("phonenumber", getPhonenumber())
.append("sex", getSex())
.append("avatar", getAvatar())
.append("password", getPassword())
.append("status", getStatus())
.append("delFlag", getDelFlag())
.append("loginIp", getLoginIp())
.append("loginDate", getLoginDate())
.append("pwdUpdateDate", getPwdUpdateDate())
.append("createBy", getCreateBy())
.append("createTime", getCreateTime())
.append("updateBy", getUpdateBy())
.append("updateTime", getUpdateTime())
.append("remark", getRemark())
.append("dept", getDept())
.toString();
}
}

View File

@ -0,0 +1,97 @@
package com.ruoyi.common.core.domain.model;
import com.ruoyi.common.core.domain.BaseEntity;
import io.swagger.v3.oas.annotations.media.Schema;
/**
* 用户登录对象
*
* @author ruoyi
*/
@Schema(title = "用户登录对象")
public class LoginBody extends BaseEntity {
/**
* 用户名
*/
@Schema(title = "用户名")
private String username;
/**
* 用户密码
*/
@Schema(title = "用户密码")
private String password;
/**
* 手机号码
*/
@Schema(title = "手机号码")
private String phonenumber;
/**
* 邮箱
*/
@Schema(title = "邮箱")
private String email;
/**
* 验证码
*/
@Schema(title = "验证码")
private String code;
/**
* 唯一标识
*/
@Schema(title = "唯一标识")
private String uuid;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getUuid() {
return uuid;
}
public void setUuid(String uuid) {
this.uuid = uuid;
}
public String getPhonenumber() {
return phonenumber;
}
public void setPhonenumber(String phonenumber) {
this.phonenumber = phonenumber;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}

View File

@ -0,0 +1,266 @@
package com.ruoyi.common.core.domain.model;
import java.util.Collection;
import java.util.Set;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import com.alibaba.fastjson2.annotation.JSONField;
import com.ruoyi.common.core.domain.entity.SysUser;
/**
* 登录用户身份权限
*
* @author ruoyi
*/
public class LoginUser implements UserDetails
{
private static final long serialVersionUID = 1L;
/**
* 用户ID
*/
private Long userId;
/**
* 部门ID
*/
private Long deptId;
/**
* 用户唯一标识
*/
private String token;
/**
* 登录时间
*/
private Long loginTime;
/**
* 过期时间
*/
private Long expireTime;
/**
* 登录IP地址
*/
private String ipaddr;
/**
* 登录地点
*/
private String loginLocation;
/**
* 浏览器类型
*/
private String browser;
/**
* 操作系统
*/
private String os;
/**
* 权限列表
*/
private Set<String> permissions;
/**
* 用户信息
*/
private SysUser user;
public Long getUserId()
{
return userId;
}
public void setUserId(Long userId)
{
this.userId = userId;
}
public Long getDeptId()
{
return deptId;
}
public void setDeptId(Long deptId)
{
this.deptId = deptId;
}
public String getToken()
{
return token;
}
public void setToken(String token)
{
this.token = token;
}
public LoginUser()
{
}
public LoginUser(SysUser user, Set<String> permissions)
{
this.user = user;
this.permissions = permissions;
}
public LoginUser(Long userId, Long deptId, SysUser user, Set<String> permissions)
{
this.userId = userId;
this.deptId = deptId;
this.user = user;
this.permissions = permissions;
}
@JSONField(serialize = false)
@Override
public String getPassword()
{
return user.getPassword();
}
@Override
public String getUsername()
{
return user.getUserName();
}
/**
* 账户是否未过期,过期无法验证
*/
@JSONField(serialize = false)
@Override
public boolean isAccountNonExpired()
{
return true;
}
/**
* 指定用户是否解锁,锁定的用户无法进行身份验证
*
* @return
*/
@JSONField(serialize = false)
@Override
public boolean isAccountNonLocked()
{
return true;
}
/**
* 指示是否已过期的用户的凭据(密码),过期的凭据防止认证
*
* @return
*/
@JSONField(serialize = false)
@Override
public boolean isCredentialsNonExpired()
{
return true;
}
/**
* 是否可用 ,禁用的用户不能身份验证
*
* @return
*/
@JSONField(serialize = false)
@Override
public boolean isEnabled()
{
return true;
}
public Long getLoginTime()
{
return loginTime;
}
public void setLoginTime(Long loginTime)
{
this.loginTime = loginTime;
}
public String getIpaddr()
{
return ipaddr;
}
public void setIpaddr(String ipaddr)
{
this.ipaddr = ipaddr;
}
public String getLoginLocation()
{
return loginLocation;
}
public void setLoginLocation(String loginLocation)
{
this.loginLocation = loginLocation;
}
public String getBrowser()
{
return browser;
}
public void setBrowser(String browser)
{
this.browser = browser;
}
public String getOs()
{
return os;
}
public void setOs(String os)
{
this.os = os;
}
public Long getExpireTime()
{
return expireTime;
}
public void setExpireTime(Long expireTime)
{
this.expireTime = expireTime;
}
public Set<String> getPermissions()
{
return permissions;
}
public void setPermissions(Set<String> permissions)
{
this.permissions = permissions;
}
public SysUser getUser()
{
return user;
}
public void setUser(SysUser user)
{
this.user = user;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities()
{
return null;
}
}

View File

@ -0,0 +1,14 @@
package com.ruoyi.common.core.domain.model;
import io.swagger.v3.oas.annotations.media.Schema;
/**
* 用户注册对象
*
* @author ruoyi
*/
@Schema(title = "用户注册对象")
public class RegisterBody extends LoginBody
{
}

View File

@ -0,0 +1,101 @@
package com.ruoyi.common.core.page;
import com.ruoyi.common.utils.StringUtils;
/**
* 分页数据
*
* @author ruoyi
*/
public class PageDomain
{
/** 当前记录起始索引 */
private Integer pageNum;
/** 每页显示记录数 */
private Integer pageSize;
/** 排序列 */
private String orderByColumn;
/** 排序的方向desc或者asc */
private String isAsc = "asc";
/** 分页参数合理化 */
private Boolean reasonable = true;
public String getOrderBy()
{
if (StringUtils.isEmpty(orderByColumn))
{
return "";
}
return StringUtils.toUnderScoreCase(orderByColumn) + " " + isAsc;
}
public Integer getPageNum()
{
return pageNum;
}
public void setPageNum(Integer pageNum)
{
this.pageNum = pageNum;
}
public Integer getPageSize()
{
return pageSize;
}
public void setPageSize(Integer pageSize)
{
this.pageSize = pageSize;
}
public String getOrderByColumn()
{
return orderByColumn;
}
public void setOrderByColumn(String orderByColumn)
{
this.orderByColumn = orderByColumn;
}
public String getIsAsc()
{
return isAsc;
}
public void setIsAsc(String isAsc)
{
if (StringUtils.isNotEmpty(isAsc))
{
// 兼容前端排序类型
if ("ascending".equals(isAsc))
{
isAsc = "asc";
}
else if ("descending".equals(isAsc))
{
isAsc = "desc";
}
this.isAsc = isAsc;
}
}
public Boolean getReasonable()
{
if (StringUtils.isNull(reasonable))
{
return Boolean.TRUE;
}
return reasonable;
}
public void setReasonable(Boolean reasonable)
{
this.reasonable = reasonable;
}
}

View File

@ -0,0 +1,92 @@
package com.ruoyi.common.core.page;
import java.io.Serializable;
import java.util.List;
import io.swagger.v3.oas.annotations.media.Schema;
/**
* 表格分页数据对象
*
* @author ruoyi
*/
@Schema(title = "表格分页数据对象")
public class TableDataInfo implements Serializable
{
private static final long serialVersionUID = 1L;
/** 总记录数 */
@Schema(title = "总记录数")
private long total;
/** 列表数据 */
@Schema(title = "列表数据")
private List<?> rows;
/** 消息状态码 */
@Schema(title = "消息状态码")
private int code;
/** 消息内容 */
@Schema(title = "消息内容")
private String msg;
/**
* 表格数据对象
*/
public TableDataInfo()
{
}
/**
* 分页
*
* @param list 列表数据
* @param total 总记录数
*/
public TableDataInfo(List<?> list, int total)
{
this.rows = list;
this.total = total;
}
public long getTotal()
{
return total;
}
public void setTotal(long total)
{
this.total = total;
}
public List<?> getRows()
{
return rows;
}
public void setRows(List<?> rows)
{
this.rows = rows;
}
public int getCode()
{
return code;
}
public void setCode(int code)
{
this.code = code;
}
public String getMsg()
{
return msg;
}
public void setMsg(String msg)
{
this.msg = msg;
}
}

View File

@ -0,0 +1,56 @@
package com.ruoyi.common.core.page;
import com.ruoyi.common.core.text.Convert;
import com.ruoyi.common.utils.ServletUtils;
/**
* 表格数据处理
*
* @author ruoyi
*/
public class TableSupport
{
/**
* 当前记录起始索引
*/
public static final String PAGE_NUM = "pageNum";
/**
* 每页显示记录数
*/
public static final String PAGE_SIZE = "pageSize";
/**
* 排序列
*/
public static final String ORDER_BY_COLUMN = "orderByColumn";
/**
* 排序的方向 "desc" 或者 "asc".
*/
public static final String IS_ASC = "isAsc";
/**
* 分页参数合理化
*/
public static final String REASONABLE = "reasonable";
/**
* 封装分页对象
*/
public static PageDomain getPageDomain()
{
PageDomain pageDomain = new PageDomain();
pageDomain.setPageNum(Convert.toInt(ServletUtils.getParameter(PAGE_NUM), 1));
pageDomain.setPageSize(Convert.toInt(ServletUtils.getParameter(PAGE_SIZE), 10));
pageDomain.setOrderByColumn(ServletUtils.getParameter(ORDER_BY_COLUMN));
pageDomain.setIsAsc(ServletUtils.getParameter(IS_ASC));
pageDomain.setReasonable(ServletUtils.getParameterToBool(REASONABLE));
return pageDomain;
}
public static PageDomain buildPageRequest()
{
return getPageDomain();
}
}

View File

@ -0,0 +1,56 @@
package com.ruoyi.common.core.security.service;
public interface IPermissionService {
/**
* 验证用户是否具备某权限
*
* @param permission 权限字符串
* @return 用户是否具备某权限
*/
boolean hasPermi(String permission);
/**
* 验证用户是否不具备某权限 hasPermi 逻辑相反
*
* @param permission 权限字符串
* @return 用户是否不具备某权限
*/
default boolean lacksPermi(String permission) {
return !hasPermi(permission);
}
/**
* 验证用户是否具有以下任意一个权限
*
* @param permissions PERMISSION_DELIMETER 为分隔符的权限列表
* @return 用户是否具有以下任意一个权限
*/
boolean hasAnyPermi(String permissions);
/**
* 判断用户是否拥有某个角色
*
* @param role 角色字符串
* @return 用户是否具备某角色
*/
boolean hasRole(String role);
/**
* 验证用户是否不具备某角色 hasRole 逻辑相反
*
* @param role 角色名称
* @return 用户是否不具备某角色
*/
default boolean lacksRole(String role) {
return !hasRole(role);
}
/**
* 验证用户是否具有以下任意一个角色
*
* @param roles ROLE_DELIMETER 为分隔符的角色列表
* @return 用户是否具有以下任意一个角色
*/
boolean hasAnyRoles(String roles);
}

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