| | |
| | | *.class |
| | | ###################################################################### |
| | | # Build Tools |
| | | |
| | | # Mobile Tools for Java (J2ME) |
| | | .mtj.tmp/ |
| | | .gradle |
| | | /build/ |
| | | !gradle/wrapper/gradle-wrapper.jar |
| | | |
| | | # Package Files # |
| | | *.jar |
| | | *.war |
| | | *.ear |
| | | target/ |
| | | !.mvn/wrapper/maven-wrapper.jar |
| | | |
| | | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml |
| | | hs_err_pid* |
| | | ###################################################################### |
| | | # 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 |
| | | |
| | | !*/build/*.java |
| | | !*/build/*.html |
| | | !*/build/*.xml |
New file |
| | |
| | | MIT License |
| | | |
| | | Copyright (c) 2020 若依 |
| | | |
| | | 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. |
| | |
| | | ## JiaDianHuiShou |
| | | <p align="center"> |
| | | <img alt="logo" src="https://oscimg.oschina.net/oscnet/up-b99b286755aef70355a7084753f89cdb7c9.png"> |
| | | </p> |
| | | <h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">RuoYi v3.6.4</h1> |
| | | <h4 align="center">基于 Vue/Element UI 和 Spring Boot/Spring Cloud & Alibaba 前后端分离的分布式微服务架构</h4> |
| | | <p align="center"> |
| | | <a href="https://gitee.com/y_project/RuoYi-Cloud/stargazers"><img src="https://gitee.com/y_project/RuoYi-Cloud/badge/star.svg?theme=dark"></a> |
| | | <a href="https://gitee.com/y_project/RuoYi-Cloud"><img src="https://img.shields.io/badge/RuoYi-v3.6.4-brightgreen.svg"></a> |
| | | <a href="https://gitee.com/y_project/RuoYi-Cloud/blob/master/LICENSE"><img src="https://img.shields.io/github/license/mashape/apistatus.svg"></a> |
| | | </p> |
| | | |
| | | 二手家电回收 |
| | | ## 平台简介 |
| | | |
| | | 若依是一套全部开源的快速开发平台,毫无保留给个人及企业免费使用。 |
| | | |
| | | * 采用前后端分离的模式,微服务版本前端(基于 [RuoYi-Vue](https://gitee.com/y_project/RuoYi-Vue))。 |
| | | * 后端采用Spring Boot、Spring Cloud & Alibaba。 |
| | | * 注册中心、配置中心选型Nacos,权限认证使用Redis。 |
| | | * 流量控制框架选型Sentinel,分布式事务选型Seata。 |
| | | * 提供了技术栈([Vue3](https://v3.cn.vuejs.org) [Element Plus](https://element-plus.org/zh-CN) [Vite](https://cn.vitejs.dev))版本[RuoYi-Cloud-Vue3](https://github.com/yangzongzhuan/RuoYi-Cloud-Vue3),保持同步更新。 |
| | | * 如需不分离应用,请移步 [RuoYi](https://gitee.com/y_project/RuoYi),如需分离应用,请移步 [RuoYi-Vue](https://gitee.com/y_project/RuoYi-Vue) |
| | | * 阿里云折扣场:[点我进入](http://aly.ruoyi.vip),腾讯云秒杀场:[点我进入](http://txy.ruoyi.vip) |
| | | * 阿里云优惠券:[点我领取](https://www.aliyun.com/minisite/goods?userCode=brki8iof&share_source=copy_link),腾讯云优惠券:[点我领取](https://cloud.tencent.com/redirect.php?redirect=1025&cps_key=198c8df2ed259157187173bc7f4f32fd&from=console) |
| | | |
| | | #### 友情链接 [若依/RuoYi-Cloud](https://gitee.com/zhangmrit/ruoyi-cloud) Ant Design版本。 |
| | | |
| | | ## 系统模块 |
| | | |
| | | ~~~ |
| | | com.ruoyi |
| | | ├── ruoyi-ui // 前端框架 [80] |
| | | ├── ruoyi-gateway // 网关模块 [8080] |
| | | ├── ruoyi-auth // 认证中心 [9200] |
| | | ├── ruoyi-api // 接口模块 |
| | | │ └── ruoyi-api-system // 系统接口 |
| | | ├── ruoyi-common // 通用模块 |
| | | │ └── ruoyi-common-core // 核心模块 |
| | | │ └── ruoyi-common-datascope // 权限范围 |
| | | │ └── ruoyi-common-datasource // 多数据源 |
| | | │ └── ruoyi-common-log // 日志记录 |
| | | │ └── ruoyi-common-redis // 缓存服务 |
| | | │ └── ruoyi-common-seata // 分布式事务 |
| | | │ └── ruoyi-common-security // 安全模块 |
| | | │ └── ruoyi-common-swagger // 系统接口 |
| | | ├── ruoyi-modules // 业务模块 |
| | | │ └── ruoyi-system // 系统模块 [9201] |
| | | │ └── ruoyi-gen // 代码生成 [9202] |
| | | │ └── ruoyi-job // 定时任务 [9203] |
| | | │ └── ruoyi-file // 文件服务 [9300] |
| | | ├── ruoyi-visual // 图形化管理模块 |
| | | │ └── ruoyi-visual-monitor // 监控中心 [9100] |
| | | ├──pom.xml // 公共依赖 |
| | | ~~~ |
| | | |
| | | ## 架构图 |
| | | |
| | | <img src="https://oscimg.oschina.net/oscnet/up-82e9722ecb846786405a904bafcf19f73f3.png"/> |
| | | |
| | | ## 内置功能 |
| | | |
| | | 1. 用户管理:用户是系统操作者,该功能主要完成系统用户配置。 |
| | | 2. 部门管理:配置系统组织机构(公司、部门、小组),树结构展现支持数据权限。 |
| | | 3. 岗位管理:配置系统用户所属担任职务。 |
| | | 4. 菜单管理:配置系统菜单,操作权限,按钮权限标识等。 |
| | | 5. 角色管理:角色菜单权限分配、设置角色按机构进行数据范围权限划分。 |
| | | 6. 字典管理:对系统中经常使用的一些较为固定的数据进行维护。 |
| | | 7. 参数管理:对系统动态配置常用参数。 |
| | | 8. 通知公告:系统通知公告信息发布维护。 |
| | | 9. 操作日志:系统正常操作日志记录和查询;系统异常信息日志记录和查询。 |
| | | 10. 登录日志:系统登录日志记录查询包含登录异常。 |
| | | 11. 在线用户:当前系统中活跃用户状态监控。 |
| | | 12. 定时任务:在线(添加、修改、删除)任务调度包含执行结果日志。 |
| | | 13. 代码生成:前后端代码的生成(java、html、xml、sql)支持CRUD下载 。 |
| | | 14. 系统接口:根据业务代码自动生成相关的api接口文档。 |
| | | 15. 服务监控:监视当前系统CPU、内存、磁盘、堆栈等相关信息。 |
| | | 16. 在线构建器:拖动表单元素生成相应的HTML代码。 |
| | | 17. 连接池监视:监视当前系统数据库连接池状态,可进行分析SQL找出系统性能瓶颈。 |
| | | |
| | | ## 在线体验 |
| | | |
| | | - admin/admin123 |
| | | - 陆陆续续收到一些打赏,为了更好的体验已用于演示服务器升级。谢谢各位小伙伴。 |
| | | |
| | | 演示地址:http://ruoyi.vip |
| | | 文档地址:http://doc.ruoyi.vip |
| | | |
| | | ## 演示图 |
| | | |
| | | <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-4148b24f58660a9dc347761e4cf6162f28f.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/up-ece3fd37a3d4bb75a3926e905a3c5629055.png"/></td> |
| | | <td><img src="https://oscimg.oschina.net/oscnet/up-92ffb7f3835855cff100fa0f754a6be0d99.png"/></td> |
| | | </tr> |
| | | <tr> |
| | | <td><img src="https://oscimg.oschina.net/oscnet/up-ff9e3066561574aca73005c5730c6a41f15.png"/></td> |
| | | <td><img src="https://oscimg.oschina.net/oscnet/up-5e4daac0bb59612c5038448acbcef235e3a.png"/></td> |
| | | </tr> |
| | | </table> |
| | | |
| | | |
| | | ## 若依微服务交流群 |
| | | |
| | | QQ群: [](https://jq.qq.com/?_wv=1027&k=yqInfq0S) [](https://jq.qq.com/?_wv=1027&k=Oy1mb3p8) [](https://jq.qq.com/?_wv=1027&k=rvxkJtXK) [](https://jq.qq.com/?_wv=1027&k=0Ck3PvTe) [](https://jq.qq.com/?_wv=1027&k=FnHHP4TT) [](https://jq.qq.com/?_wv=1027&k=qdT1Ojpz) [](https://jq.qq.com/?_wv=1027&k=nw3OiyXs) [](https://jq.qq.com/?_wv=1027&k=kiU5WDls) [](https://jq.qq.com/?_wv=1027&k=MtBy6YfT) [](https://jq.qq.com/?_wv=1027&k=FqImHgH2) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=G4jZ4EtdT50PhnMBudTnEwgonxkXOscJ&authKey=FkGHYfoTKlGE6wHdKdjH9bVoOgQjtLP9WM%2Fj7pqGY1msoqw9uxDiBo39E2mLgzYg&noverify=0&group_code=128355254) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=irnwcXhbLOQEv1g-TwGifjNTA_f4wZiA&authKey=4bpzEwhcUY%2FvsPDHvzYn6xfoS%2FtOArvZ%2BGXzfr7O0%2FEqLfkKA%2BuCDXlzHIFg8t93&noverify=0&group_code=179219821) 点击按钮入群。 |
New file |
| | |
| | | @echo off |
| | | echo. |
| | | echo [ÐÅÏ¢] ÇåÀí¹¤³ÌtargetÉú³É·¾¶¡£ |
| | | echo. |
| | | |
| | | %~d0 |
| | | cd %~dp0 |
| | | |
| | | cd .. |
| | | call mvn clean |
| | | |
| | | pause |
New file |
| | |
| | | @echo off |
| | | echo. |
| | | echo [ÐÅÏ¢] ´ò°üWeb¹¤³Ì£¬Éú³Éwar/jar°üÎļþ¡£ |
| | | echo. |
| | | |
| | | %~d0 |
| | | cd %~dp0 |
| | | |
| | | cd .. |
| | | call mvn clean package -Dmaven.test.skip=true |
| | | |
| | | pause |
New file |
| | |
| | | @echo off |
| | | echo. |
| | | echo [ÐÅÏ¢] ʹÓÃJarÃüÁîÔËÐÐAuth¹¤³Ì¡£ |
| | | echo. |
| | | |
| | | cd %~dp0 |
| | | cd ../ruoyi-auth/target |
| | | |
| | | set JAVA_OPTS=-Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m |
| | | |
| | | java -Dfile.encoding=utf-8 %JAVA_OPTS% -jar ruoyi-auth.jar |
| | | |
| | | cd bin |
| | | pause |
New file |
| | |
| | | @echo off |
| | | echo. |
| | | echo [ÐÅÏ¢] ʹÓÃJarÃüÁîÔËÐÐGateway¹¤³Ì¡£ |
| | | echo. |
| | | |
| | | cd %~dp0 |
| | | cd ../ruoyi-gateway/target |
| | | |
| | | set JAVA_OPTS=-Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m |
| | | |
| | | java -Dfile.encoding=utf-8 %JAVA_OPTS% -jar ruoyi-gateway.jar |
| | | |
| | | cd bin |
| | | pause |
New file |
| | |
| | | @echo off |
| | | echo. |
| | | echo [ÐÅÏ¢] ʹÓÃJarÃüÁîÔËÐÐModules-File¹¤³Ì¡£ |
| | | echo. |
| | | |
| | | cd %~dp0 |
| | | cd ../ruoyi-modules/ruoyi-file/target |
| | | |
| | | set JAVA_OPTS=-Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m |
| | | |
| | | java -Dfile.encoding=utf-8 %JAVA_OPTS% -jar ruoyi-modules-file.jar |
| | | |
| | | cd bin |
| | | pause |
New file |
| | |
| | | @echo off |
| | | echo. |
| | | echo [ÐÅÏ¢] ʹÓÃJarÃüÁîÔËÐÐModules-Gen¹¤³Ì¡£ |
| | | echo. |
| | | |
| | | cd %~dp0 |
| | | cd ../ruoyi-modules/ruoyi-gen/target |
| | | |
| | | set JAVA_OPTS=-Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m |
| | | |
| | | java -Dfile.encoding=utf-8 %JAVA_OPTS% -jar ruoyi-modules-gen.jar |
| | | |
| | | cd bin |
| | | pause |
New file |
| | |
| | | @echo off |
| | | echo. |
| | | echo [ÐÅÏ¢] ʹÓÃJarÃüÁîÔËÐÐModules-Job¹¤³Ì¡£ |
| | | echo. |
| | | |
| | | cd %~dp0 |
| | | cd ../ruoyi-modules/ruoyi-job/target |
| | | |
| | | set JAVA_OPTS=-Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m |
| | | |
| | | java -Dfile.encoding=utf-8 %JAVA_OPTS% -jar ruoyi-modules-job.jar |
| | | |
| | | cd bin |
| | | pause |
New file |
| | |
| | | @echo off |
| | | echo. |
| | | echo [ÐÅÏ¢] ʹÓÃJarÃüÁîÔËÐÐModules-System¹¤³Ì¡£ |
| | | echo. |
| | | |
| | | cd %~dp0 |
| | | cd ../ruoyi-modules/ruoyi-system/target |
| | | |
| | | set JAVA_OPTS=-Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m |
| | | |
| | | java -Dfile.encoding=utf-8 %JAVA_OPTS% -jar ruoyi-modules-system.jar |
| | | |
| | | cd bin |
| | | pause |
New file |
| | |
| | | @echo off |
| | | echo. |
| | | echo [ÐÅÏ¢] ʹÓÃJarÃüÁîÔËÐÐMonitor¹¤³Ì¡£ |
| | | echo. |
| | | |
| | | cd %~dp0 |
| | | cd ../ruoyi-visual/ruoyi-monitor/target |
| | | |
| | | set JAVA_OPTS=-Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m |
| | | |
| | | java -Dfile.encoding=utf-8 %JAVA_OPTS% -jar ruoyi-visual-monitor.jar |
| | | |
| | | cd bin |
| | | pause |
New file |
| | |
| | | #!/bin/sh |
| | | |
| | | # 复制项目的文件到对应docker路径,便于一键生成镜像。 |
| | | usage() { |
| | | echo "Usage: sh copy.sh" |
| | | exit 1 |
| | | } |
| | | |
| | | |
| | | # copy sql |
| | | echo "begin copy sql " |
| | | cp ../sql/ry_20231130.sql ./mysql/db |
| | | cp ../sql/ry_config_20231204.sql ./mysql/db |
| | | |
| | | # copy html |
| | | echo "begin copy html " |
| | | cp -r ../ruoyi-ui/dist/** ./nginx/html/dist |
| | | |
| | | |
| | | # copy jar |
| | | echo "begin copy ruoyi-gateway " |
| | | cp ../ruoyi-gateway/target/ruoyi-gateway.jar ./ruoyi/gateway/jar |
| | | |
| | | echo "begin copy ruoyi-auth " |
| | | cp ../ruoyi-auth/target/ruoyi-auth.jar ./ruoyi/auth/jar |
| | | |
| | | echo "begin copy ruoyi-visual " |
| | | cp ../ruoyi-visual/ruoyi-monitor/target/ruoyi-visual-monitor.jar ./ruoyi/visual/monitor/jar |
| | | |
| | | echo "begin copy ruoyi-modules-system " |
| | | cp ../ruoyi-modules/ruoyi-system/target/ruoyi-modules-system.jar ./ruoyi/modules/system/jar |
| | | |
| | | echo "begin copy ruoyi-modules-file " |
| | | cp ../ruoyi-modules/ruoyi-file/target/ruoyi-modules-file.jar ./ruoyi/modules/file/jar |
| | | |
| | | echo "begin copy ruoyi-modules-job " |
| | | cp ../ruoyi-modules/ruoyi-job/target/ruoyi-modules-job.jar ./ruoyi/modules/job/jar |
| | | |
| | | echo "begin copy ruoyi-modules-gen " |
| | | cp ../ruoyi-modules/ruoyi-gen/target/ruoyi-modules-gen.jar ./ruoyi/modules/gen/jar |
| | | |
New file |
| | |
| | | #!/bin/sh |
| | | |
| | | # 使用说明,用来提示输入参数 |
| | | usage() { |
| | | echo "Usage: sh 执行脚本.sh [port|base|modules|stop|rm]" |
| | | exit 1 |
| | | } |
| | | |
| | | # 开启所需端口 |
| | | port(){ |
| | | firewall-cmd --add-port=80/tcp --permanent |
| | | firewall-cmd --add-port=8080/tcp --permanent |
| | | firewall-cmd --add-port=8848/tcp --permanent |
| | | firewall-cmd --add-port=9848/tcp --permanent |
| | | firewall-cmd --add-port=9849/tcp --permanent |
| | | firewall-cmd --add-port=6379/tcp --permanent |
| | | firewall-cmd --add-port=3306/tcp --permanent |
| | | firewall-cmd --add-port=9100/tcp --permanent |
| | | firewall-cmd --add-port=9200/tcp --permanent |
| | | firewall-cmd --add-port=9201/tcp --permanent |
| | | firewall-cmd --add-port=9202/tcp --permanent |
| | | firewall-cmd --add-port=9203/tcp --permanent |
| | | firewall-cmd --add-port=9300/tcp --permanent |
| | | service firewalld restart |
| | | } |
| | | |
| | | # 启动基础环境(必须) |
| | | base(){ |
| | | docker-compose up -d ruoyi-mysql ruoyi-redis ruoyi-nacos |
| | | } |
| | | |
| | | # 启动程序模块(必须) |
| | | modules(){ |
| | | docker-compose up -d ruoyi-nginx ruoyi-gateway ruoyi-auth ruoyi-modules-system |
| | | } |
| | | |
| | | # 关闭所有环境/模块 |
| | | stop(){ |
| | | docker-compose stop |
| | | } |
| | | |
| | | # 删除所有环境/模块 |
| | | rm(){ |
| | | docker-compose rm |
| | | } |
| | | |
| | | # 根据输入参数,选择执行对应方法,不输入则执行使用说明 |
| | | case "$1" in |
| | | "port") |
| | | port |
| | | ;; |
| | | "base") |
| | | base |
| | | ;; |
| | | "modules") |
| | | modules |
| | | ;; |
| | | "stop") |
| | | stop |
| | | ;; |
| | | "rm") |
| | | rm |
| | | ;; |
| | | *) |
| | | usage |
| | | ;; |
| | | esac |
New file |
| | |
| | | version : '3.8' |
| | | services: |
| | | ruoyi-nacos: |
| | | container_name: ruoyi-nacos |
| | | image: nacos/nacos-server |
| | | build: |
| | | context: ./nacos |
| | | environment: |
| | | - MODE=standalone |
| | | volumes: |
| | | - ./nacos/logs/:/home/nacos/logs |
| | | - ./nacos/conf/application.properties:/home/nacos/conf/application.properties |
| | | ports: |
| | | - "8848:8848" |
| | | - "9848:9848" |
| | | - "9849:9849" |
| | | depends_on: |
| | | - ruoyi-mysql |
| | | ruoyi-mysql: |
| | | container_name: ruoyi-mysql |
| | | image: mysql:5.7 |
| | | build: |
| | | context: ./mysql |
| | | ports: |
| | | - "3306:3306" |
| | | volumes: |
| | | - ./mysql/conf:/etc/mysql/conf.d |
| | | - ./mysql/logs:/logs |
| | | - ./mysql/data:/var/lib/mysql |
| | | command: [ |
| | | 'mysqld', |
| | | '--innodb-buffer-pool-size=80M', |
| | | '--character-set-server=utf8mb4', |
| | | '--collation-server=utf8mb4_unicode_ci', |
| | | '--default-time-zone=+8:00', |
| | | '--lower-case-table-names=1' |
| | | ] |
| | | environment: |
| | | MYSQL_DATABASE: 'ry-cloud' |
| | | MYSQL_ROOT_PASSWORD: password |
| | | ruoyi-redis: |
| | | container_name: ruoyi-redis |
| | | image: redis |
| | | build: |
| | | context: ./redis |
| | | ports: |
| | | - "6379:6379" |
| | | volumes: |
| | | - ./redis/conf/redis.conf:/home/ruoyi/redis/redis.conf |
| | | - ./redis/data:/data |
| | | command: redis-server /home/ruoyi/redis/redis.conf |
| | | ruoyi-nginx: |
| | | container_name: ruoyi-nginx |
| | | image: nginx |
| | | build: |
| | | context: ./nginx |
| | | ports: |
| | | - "80:80" |
| | | volumes: |
| | | - ./nginx/html/dist:/home/ruoyi/projects/ruoyi-ui |
| | | - ./nginx/conf/nginx.conf:/etc/nginx/nginx.conf |
| | | - ./nginx/logs:/var/log/nginx |
| | | - ./nginx/conf.d:/etc/nginx/conf.d |
| | | depends_on: |
| | | - ruoyi-gateway |
| | | links: |
| | | - ruoyi-gateway |
| | | ruoyi-gateway: |
| | | container_name: ruoyi-gateway |
| | | build: |
| | | context: ./ruoyi/gateway |
| | | dockerfile: dockerfile |
| | | ports: |
| | | - "8080:8080" |
| | | depends_on: |
| | | - ruoyi-redis |
| | | links: |
| | | - ruoyi-redis |
| | | ruoyi-auth: |
| | | container_name: ruoyi-auth |
| | | build: |
| | | context: ./ruoyi/auth |
| | | dockerfile: dockerfile |
| | | ports: |
| | | - "9200:9200" |
| | | depends_on: |
| | | - ruoyi-redis |
| | | links: |
| | | - ruoyi-redis |
| | | ruoyi-modules-system: |
| | | container_name: ruoyi-modules-system |
| | | build: |
| | | context: ./ruoyi/modules/system |
| | | dockerfile: dockerfile |
| | | ports: |
| | | - "9201:9201" |
| | | depends_on: |
| | | - ruoyi-redis |
| | | - ruoyi-mysql |
| | | links: |
| | | - ruoyi-redis |
| | | - ruoyi-mysql |
| | | ruoyi-modules-gen: |
| | | container_name: ruoyi-modules-gen |
| | | build: |
| | | context: ./ruoyi/modules/gen |
| | | dockerfile: dockerfile |
| | | ports: |
| | | - "9202:9202" |
| | | depends_on: |
| | | - ruoyi-mysql |
| | | links: |
| | | - ruoyi-mysql |
| | | ruoyi-modules-job: |
| | | container_name: ruoyi-modules-job |
| | | build: |
| | | context: ./ruoyi/modules/job |
| | | dockerfile: dockerfile |
| | | ports: |
| | | - "9203:9203" |
| | | depends_on: |
| | | - ruoyi-mysql |
| | | links: |
| | | - ruoyi-mysql |
| | | ruoyi-modules-file: |
| | | container_name: ruoyi-modules-file |
| | | build: |
| | | context: ./ruoyi/modules/file |
| | | dockerfile: dockerfile |
| | | ports: |
| | | - "9300:9300" |
| | | volumes: |
| | | - ./ruoyi/uploadPath:/home/ruoyi/uploadPath |
| | | ruoyi-visual-monitor: |
| | | container_name: ruoyi-visual-monitor |
| | | build: |
| | | context: ./ruoyi/visual/monitor |
| | | dockerfile: dockerfile |
| | | ports: |
| | | - "9100:9100" |
New file |
| | |
| | | ´æ·ÅsqlĿ¼ÏµÄËùÓнű¾£¬ÓÃÓÚdocker×Ô¶¯Ö´ÐС£ |
New file |
| | |
| | | # 基础镜像 |
| | | FROM mysql:5.7 |
| | | # author |
| | | MAINTAINER ruoyi |
| | | |
| | | # 执行sql脚本 |
| | | ADD ./db/*.sql /docker-entrypoint-initdb.d/ |
New file |
| | |
| | | spring.datasource.platform=mysql |
| | | db.num=1 |
| | | db.url.0=jdbc:mysql://localhost:3306/ry-config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC |
| | | db.user=root |
| | | db.password=root |
| | | nacos.naming.empty-service.auto-clean=true |
| | | nacos.naming.empty-service.clean.initial-delay-ms=50000 |
| | | nacos.naming.empty-service.clean.period-time-ms=30000 |
| | | management.endpoints.web.exposure.include=* |
| | | management.metrics.export.elastic.enabled=false |
| | | management.metrics.export.influx.enabled=false |
| | | server.tomcat.accesslog.enabled=true |
| | | server.tomcat.accesslog.pattern=%h %l %u %t "%r" %s %b %D %{User-Agent}i %{Request-Source}i |
| | | server.tomcat.basedir=/home/ruoyi/nacos/tomcat/logs |
| | | nacos.security.ignore.urls=/,/error,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-ui/public/**,/v1/auth/**,/v1/console/health/**,/actuator/**,/v1/console/server/** |
| | | nacos.core.auth.system.type=nacos |
| | | nacos.core.auth.enabled=false |
| | | nacos.core.auth.default.token.expire.seconds=18000 |
| | | nacos.core.auth.default.token.secret.key=SecretKey012345678901234567890123456789012345678901234567890123456789 |
| | | nacos.core.auth.caching.enabled=true |
| | | nacos.core.auth.enable.userAgentAuthWhite=false |
| | | nacos.core.auth.server.identity.key=serverIdentity |
| | | nacos.core.auth.server.identity.value=security |
| | | nacos.istio.mcp.server.enabled=false |
New file |
| | |
| | | # 基础镜像 |
| | | FROM nacos/nacos-server |
| | | # author |
| | | MAINTAINER ruoyi |
| | | |
| | | # 复制conf文件到路径 |
| | | COPY ./conf/application.properties /home/nacos/conf/application.properties |
New file |
| | |
| | | worker_processes 1; |
| | | |
| | | events { |
| | | worker_connections 1024; |
| | | } |
| | | |
| | | http { |
| | | include mime.types; |
| | | default_type application/octet-stream; |
| | | sendfile on; |
| | | keepalive_timeout 65; |
| | | |
| | | server { |
| | | listen 80; |
| | | server_name localhost; |
| | | |
| | | location / { |
| | | root /home/ruoyi/projects/ruoyi-ui; |
| | | try_files $uri $uri/ /index.html; |
| | | index index.html index.htm; |
| | | } |
| | | |
| | | location /prod-api/{ |
| | | proxy_set_header Host $http_host; |
| | | proxy_set_header X-Real-IP $remote_addr; |
| | | proxy_set_header REMOTE-HOST $remote_addr; |
| | | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; |
| | | proxy_pass http://ruoyi-gateway:8080/; |
| | | } |
| | | |
| | | # 避免actuator暴露 |
| | | if ($request_uri ~ "/actuator") { |
| | | return 403; |
| | | } |
| | | |
| | | error_page 500 502 503 504 /50x.html; |
| | | location = /50x.html { |
| | | root html; |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | # 基础镜像 |
| | | FROM nginx |
| | | # author |
| | | MAINTAINER ruoyi |
| | | |
| | | # 挂载目录 |
| | | VOLUME /home/ruoyi/projects/ruoyi-ui |
| | | # 创建目录 |
| | | RUN mkdir -p /home/ruoyi/projects/ruoyi-ui |
| | | # 指定路径 |
| | | WORKDIR /home/ruoyi/projects/ruoyi-ui |
| | | # 复制conf文件到路径 |
| | | COPY ./conf/nginx.conf /etc/nginx/nginx.conf |
| | | # 复制html文件到路径 |
| | | COPY ./html/dist /home/ruoyi/projects/ruoyi-ui |
New file |
| | |
| | | # requirepass 123456 |
New file |
| | |
| | | # 基础镜像 |
| | | FROM redis |
| | | # author |
| | | MAINTAINER ruoyi |
| | | |
| | | # 挂载目录 |
| | | VOLUME /home/ruoyi/redis |
| | | # 创建目录 |
| | | RUN mkdir -p /home/ruoyi/redis |
| | | # 指定路径 |
| | | WORKDIR /home/ruoyi/redis |
| | | # 复制conf文件到路径 |
| | | COPY ./conf/redis.conf /home/ruoyi/redis/redis.conf |
New file |
| | |
| | | # 基础镜像 |
| | | FROM openjdk:8-jre |
| | | # author |
| | | MAINTAINER ruoyi |
| | | |
| | | # 挂载目录 |
| | | VOLUME /home/ruoyi |
| | | # 创建目录 |
| | | RUN mkdir -p /home/ruoyi |
| | | # 指定路径 |
| | | WORKDIR /home/ruoyi |
| | | # 复制jar文件到路径 |
| | | COPY ./jar/ruoyi-auth.jar /home/ruoyi/ruoyi-auth.jar |
| | | # 启动认证服务 |
| | | ENTRYPOINT ["java","-jar","ruoyi-auth.jar"] |
New file |
| | |
| | | ´æ·ÅÈÏÖ¤ÖÐÐÄ´ò°üºÃµÄjarÎļþ£¬ÓÃÓÚdockerÆô¶¯Ó¦Óᣠ|
New file |
| | |
| | | # 基础镜像 |
| | | FROM openjdk:8-jre |
| | | # author |
| | | MAINTAINER ruoyi |
| | | |
| | | # 挂载目录 |
| | | VOLUME /home/ruoyi |
| | | # 创建目录 |
| | | RUN mkdir -p /home/ruoyi |
| | | # 指定路径 |
| | | WORKDIR /home/ruoyi |
| | | # 复制jar文件到路径 |
| | | COPY ./jar/ruoyi-gateway.jar /home/ruoyi/ruoyi-gateway.jar |
| | | # 启动网关服务 |
| | | ENTRYPOINT ["java","-jar","ruoyi-gateway.jar"] |
New file |
| | |
| | | ´æ·ÅÍø¹ØÄ£¿é´ò°üºÃµÄjarÎļþ£¬ÓÃÓÚdockerÆô¶¯Ó¦Óᣠ|
New file |
| | |
| | | # 基础镜像 |
| | | FROM openjdk:8-jre |
| | | # author |
| | | MAINTAINER ruoyi |
| | | |
| | | # 挂载目录 |
| | | VOLUME /home/ruoyi |
| | | # 创建目录 |
| | | RUN mkdir -p /home/ruoyi |
| | | # 指定路径 |
| | | WORKDIR /home/ruoyi |
| | | # 复制jar文件到路径 |
| | | COPY ./jar/ruoyi-modules-file.jar /home/ruoyi/ruoyi-modules-file.jar |
| | | # 启动文件服务 |
| | | ENTRYPOINT ["java","-jar","ruoyi-modules-file.jar"] |
New file |
| | |
| | | ´æ·ÅÎļþ·þÎñ´ò°üºÃµÄjarÎļþ£¬ÓÃÓÚdockerÆô¶¯Ó¦Óᣠ|
New file |
| | |
| | | # 基础镜像 |
| | | FROM openjdk:8-jre |
| | | # author |
| | | MAINTAINER ruoyi |
| | | |
| | | # 挂载目录 |
| | | VOLUME /home/ruoyi |
| | | # 创建目录 |
| | | RUN mkdir -p /home/ruoyi |
| | | # 指定路径 |
| | | WORKDIR /home/ruoyi |
| | | # 复制jar文件到路径 |
| | | COPY ./jar/ruoyi-modules-gen.jar /home/ruoyi/ruoyi-modules-gen.jar |
| | | # 启动代码生成服务 |
| | | ENTRYPOINT ["java","-jar","ruoyi-modules-gen.jar"] |
New file |
| | |
| | | ´æ·Å´úÂëÉú³É´ò°üºÃµÄjarÎļþ£¬ÓÃÓÚdockerÆô¶¯Ó¦Óᣠ|
New file |
| | |
| | | # 基础镜像 |
| | | FROM openjdk:8-jre |
| | | # author |
| | | MAINTAINER ruoyi |
| | | |
| | | # 挂载目录 |
| | | VOLUME /home/ruoyi |
| | | # 创建目录 |
| | | RUN mkdir -p /home/ruoyi |
| | | # 指定路径 |
| | | WORKDIR /home/ruoyi |
| | | # 复制jar文件到路径 |
| | | COPY ./jar/ruoyi-modules-job.jar /home/ruoyi/ruoyi-modules-job.jar |
| | | # 启动定时任务服务 |
| | | ENTRYPOINT ["java","-jar","ruoyi-modules-job.jar"] |
New file |
| | |
| | | ´æ·Å¶¨Ê±ÈÎÎñ´ò°üºÃµÄjarÎļþ£¬ÓÃÓÚdockerÆô¶¯Ó¦Óᣠ|
New file |
| | |
| | | # 基础镜像 |
| | | FROM openjdk:8-jre |
| | | # author |
| | | MAINTAINER ruoyi |
| | | |
| | | # 挂载目录 |
| | | VOLUME /home/ruoyi |
| | | # 创建目录 |
| | | RUN mkdir -p /home/ruoyi |
| | | # 指定路径 |
| | | WORKDIR /home/ruoyi |
| | | # 复制jar文件到路径 |
| | | COPY ./jar/ruoyi-modules-system.jar /home/ruoyi/ruoyi-modules-system.jar |
| | | # 启动系统服务 |
| | | ENTRYPOINT ["java","-jar","ruoyi-modules-system.jar"] |
New file |
| | |
| | | ´æ·Åϵͳģ¿é´ò°üºÃµÄjarÎļþ£¬ÓÃÓÚdockerÆô¶¯Ó¦Óᣠ|
New file |
| | |
| | | # 基础镜像 |
| | | FROM openjdk:8-jre |
| | | # author |
| | | MAINTAINER ruoyi |
| | | |
| | | # 挂载目录 |
| | | VOLUME /home/ruoyi |
| | | # 创建目录 |
| | | RUN mkdir -p /home/ruoyi |
| | | # 指定路径 |
| | | WORKDIR /home/ruoyi |
| | | # 复制jar文件到路径 |
| | | COPY ./jar/ruoyi-visual-monitor.jar /home/ruoyi/ruoyi-visual-monitor.jar |
| | | # 启动系统服务 |
| | | ENTRYPOINT ["java","-jar","ruoyi-visual-monitor.jar"] |
New file |
| | |
| | | ´æ·Å¼à¿ØÖÐÐÄ´ò°üºÃµÄjarÎļþ£¬ÓÃÓÚdockerÆô¶¯Ó¦Óᣠ|
New file |
| | |
| | | <?xml version="1.0" encoding="UTF-8"?> |
| | | <project xmlns="http://maven.apache.org/POM/4.0.0" |
| | | xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
| | | xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
| | | <modelVersion>4.0.0</modelVersion> |
| | | |
| | | <groupId>com.ruoyi</groupId> |
| | | <artifactId>ruoyi</artifactId> |
| | | <version>3.6.4</version> |
| | | |
| | | <name>ruoyi</name> |
| | | <url>http://www.ruoyi.vip</url> |
| | | <description>若依微服务系统</description> |
| | | |
| | | <properties> |
| | | <ruoyi.version>3.6.4</ruoyi.version> |
| | | <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> |
| | | <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> |
| | | <java.version>1.8</java.version> |
| | | <spring-boot.version>2.7.18</spring-boot.version> |
| | | <spring-cloud.version>2021.0.8</spring-cloud.version> |
| | | <spring-cloud-alibaba.version>2021.0.5.0</spring-cloud-alibaba.version> |
| | | <spring-framework.version>5.3.33</spring-framework.version> |
| | | <spring-boot-admin.version>2.7.15</spring-boot-admin.version> |
| | | <swagger.fox.version>3.0.0</swagger.fox.version> |
| | | <swagger.core.version>1.6.2</swagger.core.version> |
| | | <tobato.version>1.27.2</tobato.version> |
| | | <kaptcha.version>2.3.3</kaptcha.version> |
| | | <pagehelper.boot.version>2.0.0</pagehelper.boot.version> |
| | | <druid.version>1.2.20</druid.version> |
| | | <dynamic-ds.version>4.2.0</dynamic-ds.version> |
| | | <commons.io.version>2.13.0</commons.io.version> |
| | | <velocity.version>2.3</velocity.version> |
| | | <fastjson.version>2.0.43</fastjson.version> |
| | | <jjwt.version>0.9.1</jjwt.version> |
| | | <minio.version>8.2.2</minio.version> |
| | | <poi.version>4.1.2</poi.version> |
| | | <transmittable-thread-local.version>2.14.4</transmittable-thread-local.version> |
| | | </properties> |
| | | |
| | | <!-- 依赖声明 --> |
| | | <dependencyManagement> |
| | | <dependencies> |
| | | |
| | | <!-- SpringFramework的依赖配置--> |
| | | <dependency> |
| | | <groupId>org.springframework</groupId> |
| | | <artifactId>spring-framework-bom</artifactId> |
| | | <version>${spring-framework.version}</version> |
| | | <type>pom</type> |
| | | <scope>import</scope> |
| | | </dependency> |
| | | |
| | | <!-- SpringCloud 微服务 --> |
| | | <dependency> |
| | | <groupId>org.springframework.cloud</groupId> |
| | | <artifactId>spring-cloud-dependencies</artifactId> |
| | | <version>${spring-cloud.version}</version> |
| | | <type>pom</type> |
| | | <scope>import</scope> |
| | | </dependency> |
| | | |
| | | <!-- SpringCloud Alibaba 微服务 --> |
| | | <dependency> |
| | | <groupId>com.alibaba.cloud</groupId> |
| | | <artifactId>spring-cloud-alibaba-dependencies</artifactId> |
| | | <version>${spring-cloud-alibaba.version}</version> |
| | | <type>pom</type> |
| | | <scope>import</scope> |
| | | </dependency> |
| | | |
| | | <!-- SpringBoot 依赖配置 --> |
| | | <dependency> |
| | | <groupId>org.springframework.boot</groupId> |
| | | <artifactId>spring-boot-dependencies</artifactId> |
| | | <version>${spring-boot.version}</version> |
| | | <type>pom</type> |
| | | <scope>import</scope> |
| | | </dependency> |
| | | |
| | | <!-- FastDFS 分布式文件系统 --> |
| | | <dependency> |
| | | <groupId>com.github.tobato</groupId> |
| | | <artifactId>fastdfs-client</artifactId> |
| | | <version>${tobato.version}</version> |
| | | </dependency> |
| | | |
| | | <!-- Swagger 依赖配置 --> |
| | | <dependency> |
| | | <groupId>io.swagger</groupId> |
| | | <artifactId>swagger-models</artifactId> |
| | | <version>${swagger.core.version}</version> |
| | | </dependency> |
| | | <dependency> |
| | | <groupId>io.swagger</groupId> |
| | | <artifactId>swagger-annotations</artifactId> |
| | | <version>${swagger.core.version}</version> |
| | | </dependency> |
| | | |
| | | <!-- 验证码 --> |
| | | <dependency> |
| | | <groupId>pro.fessional</groupId> |
| | | <artifactId>kaptcha</artifactId> |
| | | <version>${kaptcha.version}</version> |
| | | </dependency> |
| | | |
| | | <!-- pagehelper 分页插件 --> |
| | | <dependency> |
| | | <groupId>com.github.pagehelper</groupId> |
| | | <artifactId>pagehelper-spring-boot-starter</artifactId> |
| | | <version>${pagehelper.boot.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> |
| | | |
| | | <!-- 代码生成使用模板 --> |
| | | <dependency> |
| | | <groupId>org.apache.velocity</groupId> |
| | | <artifactId>velocity-engine-core</artifactId> |
| | | <version>${velocity.version}</version> |
| | | </dependency> |
| | | |
| | | <!-- JSON 解析器和生成器 --> |
| | | <dependency> |
| | | <groupId>com.alibaba.fastjson2</groupId> |
| | | <artifactId>fastjson2</artifactId> |
| | | <version>${fastjson.version}</version> |
| | | </dependency> |
| | | |
| | | <!-- JWT --> |
| | | <dependency> |
| | | <groupId>io.jsonwebtoken</groupId> |
| | | <artifactId>jjwt</artifactId> |
| | | <version>${jjwt.version}</version> |
| | | </dependency> |
| | | |
| | | <!-- 线程传递值 --> |
| | | <dependency> |
| | | <groupId>com.alibaba</groupId> |
| | | <artifactId>transmittable-thread-local</artifactId> |
| | | <version>${transmittable-thread-local.version}</version> |
| | | </dependency> |
| | | |
| | | <!-- 核心模块 --> |
| | | <dependency> |
| | | <groupId>com.ruoyi</groupId> |
| | | <artifactId>ruoyi-common-core</artifactId> |
| | | <version>${ruoyi.version}</version> |
| | | </dependency> |
| | | |
| | | <!-- 接口模块 --> |
| | | <dependency> |
| | | <groupId>com.ruoyi</groupId> |
| | | <artifactId>ruoyi-common-swagger</artifactId> |
| | | <version>${ruoyi.version}</version> |
| | | </dependency> |
| | | |
| | | <!-- 安全模块 --> |
| | | <dependency> |
| | | <groupId>com.ruoyi</groupId> |
| | | <artifactId>ruoyi-common-security</artifactId> |
| | | <version>${ruoyi.version}</version> |
| | | </dependency> |
| | | |
| | | <!-- 数据脱敏 --> |
| | | <dependency> |
| | | <groupId>com.ruoyi</groupId> |
| | | <artifactId>ruoyi-common-sensitive</artifactId> |
| | | <version>${ruoyi.version}</version> |
| | | </dependency> |
| | | |
| | | <!-- 权限范围 --> |
| | | <dependency> |
| | | <groupId>com.ruoyi</groupId> |
| | | <artifactId>ruoyi-common-datascope</artifactId> |
| | | <version>${ruoyi.version}</version> |
| | | </dependency> |
| | | |
| | | <!-- 多数据源 --> |
| | | <dependency> |
| | | <groupId>com.ruoyi</groupId> |
| | | <artifactId>ruoyi-common-datasource</artifactId> |
| | | <version>${ruoyi.version}</version> |
| | | </dependency> |
| | | |
| | | <!-- 分布式事务 --> |
| | | <dependency> |
| | | <groupId>com.ruoyi</groupId> |
| | | <artifactId>ruoyi-common-seata</artifactId> |
| | | <version>${ruoyi.version}</version> |
| | | </dependency> |
| | | |
| | | <!-- 日志记录 --> |
| | | <dependency> |
| | | <groupId>com.ruoyi</groupId> |
| | | <artifactId>ruoyi-common-log</artifactId> |
| | | <version>${ruoyi.version}</version> |
| | | </dependency> |
| | | |
| | | <!-- 缓存服务 --> |
| | | <dependency> |
| | | <groupId>com.ruoyi</groupId> |
| | | <artifactId>ruoyi-common-redis</artifactId> |
| | | <version>${ruoyi.version}</version> |
| | | </dependency> |
| | | |
| | | <!-- 系统接口 --> |
| | | <dependency> |
| | | <groupId>com.ruoyi</groupId> |
| | | <artifactId>ruoyi-api-system</artifactId> |
| | | <version>${ruoyi.version}</version> |
| | | </dependency> |
| | | |
| | | </dependencies> |
| | | </dependencyManagement> |
| | | |
| | | <modules> |
| | | <module>ruoyi-auth</module> |
| | | <module>ruoyi-gateway</module> |
| | | <module>ruoyi-visual</module> |
| | | <module>ruoyi-modules</module> |
| | | <module>ruoyi-api</module> |
| | | <module>ruoyi-common</module> |
| | | <module>ruoyi-service</module> |
| | | </modules> |
| | | <packaging>pom</packaging> |
| | | |
| | | <dependencies> |
| | | <!-- bootstrap 启动器 --> |
| | | <dependency> |
| | | <groupId>org.springframework.cloud</groupId> |
| | | <artifactId>spring-cloud-starter-bootstrap</artifactId> |
| | | </dependency> |
| | | </dependencies> |
| | | |
| | | <build> |
| | | <plugins> |
| | | <plugin> |
| | | <groupId>org.apache.maven.plugins</groupId> |
| | | <artifactId>maven-compiler-plugin</artifactId> |
| | | <configuration> |
| | | <source>${java.version}</source> |
| | | <target>${java.version}</target> |
| | | <encoding>${project.build.sourceEncoding}</encoding> |
| | | </configuration> |
| | | </plugin> |
| | | </plugins> |
| | | <pluginManagement> |
| | | <plugins> |
| | | <plugin> |
| | | <groupId>org.springframework.boot</groupId> |
| | | <artifactId>spring-boot-maven-plugin</artifactId> |
| | | <version>${spring-boot.version}</version> |
| | | <executions> |
| | | <execution> |
| | | <goals> |
| | | <goal>repackage</goal> |
| | | </goals> |
| | | </execution> |
| | | </executions> |
| | | </plugin> |
| | | </plugins> |
| | | </pluginManagement> |
| | | </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> |
New file |
| | |
| | | <?xml version="1.0" encoding="UTF-8"?> |
| | | <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" |
| | | xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
| | | <parent> |
| | | <groupId>com.ruoyi</groupId> |
| | | <artifactId>ruoyi</artifactId> |
| | | <version>3.6.4</version> |
| | | </parent> |
| | | <modelVersion>4.0.0</modelVersion> |
| | | |
| | | <modules> |
| | | <module>ruoyi-api-system</module> |
| | | <module>ruoyi-api-user</module> |
| | | <module>ruoyi-api-worker</module> |
| | | <module>ruoyi-api-admin</module> |
| | | </modules> |
| | | |
| | | <artifactId>ruoyi-api</artifactId> |
| | | <packaging>pom</packaging> |
| | | |
| | | <description> |
| | | ruoyi-api系统接口 |
| | | </description> |
| | | |
| | | </project> |
New file |
| | |
| | | target/ |
| | | !.mvn/wrapper/maven-wrapper.jar |
| | | !**/src/main/**/target/ |
| | | !**/src/test/**/target/ |
| | | |
| | | ### IntelliJ IDEA ### |
| | | .idea/modules.xml |
| | | .idea/jarRepositories.xml |
| | | .idea/compiler.xml |
| | | .idea/libraries/ |
| | | *.iws |
| | | *.iml |
| | | *.ipr |
| | | |
| | | ### Eclipse ### |
| | | .apt_generated |
| | | .classpath |
| | | .factorypath |
| | | .project |
| | | .settings |
| | | .springBeans |
| | | .sts4-cache |
| | | |
| | | ### NetBeans ### |
| | | /nbproject/private/ |
| | | /nbbuild/ |
| | | /dist/ |
| | | /nbdist/ |
| | | /.nb-gradle/ |
| | | build/ |
| | | !**/src/main/**/build/ |
| | | !**/src/test/**/build/ |
| | | |
| | | ### VS Code ### |
| | | .vscode/ |
| | | |
| | | ### Mac OS ### |
| | | .DS_Store |
New file |
| | |
| | | <?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> |
| | | <parent> |
| | | <groupId>com.ruoyi</groupId> |
| | | <artifactId>ruoyi</artifactId> |
| | | <version>3.6.4</version> |
| | | <relativePath>../../pom.xml</relativePath> |
| | | </parent> |
| | | |
| | | <artifactId>ruoyi-api-admin</artifactId> |
| | | |
| | | <properties> |
| | | <maven.compiler.source>8</maven.compiler.source> |
| | | <maven.compiler.target>8</maven.compiler.target> |
| | | <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> |
| | | </properties> |
| | | |
| | | |
| | | <dependencies> |
| | | |
| | | <!-- RuoYi Common Core--> |
| | | <dependency> |
| | | <groupId>com.ruoyi</groupId> |
| | | <artifactId>ruoyi-common-core</artifactId> |
| | | <exclusions> |
| | | <exclusion> |
| | | <groupId>com.github.pagehelper</groupId> |
| | | <artifactId>pagehelper-spring-boot-starter</artifactId> |
| | | </exclusion> |
| | | </exclusions> |
| | | </dependency> |
| | | |
| | | </dependencies> |
| | | |
| | | </project> |
New file |
| | |
| | | package com.ruoyi.admin.api.entity; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import com.ruoyi.common.core.domain.BaseEntity; |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Getter; |
| | | import lombok.Setter; |
| | | |
| | | /** |
| | | * <p> |
| | | * 协议政策、司机操作指导 |
| | | * </p> |
| | | * |
| | | * @author hjl |
| | | * @since 2024-05-29 |
| | | */ |
| | | @Getter |
| | | @Setter |
| | | @TableName("sys_agreement") |
| | | @ApiModel(value = "Agreement对象", description = "协议政策、司机操作指导") |
| | | public class Agreement extends BaseEntity { |
| | | |
| | | @TableId("id") |
| | | private Integer id; |
| | | |
| | | @ApiModelProperty("内容") |
| | | @TableField("content") |
| | | private String content; |
| | | |
| | | @ApiModelProperty("0注册协议;1:隐私政策;2:司机操作指导") |
| | | @TableField("content_type") |
| | | private Integer contentType; |
| | | |
| | | } |
New file |
| | |
| | | package com.ruoyi.admin.api.entity; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import com.ruoyi.common.core.domain.BaseEntity; |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Getter; |
| | | import lombok.Setter; |
| | | |
| | | import java.util.Date; |
| | | |
| | | /** |
| | | * <p> |
| | | * 改派管理 |
| | | * </p> |
| | | * |
| | | * @author hjl |
| | | * @since 2024-05-31 |
| | | */ |
| | | @Getter |
| | | @Setter |
| | | @TableName("sys_change_dispatch") |
| | | @ApiModel(value = "ExchangeDispatch对象", description = "改派管理") |
| | | public class ExchangeDispatch extends BaseEntity { |
| | | |
| | | @TableId("id") |
| | | private Integer id; |
| | | |
| | | @ApiModelProperty("申请师傅") |
| | | @TableField("worker_id") |
| | | private Integer workerId; |
| | | |
| | | @ApiModelProperty("申请师傅姓名") |
| | | @TableField("worker_name") |
| | | private String workerName; |
| | | |
| | | @ApiModelProperty("申请原因") |
| | | @TableField("apply_reason") |
| | | private String applyReason; |
| | | |
| | | @ApiModelProperty("申请改派时间") |
| | | @TableField("apply_time") |
| | | private Date applyTime; |
| | | |
| | | @ApiModelProperty("改派状态 (0:未改派 ;1:已改派)") |
| | | @TableField("state") |
| | | private Integer state; |
| | | |
| | | @ApiModelProperty("订单id") |
| | | @TableField("order_id") |
| | | private Integer orderId; |
| | | |
| | | @ApiModelProperty("订单编号") |
| | | @TableField("order_number") |
| | | private String orderNumber; |
| | | |
| | | @ApiModelProperty("下单用户id") |
| | | @TableField("user_id") |
| | | private Integer userId; |
| | | |
| | | @ApiModelProperty("下单用户名称") |
| | | @TableField("user_name") |
| | | private String userName; |
| | | |
| | | } |
New file |
| | |
| | | package com.ruoyi.admin.api.entity; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import com.ruoyi.common.core.domain.BaseEntity; |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Getter; |
| | | import lombok.Setter; |
| | | |
| | | /** |
| | | * <p> |
| | | * 系统通知管理 |
| | | * </p> |
| | | * |
| | | * @author hjl |
| | | * @since 2024-05-29 |
| | | */ |
| | | @Getter |
| | | @Setter |
| | | @TableName("sys_notices") |
| | | @ApiModel(value = "Notices对象", description = "系统通知管理") |
| | | public class Notices extends BaseEntity { |
| | | |
| | | @TableId("id") |
| | | private Integer id; |
| | | |
| | | @ApiModelProperty("通知标题") |
| | | @TableField("notice_title") |
| | | private String noticeTitle; |
| | | |
| | | @ApiModelProperty("通知内容") |
| | | @TableField("notice_detail") |
| | | private String noticeDetail; |
| | | |
| | | } |
New file |
| | |
| | | package com.ruoyi.admin.api.entity; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import com.ruoyi.common.core.domain.BaseEntity; |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Getter; |
| | | import lombok.Setter; |
| | | |
| | | import java.util.Date; |
| | | |
| | | /** |
| | | * <p> |
| | | * 订单管理 |
| | | * </p> |
| | | * |
| | | * @author hjl |
| | | * @since 2024-05-29 |
| | | */ |
| | | @Getter |
| | | @Setter |
| | | @TableName("t_order") |
| | | @ApiModel(value = "Order对象", description = "订单管理") |
| | | public class Order extends BaseEntity { |
| | | |
| | | @TableId("id") |
| | | private Integer id; |
| | | |
| | | @ApiModelProperty("站点id") |
| | | @TableField("site_id") |
| | | private Integer siteId; |
| | | |
| | | @ApiModelProperty("站点名称") |
| | | @TableField("site_name") |
| | | private String siteName; |
| | | |
| | | @ApiModelProperty("服务id") |
| | | @TableField("serve_id") |
| | | private Integer serveId; |
| | | |
| | | @ApiModelProperty("服务名称") |
| | | @TableField("serve_name") |
| | | private String serveName; |
| | | |
| | | @ApiModelProperty("预约姓名") |
| | | @TableField("reservation_name") |
| | | private String reservationName; |
| | | |
| | | @ApiModelProperty("预约手机号") |
| | | @TableField("reservation_phone") |
| | | private String reservationPhone; |
| | | |
| | | @ApiModelProperty("预约地址") |
| | | @TableField("reservation_address") |
| | | private String reservationAddress; |
| | | |
| | | @ApiModelProperty("上门时间") |
| | | @TableField("time") |
| | | private Date time; |
| | | |
| | | @ApiModelProperty("服务人员id") |
| | | @TableField("server_id") |
| | | private Integer serverId; |
| | | |
| | | @ApiModelProperty("服务人员名称") |
| | | @TableField("server_name") |
| | | private String serverName; |
| | | |
| | | @ApiModelProperty("预约备注") |
| | | @TableField("reservation_remark") |
| | | private String reservationRemark; |
| | | |
| | | @ApiModelProperty("订单状态") |
| | | @TableField("state") |
| | | private Integer state; |
| | | |
| | | } |
New file |
| | |
| | | package com.ruoyi.admin.api.entity; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.IdType; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import com.ruoyi.common.core.domain.BaseEntity; |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Getter; |
| | | import lombok.Setter; |
| | | |
| | | /** |
| | | * <p> |
| | | * 奖品管理 |
| | | * </p> |
| | | * |
| | | * @author hjl |
| | | * @since 2024-05-29 |
| | | */ |
| | | @Getter |
| | | @Setter |
| | | @TableName("sys_prize") |
| | | @ApiModel(value = "Prize对象", description = "奖品管理") |
| | | public class Prize extends BaseEntity { |
| | | |
| | | @TableId(value = "id", type = IdType.AUTO) |
| | | private Integer id; |
| | | |
| | | @ApiModelProperty("奖品图片") |
| | | @TableField("prize_image") |
| | | private String prizeImage; |
| | | |
| | | @ApiModelProperty("奖品名称") |
| | | @TableField("prize_name") |
| | | private String prizeName; |
| | | |
| | | @ApiModelProperty("中奖概率") |
| | | @TableField("probability") |
| | | private Double probability; |
| | | |
| | | } |
New file |
| | |
| | | package com.ruoyi.admin.api.entity; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import com.ruoyi.common.core.domain.BaseEntity; |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Getter; |
| | | import lombok.Setter; |
| | | |
| | | /** |
| | | * <p> |
| | | * 常见问题管理 |
| | | * </p> |
| | | * |
| | | * @author hjl |
| | | * @since 2024-05-29 |
| | | */ |
| | | @Getter |
| | | @Setter |
| | | @TableName("sys_problem") |
| | | @ApiModel(value = "Problem对象", description = "常见问题管理") |
| | | public class Problem extends BaseEntity { |
| | | |
| | | @TableId("id") |
| | | private Integer id; |
| | | |
| | | @ApiModelProperty("标题") |
| | | @TableField("title") |
| | | private String title; |
| | | |
| | | @ApiModelProperty("详情内容") |
| | | @TableField("detail") |
| | | private String detail; |
| | | |
| | | @ApiModelProperty("排序权重") |
| | | @TableField("order") |
| | | private Integer order; |
| | | |
| | | } |
New file |
| | |
| | | package com.ruoyi.admin.api.entity; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import com.ruoyi.common.core.domain.BaseEntity; |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Getter; |
| | | import lombok.Setter; |
| | | |
| | | /** |
| | | * <p> |
| | | * 轮播图图片管理 |
| | | * </p> |
| | | * |
| | | * @author hjl |
| | | * @since 2024-05-29 |
| | | */ |
| | | @Getter |
| | | @Setter |
| | | @TableName("sys_rotate") |
| | | @ApiModel(value = "Rotate对象", description = "轮播图图片管理") |
| | | public class Rotate extends BaseEntity { |
| | | |
| | | @TableId("id") |
| | | private Integer id; |
| | | |
| | | @ApiModelProperty("轮播图图片") |
| | | @TableField("rotate_picture") |
| | | private String rotatePicture; |
| | | |
| | | @ApiModelProperty("排序") |
| | | @TableField("order") |
| | | private Integer order; |
| | | |
| | | @ApiModelProperty("跳转服务id") |
| | | @TableField("rotate_serve_id") |
| | | private Integer rotateServeId; |
| | | |
| | | @ApiModelProperty("是否跳转服务 0不跳转;1跳转") |
| | | @TableField("is_rotate") |
| | | private Integer isRotate; |
| | | |
| | | } |
New file |
| | |
| | | package com.ruoyi.admin.api.entity; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import com.ruoyi.common.core.domain.BaseEntity; |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Getter; |
| | | import lombok.Setter; |
| | | |
| | | /** |
| | | * <p> |
| | | * 服务优势管理 |
| | | * </p> |
| | | * |
| | | * @author hjl |
| | | * @since 2024-05-29 |
| | | */ |
| | | @Getter |
| | | @Setter |
| | | @TableName("sys_serve_advantage") |
| | | @ApiModel(value = "ServeAdvantage对象", description = "服务优势管理") |
| | | public class ServeAdvantage extends BaseEntity { |
| | | |
| | | @TableId("id") |
| | | private Integer id; |
| | | |
| | | @ApiModelProperty("标题") |
| | | @TableField("title") |
| | | private String title; |
| | | |
| | | @ApiModelProperty("描述") |
| | | @TableField("describe") |
| | | private String describe; |
| | | |
| | | } |
New file |
| | |
| | | package com.ruoyi.admin.api.entity; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Getter; |
| | | import lombok.Setter; |
| | | |
| | | /** |
| | | * <p> |
| | | * 审核管理全局设置 |
| | | * </p> |
| | | * |
| | | * @author hjl |
| | | * @since 2024-06-17 |
| | | */ |
| | | @Getter |
| | | @Setter |
| | | @TableName("sys_withdrawal_setting") |
| | | @ApiModel(value = "WithdrawalSetting对象", description = "审核管理全局设置") |
| | | public class WithdrawalSetting { |
| | | |
| | | @TableId("id") |
| | | private Integer id; |
| | | |
| | | @ApiModelProperty("是否开启审核(0:未开启,1:已开启)") |
| | | @TableField("enable_process") |
| | | private Integer enableProcess; |
| | | |
| | | |
| | | } |
New file |
| | |
| | | package com.ruoyi.admin.api.factory; |
| | | |
| | | import com.ruoyi.admin.api.entity.*; |
| | | import com.ruoyi.admin.api.feignClient.AdminClient; |
| | | import com.ruoyi.common.core.domain.R; |
| | | import org.springframework.cloud.openfeign.FallbackFactory; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * @author HJL |
| | | */ |
| | | @Component |
| | | public class AdminFallbackFactory implements FallbackFactory<AdminClient> { |
| | | |
| | | @Override |
| | | public AdminClient create(Throwable cause) { |
| | | return new AdminClient() { |
| | | @Override |
| | | public R<List<Order>> queryList(String phone) { |
| | | return R.fail("获取订单信息失败"); |
| | | } |
| | | |
| | | @Override |
| | | public R<Agreement> dataInfo(Integer type) { |
| | | return R.fail("获取协议/政策/操作指导失败"); |
| | | } |
| | | |
| | | @Override |
| | | public R<Boolean> changeDispatchSave(ExchangeDispatch changeDispatch) { |
| | | return R.fail(Boolean.FALSE); |
| | | } |
| | | |
| | | @Override |
| | | public R<List<Notices>> noticesList() { |
| | | return R.fail("获取通知公告列表失败" + cause.getMessage()); |
| | | } |
| | | |
| | | @Override |
| | | public R<List<Rotate>> bannerList() { |
| | | return R.fail("获取轮播图列表失败" + cause.getMessage()); |
| | | } |
| | | |
| | | @Override |
| | | public R<List<ServeAdvantage>> advantageList() { |
| | | return R.fail("服务优势列表失败" + cause.getMessage()); |
| | | } |
| | | |
| | | @Override |
| | | public R<List<Problem>> problemList() { |
| | | return R.fail("获取常见问题列表失败" + cause.getMessage()); |
| | | } |
| | | |
| | | @Override |
| | | public R<List<Prize>> prizeList() { |
| | | return R.fail("获取奖品列表失败" + cause.getMessage()); |
| | | } |
| | | |
| | | @Override |
| | | public R<WithdrawalSetting> withdrawProcess() { |
| | | return R.fail("获取系统审核设置失败" + cause.getMessage()); |
| | | } |
| | | |
| | | }; |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.admin.api.feignClient; |
| | | |
| | | import com.ruoyi.admin.api.entity.*; |
| | | import com.ruoyi.admin.api.factory.AdminFallbackFactory; |
| | | import com.ruoyi.common.core.constant.ServiceNameConstants; |
| | | import com.ruoyi.common.core.domain.R; |
| | | import org.springframework.cloud.openfeign.FeignClient; |
| | | 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.RequestParam; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * @author HJL |
| | | * @since 2024.05.29 |
| | | */ |
| | | @FeignClient(contextId = "UserClient", value = ServiceNameConstants.ADMIN_SERVICE, fallbackFactory = AdminFallbackFactory.class) |
| | | public interface AdminClient { |
| | | |
| | | /** |
| | | * 根据前台用户id查询所有订单信息 |
| | | * |
| | | * @param phone 手机号 |
| | | * @return 订单信息 |
| | | */ |
| | | @GetMapping(value = "/admin/order/queryList") |
| | | R<List<Order>> queryList(@RequestParam("phone") String phone); |
| | | |
| | | /** |
| | | * 根据类型获取注册协议、隐私政策、司机操作指南 |
| | | * |
| | | * @param type 查询类型 |
| | | * @return 详细数据 |
| | | */ |
| | | @GetMapping(value = "/admin/agreement/dataInfo") |
| | | R<Agreement> dataInfo(@RequestParam("type") Integer type); |
| | | |
| | | /** |
| | | * 新增订单改派 |
| | | * |
| | | * @param changeDispatch 改派信息 |
| | | * @return 详细数据 |
| | | */ |
| | | @PostMapping(value = "/admin/changeDispatch/save") |
| | | R<Boolean> changeDispatchSave(@RequestBody ExchangeDispatch changeDispatch); |
| | | |
| | | /** |
| | | * 系统通知列表 |
| | | * |
| | | * @return 通知公告列表 |
| | | */ |
| | | @GetMapping(value = "/admin/notices/list") |
| | | R<List<Notices>> noticesList(); |
| | | |
| | | /** |
| | | * 轮播图列表 |
| | | * |
| | | * @return 轮播图列表 |
| | | */ |
| | | @GetMapping(value = "/admin/rotate/bannerList") |
| | | R<List<Rotate>> bannerList(); |
| | | |
| | | /** |
| | | * 服务优势查询列表 |
| | | * |
| | | * @return 服务优势列表 |
| | | */ |
| | | @GetMapping(value = "/admin/advantage/advantageList") |
| | | R<List<ServeAdvantage>> advantageList(); |
| | | |
| | | /** |
| | | * 常见问题查询列表 |
| | | * |
| | | * @return 列表 |
| | | */ |
| | | @GetMapping(value = "/admin/problem/problemList") |
| | | R<List<Problem>> problemList(); |
| | | |
| | | /** |
| | | * 获取奖品列表 |
| | | * |
| | | * @return 奖品列表 |
| | | */ |
| | | @GetMapping(value = "/admin/prize/list") |
| | | R<List<Prize>> prizeList(); |
| | | |
| | | /** |
| | | * 修改系统设置-关闭/开启审核 |
| | | * |
| | | * @return 操作结果 |
| | | */ |
| | | @GetMapping(value = "/withdrawProcess") |
| | | R<WithdrawalSetting> withdrawProcess(); |
| | | |
| | | } |
New file |
| | |
| | | com.ruoyi.admin.api.factory.AdminFallbackFactory |
New file |
| | |
| | | <?xml version="1.0" encoding="UTF-8"?> |
| | | <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
| | | xmlns="http://maven.apache.org/POM/4.0.0" |
| | | xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
| | | <parent> |
| | | <groupId>com.ruoyi</groupId> |
| | | <artifactId>ruoyi-api</artifactId> |
| | | <version>3.6.4</version> |
| | | </parent> |
| | | <modelVersion>4.0.0</modelVersion> |
| | | |
| | | <artifactId>ruoyi-api-system</artifactId> |
| | | |
| | | <description> |
| | | ruoyi-api-system系统接口模块 |
| | | </description> |
| | | |
| | | <dependencies> |
| | | |
| | | <!-- RuoYi Common Core--> |
| | | <dependency> |
| | | <groupId>com.ruoyi</groupId> |
| | | <artifactId>ruoyi-common-core</artifactId> |
| | | </dependency> |
| | | |
| | | </dependencies> |
| | | |
| | | </project> |
New file |
| | |
| | | package com.ruoyi.system.api; |
| | | |
| | | import org.springframework.cloud.openfeign.FeignClient; |
| | | import org.springframework.http.MediaType; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | | import org.springframework.web.bind.annotation.RequestPart; |
| | | import org.springframework.web.multipart.MultipartFile; |
| | | import com.ruoyi.common.core.constant.ServiceNameConstants; |
| | | import com.ruoyi.common.core.domain.R; |
| | | import com.ruoyi.system.api.domain.SysFile; |
| | | import com.ruoyi.system.api.factory.RemoteFileFallbackFactory; |
| | | |
| | | /** |
| | | * 文件服务 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | @FeignClient(contextId = "remoteFileService", value = ServiceNameConstants.FILE_SERVICE, fallbackFactory = RemoteFileFallbackFactory.class) |
| | | public interface RemoteFileService { |
| | | /** |
| | | * 上传文件 |
| | | * |
| | | * @param file 文件信息 |
| | | * @return 结果 |
| | | */ |
| | | @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) |
| | | R<SysFile> upload(@RequestPart(value = "file") MultipartFile file); |
| | | } |
New file |
| | |
| | | package com.ruoyi.system.api; |
| | | |
| | | import org.springframework.cloud.openfeign.FeignClient; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | | import org.springframework.web.bind.annotation.RequestBody; |
| | | import org.springframework.web.bind.annotation.RequestHeader; |
| | | import com.ruoyi.common.core.constant.SecurityConstants; |
| | | import com.ruoyi.common.core.constant.ServiceNameConstants; |
| | | import com.ruoyi.common.core.domain.R; |
| | | import com.ruoyi.system.api.domain.SysLogininfor; |
| | | import com.ruoyi.system.api.domain.SysOperLog; |
| | | import com.ruoyi.system.api.factory.RemoteLogFallbackFactory; |
| | | |
| | | /** |
| | | * 日志服务 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | @FeignClient(contextId = "remoteLogService", value = ServiceNameConstants.SYSTEM_SERVICE, fallbackFactory = RemoteLogFallbackFactory.class) |
| | | public interface RemoteLogService { |
| | | /** |
| | | * 保存系统日志 |
| | | * |
| | | * @param sysOperLog 日志实体 |
| | | * @param source 请求来源 |
| | | * @return 结果 |
| | | */ |
| | | @PostMapping("/operlog") |
| | | public R<Boolean> saveLog(@RequestBody SysOperLog sysOperLog, @RequestHeader(SecurityConstants.FROM_SOURCE) String source) throws Exception; |
| | | |
| | | /** |
| | | * 保存访问记录 |
| | | * |
| | | * @param sysLogininfor 访问实体 |
| | | * @param source 请求来源 |
| | | * @return 结果 |
| | | */ |
| | | @PostMapping("/logininfor") |
| | | public R<Boolean> saveLogininfor(@RequestBody SysLogininfor sysLogininfor, @RequestHeader(SecurityConstants.FROM_SOURCE) String source); |
| | | } |
New file |
| | |
| | | package com.ruoyi.system.api; |
| | | |
| | | import org.springframework.cloud.openfeign.FeignClient; |
| | | 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.RequestBody; |
| | | import org.springframework.web.bind.annotation.RequestHeader; |
| | | import com.ruoyi.common.core.constant.SecurityConstants; |
| | | import com.ruoyi.common.core.constant.ServiceNameConstants; |
| | | import com.ruoyi.common.core.domain.R; |
| | | import com.ruoyi.system.api.domain.SysUser; |
| | | import com.ruoyi.system.api.factory.RemoteUserFallbackFactory; |
| | | import com.ruoyi.system.api.model.LoginUser; |
| | | |
| | | /** |
| | | * 用户服务 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | @FeignClient(contextId = "remoteUserService", value = ServiceNameConstants.SYSTEM_SERVICE, fallbackFactory = RemoteUserFallbackFactory.class) |
| | | public interface RemoteUserService { |
| | | /** |
| | | * 通过用户名查询用户信息 |
| | | * |
| | | * @param username 用户名 |
| | | * @param source 请求来源 |
| | | * @return 结果 |
| | | */ |
| | | @GetMapping("/user/info/{username}") |
| | | public R<LoginUser> getUserInfo(@PathVariable("username") String username, @RequestHeader(SecurityConstants.FROM_SOURCE) String source); |
| | | |
| | | /** |
| | | * 注册用户信息 |
| | | * |
| | | * @param sysUser 用户信息 |
| | | * @param source 请求来源 |
| | | * @return 结果 |
| | | */ |
| | | @PostMapping("/user/register") |
| | | public R<Boolean> registerUserInfo(@RequestBody SysUser sysUser, @RequestHeader(SecurityConstants.FROM_SOURCE) String source); |
| | | } |
New file |
| | |
| | | package com.ruoyi.system.api.client; |
| | | |
| | | import com.ruoyi.common.core.constant.ServiceNameConstants; |
| | | import com.ruoyi.common.core.domain.R; |
| | | import com.ruoyi.system.api.domain.SysUser; |
| | | import com.ruoyi.system.api.factory.SysUserFallbackFactory; |
| | | import org.springframework.cloud.openfeign.FeignClient; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | | import org.springframework.web.bind.annotation.RequestBody; |
| | | |
| | | /** |
| | | * @author HJL |
| | | */ |
| | | @FeignClient(contextId = "sysUserClient", value = ServiceNameConstants.SYSTEM_SERVICE, fallbackFactory = SysUserFallbackFactory.class) |
| | | public interface SysUserClient { |
| | | |
| | | /** |
| | | * 更新用户最后登录时间 |
| | | * |
| | | * @param sysUser 系统用户 |
| | | * @return 更新结果 |
| | | */ |
| | | @PostMapping("/user/updateSysUser") |
| | | R<Boolean> updateSysUser(@RequestBody SysUser sysUser); |
| | | |
| | | } |
New file |
| | |
| | | package com.ruoyi.system.api.domain; |
| | | |
| | | import javax.validation.constraints.NotBlank; |
| | | import javax.validation.constraints.Size; |
| | | |
| | | import org.apache.commons.lang3.builder.ToStringBuilder; |
| | | import org.apache.commons.lang3.builder.ToStringStyle; |
| | | import com.ruoyi.common.core.annotation.Excel; |
| | | import com.ruoyi.common.core.annotation.Excel.ColumnType; |
| | | import com.ruoyi.common.core.constant.UserConstants; |
| | | import com.ruoyi.common.core.web.domain.BaseEntity; |
| | | |
| | | /** |
| | | * 字典数据表 sys_dict_data |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class SysDictData extends BaseEntity { |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | /** |
| | | * 字典编码 |
| | | */ |
| | | @Excel(name = "字典编码", cellType = ColumnType.NUMERIC) |
| | | private Long dictCode; |
| | | |
| | | /** |
| | | * 字典排序 |
| | | */ |
| | | @Excel(name = "字典排序", cellType = ColumnType.NUMERIC) |
| | | private Long dictSort; |
| | | |
| | | /** |
| | | * 字典标签 |
| | | */ |
| | | @Excel(name = "字典标签") |
| | | private String dictLabel; |
| | | |
| | | /** |
| | | * 字典键值 |
| | | */ |
| | | @Excel(name = "字典键值") |
| | | private String dictValue; |
| | | |
| | | /** |
| | | * 字典类型 |
| | | */ |
| | | @Excel(name = "字典类型") |
| | | private String dictType; |
| | | |
| | | /** |
| | | * 样式属性(其他样式扩展) |
| | | */ |
| | | private String cssClass; |
| | | |
| | | /** |
| | | * 表格字典样式 |
| | | */ |
| | | private String listClass; |
| | | |
| | | /** |
| | | * 是否默认(Y是 N否) |
| | | */ |
| | | @Excel(name = "是否默认", readConverterExp = "Y=是,N=否") |
| | | private String isDefault; |
| | | |
| | | /** |
| | | * 状态(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(); |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.system.api.domain; |
| | | |
| | | import com.ruoyi.common.core.annotation.Excel; |
| | | import com.ruoyi.common.core.annotation.Excel.ColumnType; |
| | | import com.ruoyi.common.core.web.domain.BaseEntity; |
| | | import org.apache.commons.lang3.builder.ToStringBuilder; |
| | | import org.apache.commons.lang3.builder.ToStringStyle; |
| | | |
| | | import javax.validation.constraints.NotBlank; |
| | | import javax.validation.constraints.Pattern; |
| | | import javax.validation.constraints.Size; |
| | | |
| | | /** |
| | | * 字典类型表 sys_dict_type |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class SysDictType extends BaseEntity { |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | /** |
| | | * 字典主键 |
| | | */ |
| | | @Excel(name = "字典主键", cellType = ColumnType.NUMERIC) |
| | | private Long dictId; |
| | | |
| | | /** |
| | | * 字典名称 |
| | | */ |
| | | @Excel(name = "字典名称") |
| | | private String dictName; |
| | | |
| | | /** |
| | | * 字典类型 |
| | | */ |
| | | @Excel(name = "字典类型") |
| | | private String dictType; |
| | | |
| | | /** |
| | | * 状态(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(); |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.system.api.domain; |
| | | |
| | | import org.apache.commons.lang3.builder.ToStringBuilder; |
| | | import org.apache.commons.lang3.builder.ToStringStyle; |
| | | |
| | | /** |
| | | * 文件信息 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class SysFile { |
| | | /** |
| | | * 文件名称 |
| | | */ |
| | | private String name; |
| | | |
| | | /** |
| | | * 文件地址 |
| | | */ |
| | | private String url; |
| | | |
| | | public String getName() { |
| | | return name; |
| | | } |
| | | |
| | | public void setName(String name) { |
| | | this.name = name; |
| | | } |
| | | |
| | | public String getUrl() { |
| | | return url; |
| | | } |
| | | |
| | | public void setUrl(String url) { |
| | | this.url = url; |
| | | } |
| | | |
| | | @Override |
| | | public String toString() { |
| | | return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE) |
| | | .append("name", getName()) |
| | | .append("url", getUrl()) |
| | | .toString(); |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.system.api.domain; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonFormat; |
| | | import com.ruoyi.common.core.annotation.Excel; |
| | | import com.ruoyi.common.core.annotation.Excel.ColumnType; |
| | | import com.ruoyi.common.core.web.domain.BaseEntity; |
| | | |
| | | import java.util.Date; |
| | | |
| | | /** |
| | | * 系统访问记录表 sys_logininfor |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class SysLogininfor extends BaseEntity { |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | /** |
| | | * ID |
| | | */ |
| | | @Excel(name = "序号", cellType = ColumnType.NUMERIC) |
| | | private Long infoId; |
| | | |
| | | /** |
| | | * 用户账号 |
| | | */ |
| | | @Excel(name = "用户账号") |
| | | private String userName; |
| | | |
| | | /** |
| | | * 状态 0成功 1失败 |
| | | */ |
| | | @Excel(name = "状态", readConverterExp = "0=成功,1=失败") |
| | | private String status; |
| | | |
| | | /** |
| | | * 地址 |
| | | */ |
| | | @Excel(name = "地址") |
| | | private String ipaddr; |
| | | |
| | | /** |
| | | * 描述 |
| | | */ |
| | | @Excel(name = "描述") |
| | | private String msg; |
| | | |
| | | /** |
| | | * 访问时间 |
| | | */ |
| | | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | @Excel(name = "访问时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss") |
| | | private Date accessTime; |
| | | |
| | | public Long getInfoId() { |
| | | return infoId; |
| | | } |
| | | |
| | | public void setInfoId(Long infoId) { |
| | | this.infoId = infoId; |
| | | } |
| | | |
| | | public String getUserName() { |
| | | return userName; |
| | | } |
| | | |
| | | public void setUserName(String userName) { |
| | | this.userName = userName; |
| | | } |
| | | |
| | | public String getStatus() { |
| | | return status; |
| | | } |
| | | |
| | | public void setStatus(String status) { |
| | | this.status = status; |
| | | } |
| | | |
| | | public String getIpaddr() { |
| | | return ipaddr; |
| | | } |
| | | |
| | | public void setIpaddr(String ipaddr) { |
| | | this.ipaddr = ipaddr; |
| | | } |
| | | |
| | | public String getMsg() { |
| | | return msg; |
| | | } |
| | | |
| | | public void setMsg(String msg) { |
| | | this.msg = msg; |
| | | } |
| | | |
| | | public Date getAccessTime() { |
| | | return accessTime; |
| | | } |
| | | |
| | | public void setAccessTime(Date accessTime) { |
| | | this.accessTime = accessTime; |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.system.api.domain; |
| | | |
| | | import java.util.Date; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonFormat; |
| | | import com.ruoyi.common.core.annotation.Excel; |
| | | import com.ruoyi.common.core.annotation.Excel.ColumnType; |
| | | import com.ruoyi.common.core.web.domain.BaseEntity; |
| | | |
| | | /** |
| | | * 操作日志记录表 oper_log |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class SysOperLog extends BaseEntity { |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | /** |
| | | * 日志主键 |
| | | */ |
| | | @Excel(name = "操作序号", cellType = ColumnType.NUMERIC) |
| | | private Long operId; |
| | | |
| | | /** |
| | | * 操作模块 |
| | | */ |
| | | @Excel(name = "操作模块") |
| | | private String title; |
| | | |
| | | /** |
| | | * 业务类型(0其它 1新增 2修改 3删除) |
| | | */ |
| | | @Excel(name = "业务类型", readConverterExp = "0=其它,1=新增,2=修改,3=删除,4=授权,5=导出,6=导入,7=强退,8=生成代码,9=清空数据") |
| | | private Integer businessType; |
| | | |
| | | /** |
| | | * 业务类型数组 |
| | | */ |
| | | private Integer[] businessTypes; |
| | | |
| | | /** |
| | | * 请求方法 |
| | | */ |
| | | @Excel(name = "请求方法") |
| | | private String method; |
| | | |
| | | /** |
| | | * 请求方式 |
| | | */ |
| | | @Excel(name = "请求方式") |
| | | private String requestMethod; |
| | | |
| | | /** |
| | | * 操作类别(0其它 1后台用户 2手机端用户) |
| | | */ |
| | | @Excel(name = "操作类别", readConverterExp = "0=其它,1=后台用户,2=手机端用户") |
| | | private Integer operatorType; |
| | | |
| | | /** |
| | | * 操作人员 |
| | | */ |
| | | @Excel(name = "操作人员") |
| | | private String operName; |
| | | |
| | | /** |
| | | * 部门名称 |
| | | */ |
| | | @Excel(name = "部门名称") |
| | | private String deptName; |
| | | |
| | | /** |
| | | * 请求url |
| | | */ |
| | | @Excel(name = "请求地址") |
| | | private String operUrl; |
| | | |
| | | /** |
| | | * 操作地址 |
| | | */ |
| | | @Excel(name = "操作地址") |
| | | private String operIp; |
| | | |
| | | /** |
| | | * 请求参数 |
| | | */ |
| | | @Excel(name = "请求参数") |
| | | private String operParam; |
| | | |
| | | /** |
| | | * 返回参数 |
| | | */ |
| | | @Excel(name = "返回参数") |
| | | private String jsonResult; |
| | | |
| | | /** |
| | | * 操作状态(0正常 1异常) |
| | | */ |
| | | @Excel(name = "状态", readConverterExp = "0=正常,1=异常") |
| | | private Integer status; |
| | | |
| | | /** |
| | | * 错误消息 |
| | | */ |
| | | @Excel(name = "错误消息") |
| | | private String errorMsg; |
| | | |
| | | /** |
| | | * 操作时间 |
| | | */ |
| | | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | @Excel(name = "操作时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss") |
| | | private Date operTime; |
| | | |
| | | /** |
| | | * 消耗时间 |
| | | */ |
| | | @Excel(name = "消耗时间", suffix = "毫秒") |
| | | private Long costTime; |
| | | |
| | | public Long getOperId() { |
| | | return operId; |
| | | } |
| | | |
| | | public void setOperId(Long operId) { |
| | | this.operId = operId; |
| | | } |
| | | |
| | | public String getTitle() { |
| | | return title; |
| | | } |
| | | |
| | | public void setTitle(String title) { |
| | | this.title = title; |
| | | } |
| | | |
| | | public Integer getBusinessType() { |
| | | return businessType; |
| | | } |
| | | |
| | | public void setBusinessType(Integer businessType) { |
| | | this.businessType = businessType; |
| | | } |
| | | |
| | | public Integer[] getBusinessTypes() { |
| | | return businessTypes; |
| | | } |
| | | |
| | | public void setBusinessTypes(Integer[] businessTypes) { |
| | | this.businessTypes = businessTypes; |
| | | } |
| | | |
| | | public String getMethod() { |
| | | return method; |
| | | } |
| | | |
| | | public void setMethod(String method) { |
| | | this.method = method; |
| | | } |
| | | |
| | | public String getRequestMethod() { |
| | | return requestMethod; |
| | | } |
| | | |
| | | public void setRequestMethod(String requestMethod) { |
| | | this.requestMethod = requestMethod; |
| | | } |
| | | |
| | | public Integer getOperatorType() { |
| | | return operatorType; |
| | | } |
| | | |
| | | public void setOperatorType(Integer operatorType) { |
| | | this.operatorType = operatorType; |
| | | } |
| | | |
| | | public String getOperName() { |
| | | return operName; |
| | | } |
| | | |
| | | public void setOperName(String operName) { |
| | | this.operName = operName; |
| | | } |
| | | |
| | | public String getDeptName() { |
| | | return deptName; |
| | | } |
| | | |
| | | public void setDeptName(String deptName) { |
| | | this.deptName = deptName; |
| | | } |
| | | |
| | | public String getOperUrl() { |
| | | return operUrl; |
| | | } |
| | | |
| | | public void setOperUrl(String operUrl) { |
| | | this.operUrl = operUrl; |
| | | } |
| | | |
| | | public String getOperIp() { |
| | | return operIp; |
| | | } |
| | | |
| | | public void setOperIp(String operIp) { |
| | | this.operIp = operIp; |
| | | } |
| | | |
| | | public String getOperParam() { |
| | | return operParam; |
| | | } |
| | | |
| | | public void setOperParam(String operParam) { |
| | | this.operParam = operParam; |
| | | } |
| | | |
| | | public String getJsonResult() { |
| | | return jsonResult; |
| | | } |
| | | |
| | | public void setJsonResult(String jsonResult) { |
| | | this.jsonResult = jsonResult; |
| | | } |
| | | |
| | | public Integer getStatus() { |
| | | return status; |
| | | } |
| | | |
| | | public void setStatus(Integer status) { |
| | | this.status = status; |
| | | } |
| | | |
| | | public String getErrorMsg() { |
| | | return errorMsg; |
| | | } |
| | | |
| | | public void setErrorMsg(String errorMsg) { |
| | | this.errorMsg = errorMsg; |
| | | } |
| | | |
| | | public Date getOperTime() { |
| | | return operTime; |
| | | } |
| | | |
| | | public void setOperTime(Date operTime) { |
| | | this.operTime = operTime; |
| | | } |
| | | |
| | | public Long getCostTime() { |
| | | return costTime; |
| | | } |
| | | |
| | | public void setCostTime(Long costTime) { |
| | | this.costTime = costTime; |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.system.api.domain; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.ruoyi.common.core.annotation.Excel; |
| | | import com.ruoyi.common.core.annotation.Excel.ColumnType; |
| | | import com.ruoyi.common.core.web.domain.BaseEntity; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Data; |
| | | import lombok.EqualsAndHashCode; |
| | | import org.apache.commons.lang3.builder.ToStringBuilder; |
| | | import org.apache.commons.lang3.builder.ToStringStyle; |
| | | |
| | | import javax.validation.constraints.NotBlank; |
| | | import javax.validation.constraints.NotNull; |
| | | import javax.validation.constraints.Size; |
| | | import java.util.Set; |
| | | |
| | | /** |
| | | * 角色表 sys_role |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | @EqualsAndHashCode(callSuper = true) |
| | | @Data |
| | | public class SysRole extends BaseEntity { |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | /** |
| | | * 角色ID |
| | | */ |
| | | @Excel(name = "角色序号", cellType = ColumnType.NUMERIC) |
| | | @TableId("role_id") |
| | | @ApiModelProperty("角色序号") |
| | | private Long roleId; |
| | | |
| | | /** |
| | | * 角色名称 |
| | | */ |
| | | @Excel(name = "角色名称") |
| | | @TableField("role_name") |
| | | @ApiModelProperty("角色名称") |
| | | private String roleName; |
| | | |
| | | /** |
| | | * 角色名称 |
| | | */ |
| | | @Excel(name = "角色说明") |
| | | @TableField("role_illustrate") |
| | | @ApiModelProperty("角色说明") |
| | | private String roleIllustrate; |
| | | |
| | | /** |
| | | * 角色权限 |
| | | */ |
| | | @Excel(name = "角色权限") |
| | | @TableField("role_key") |
| | | @ApiModelProperty("角色权限") |
| | | private String roleKey; |
| | | |
| | | /** |
| | | * 角色排序 |
| | | */ |
| | | @Excel(name = "角色排序") |
| | | @TableField("role_sort") |
| | | @ApiModelProperty("角色排序") |
| | | private Integer roleSort; |
| | | |
| | | /** |
| | | * 数据范围(1:所有数据权限;2:自定义数据权限;3:本部门数据权限;4:本部门及以下数据权限;5:仅本人数据权限) |
| | | */ |
| | | @Excel(name = "数据范围", readConverterExp = "1=所有数据权限,2=自定义数据权限,3=本部门数据权限,4=本部门及以下数据权限,5=仅本人数据权限") |
| | | @TableField("data_scope") |
| | | @ApiModelProperty("角色权限") |
| | | private String dataScope; |
| | | |
| | | /** |
| | | * 菜单树选择项是否关联显示( 0:父子不互相关联显示 1:父子互相关联显示) |
| | | */ |
| | | @TableField("menu_check_strictly") |
| | | private boolean menuCheckStrictly; |
| | | |
| | | /** |
| | | * 部门树选择项是否关联显示(0:父子不互相关联显示 1:父子互相关联显示 ) |
| | | */ |
| | | @TableField("dept_check_strictly") |
| | | private boolean deptCheckStrictly; |
| | | |
| | | /** |
| | | * 角色状态(0正常 1停用) |
| | | */ |
| | | @Excel(name = "角色状态", readConverterExp = "0=正常,1=停用") |
| | | @TableField("status") |
| | | @ApiModelProperty("角色状态(0正常 1停用)") |
| | | private String status; |
| | | |
| | | /** |
| | | * 删除标志(0代表存在 2代表删除) |
| | | */ |
| | | @TableField("del_flag") |
| | | @ApiModelProperty("删除标志(0代表存在 2代表删除)") |
| | | private String delFlag; |
| | | |
| | | /** |
| | | * 用户是否存在此角色标识 默认不存在 |
| | | */ |
| | | private boolean flag = false; |
| | | |
| | | /** |
| | | * 菜单组 |
| | | */ |
| | | private Long[] menuIds; |
| | | |
| | | /** |
| | | * 部门组(数据权限) |
| | | */ |
| | | private Long[] deptIds; |
| | | |
| | | /** |
| | | * 角色菜单权限 |
| | | */ |
| | | 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(); |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.system.api.domain; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.IdType; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import com.ruoyi.common.core.domain.BaseEntity; |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Getter; |
| | | import lombok.Setter; |
| | | |
| | | import javax.validation.constraints.NotBlank; |
| | | import java.util.Date; |
| | | |
| | | /** |
| | | * <p> |
| | | * 用户信息表 |
| | | * </p> |
| | | * |
| | | * @author hjl |
| | | * @since 2024-06-03 |
| | | */ |
| | | @Getter |
| | | @Setter |
| | | @TableName("sys_user") |
| | | @ApiModel(value = "User对象", description = "用户信息表") |
| | | public class SysUser extends BaseEntity { |
| | | |
| | | @ApiModelProperty("用户ID") |
| | | @TableId(value = "user_id", type = IdType.AUTO) |
| | | private Long userId; |
| | | |
| | | @ApiModelProperty("账号名称") |
| | | @TableField("nick_name") |
| | | @NotBlank(message = "请输入:账号名称") |
| | | private String nickName; |
| | | |
| | | @ApiModelProperty("账号") |
| | | @TableField("account") |
| | | @NotBlank(message = "请输入:账号") |
| | | private String account; |
| | | |
| | | @ApiModelProperty("密码") |
| | | @TableField("password") |
| | | @NotBlank(message = "请输入:初始密码") |
| | | private String password; |
| | | |
| | | @ApiModelProperty("是否启用(0:未启用;1:已启用)") |
| | | @TableField("is_enable") |
| | | @NotBlank(message = "请选择:账号是否启用") |
| | | private Integer isEnable; |
| | | |
| | | @ApiModelProperty("最后登录IP") |
| | | @TableField("login_ip") |
| | | private String loginIp; |
| | | |
| | | @ApiModelProperty("最后登录时间") |
| | | @TableField("login_date") |
| | | private Date loginDate; |
| | | |
| | | @ApiModelProperty("密码修改时间") |
| | | @TableField("passWordUpdate") |
| | | private Date passWordUpdate; |
| | | |
| | | @ApiModelProperty("角色id") |
| | | @TableField(exist = false) |
| | | private SysRole sysRole; |
| | | |
| | | @ApiModelProperty("角色id") |
| | | @TableField(exist = false) |
| | | @NotBlank(message = "请选择:账号所属角色") |
| | | private Long roleId; |
| | | |
| | | public SysUser() { |
| | | } |
| | | |
| | | public SysUser(Long userId) { |
| | | this.userId = userId; |
| | | } |
| | | |
| | | public boolean isAdmin() { |
| | | return isAdmin(userId); |
| | | } |
| | | |
| | | public static boolean isAdmin(Long userId) { |
| | | return userId != null && 1L == userId; |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.system.api.factory; |
| | | |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | import org.springframework.cloud.openfeign.FallbackFactory; |
| | | import org.springframework.stereotype.Component; |
| | | import org.springframework.web.multipart.MultipartFile; |
| | | import com.ruoyi.common.core.domain.R; |
| | | import com.ruoyi.system.api.RemoteFileService; |
| | | import com.ruoyi.system.api.domain.SysFile; |
| | | |
| | | /** |
| | | * 文件服务降级处理 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | @Component |
| | | public class RemoteFileFallbackFactory implements FallbackFactory<RemoteFileService> |
| | | { |
| | | private static final Logger log = LoggerFactory.getLogger(RemoteFileFallbackFactory.class); |
| | | |
| | | @Override |
| | | public RemoteFileService create(Throwable throwable) |
| | | { |
| | | log.error("文件服务调用失败:{}", throwable.getMessage()); |
| | | return new RemoteFileService() |
| | | { |
| | | @Override |
| | | public R<SysFile> upload(MultipartFile file) |
| | | { |
| | | return R.fail("上传文件失败:" + throwable.getMessage()); |
| | | } |
| | | }; |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.system.api.factory; |
| | | |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | import org.springframework.cloud.openfeign.FallbackFactory; |
| | | import org.springframework.stereotype.Component; |
| | | import com.ruoyi.common.core.domain.R; |
| | | import com.ruoyi.system.api.RemoteLogService; |
| | | import com.ruoyi.system.api.domain.SysLogininfor; |
| | | import com.ruoyi.system.api.domain.SysOperLog; |
| | | |
| | | /** |
| | | * 日志服务降级处理 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | @Component |
| | | public class RemoteLogFallbackFactory implements FallbackFactory<RemoteLogService> |
| | | { |
| | | private static final Logger log = LoggerFactory.getLogger(RemoteLogFallbackFactory.class); |
| | | |
| | | @Override |
| | | public RemoteLogService create(Throwable throwable) |
| | | { |
| | | log.error("日志服务调用失败:{}", throwable.getMessage()); |
| | | return new RemoteLogService() |
| | | { |
| | | @Override |
| | | public R<Boolean> saveLog(SysOperLog sysOperLog, String source) |
| | | { |
| | | return R.fail("保存操作日志失败:" + throwable.getMessage()); |
| | | } |
| | | |
| | | @Override |
| | | public R<Boolean> saveLogininfor(SysLogininfor sysLogininfor, String source) |
| | | { |
| | | return R.fail("保存登录日志失败:" + throwable.getMessage()); |
| | | } |
| | | }; |
| | | |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.system.api.factory; |
| | | |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | import org.springframework.cloud.openfeign.FallbackFactory; |
| | | import org.springframework.stereotype.Component; |
| | | import com.ruoyi.common.core.domain.R; |
| | | import com.ruoyi.system.api.RemoteUserService; |
| | | import com.ruoyi.system.api.domain.SysUser; |
| | | import com.ruoyi.system.api.model.LoginUser; |
| | | |
| | | /** |
| | | * 用户服务降级处理 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | @Component |
| | | public class RemoteUserFallbackFactory implements FallbackFactory<RemoteUserService> |
| | | { |
| | | private static final Logger log = LoggerFactory.getLogger(RemoteUserFallbackFactory.class); |
| | | |
| | | @Override |
| | | public RemoteUserService create(Throwable throwable) |
| | | { |
| | | log.error("用户服务调用失败:{}", throwable.getMessage()); |
| | | return new RemoteUserService() |
| | | { |
| | | @Override |
| | | public R<LoginUser> getUserInfo(String username, String source) |
| | | { |
| | | return R.fail("获取用户失败:" + throwable.getMessage()); |
| | | } |
| | | |
| | | @Override |
| | | public R<Boolean> registerUserInfo(SysUser sysUser, String source) |
| | | { |
| | | return R.fail("注册用户失败:" + throwable.getMessage()); |
| | | } |
| | | }; |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.system.api.factory; |
| | | |
| | | import com.ruoyi.common.core.domain.R; |
| | | import com.ruoyi.system.api.client.SysUserClient; |
| | | import com.ruoyi.system.api.domain.SysUser; |
| | | import org.springframework.cloud.openfeign.FallbackFactory; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | /** |
| | | * @author HJL |
| | | */ |
| | | @Component |
| | | public class SysUserFallbackFactory implements FallbackFactory<SysUserClient> { |
| | | @Override |
| | | public SysUserClient create(Throwable cause) { |
| | | return new SysUserClient() { |
| | | |
| | | @Override |
| | | public R<Boolean> updateSysUser(SysUser sysUser) { |
| | | return R.fail("更新用户失败:" + cause.getMessage()); |
| | | } |
| | | |
| | | }; |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.system.api.model; |
| | | |
| | | import com.ruoyi.system.api.domain.SysUser; |
| | | import lombok.Data; |
| | | |
| | | import java.io.Serializable; |
| | | import java.util.Set; |
| | | |
| | | /** |
| | | * 用户信息 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | @Data |
| | | public class LoginUser implements Serializable { |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | /** |
| | | * 用户唯一标识 |
| | | */ |
| | | private String token; |
| | | |
| | | /** |
| | | * 用户名id |
| | | */ |
| | | private Long userid; |
| | | |
| | | /** |
| | | * 用户名 |
| | | */ |
| | | private String username; |
| | | |
| | | /** |
| | | * 登录时间 |
| | | */ |
| | | private Long loginTime; |
| | | |
| | | /** |
| | | * 过期时间 |
| | | */ |
| | | private Long expireTime; |
| | | |
| | | /** |
| | | * 登录IP地址 |
| | | */ |
| | | private String ipaddr; |
| | | |
| | | /** |
| | | * 权限列表 |
| | | */ |
| | | private Set<String> permissions; |
| | | |
| | | /** |
| | | * 角色列表 |
| | | */ |
| | | private String role; |
| | | |
| | | /** |
| | | * 用户信息 |
| | | */ |
| | | private SysUser sysUser; |
| | | |
| | | } |
New file |
| | |
| | | package com.ruoyi.system.api.model; |
| | | |
| | | import lombok.Data; |
| | | |
| | | import java.io.Serializable; |
| | | |
| | | /** |
| | | * 用户信息 师傅端和用户端使用 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | @Data |
| | | public class LoginUserInfo implements Serializable { |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | /** |
| | | * 用户唯一标识 |
| | | */ |
| | | private String token; |
| | | /** |
| | | * 用户姓名 |
| | | */ |
| | | private String name; |
| | | |
| | | /** |
| | | * 用户名id |
| | | */ |
| | | private Integer userid; |
| | | |
| | | /** |
| | | * 用户名 |
| | | */ |
| | | private String phone; |
| | | /** |
| | | * 登录时间 |
| | | */ |
| | | private Long loginTime; |
| | | |
| | | /** |
| | | * 过期时间 |
| | | */ |
| | | private Long expireTime; |
| | | |
| | | /** |
| | | * 登录IP地址 |
| | | */ |
| | | private String ipaddr; |
| | | |
| | | } |
New file |
| | |
| | | package com.ruoyi.system.api.vo; |
| | | |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * 角色表 sys_role |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | @Data |
| | | @ApiModel(value = "权限列表查询返回VO") |
| | | public class SysRoleVO { |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | /** |
| | | * 角色ID |
| | | */ |
| | | @ApiModelProperty(value = "角色id") |
| | | private Long roleId; |
| | | |
| | | @ApiModelProperty(value = "权限名称") |
| | | private String roleName; |
| | | |
| | | @ApiModelProperty(value = "创建时间") |
| | | private String createTime; |
| | | |
| | | } |
New file |
| | |
| | | package com.ruoyi.system.api.vo; |
| | | |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * 角色表 sys_role |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | @Data |
| | | @ApiModel(value = "管理员列表查询返回VO") |
| | | public class SysUserVO |
| | | { |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | /** 角色ID */ |
| | | @ApiModelProperty(value = "账号id") |
| | | private Long userId; |
| | | @ApiModelProperty(value = "账号名称") |
| | | private String nickName; |
| | | @ApiModelProperty(value = "电话") |
| | | private String phonenumber; |
| | | @ApiModelProperty(value = "所属部门") |
| | | private String deptName; |
| | | @ApiModelProperty(value = "所属权限") |
| | | private String roleName; |
| | | @ApiModelProperty(value = "角色id") |
| | | private Long roleId; |
| | | @ApiModelProperty(value = "部门id") |
| | | private Long deptId; |
| | | } |
New file |
| | |
| | | com.ruoyi.system.api.factory.RemoteUserFallbackFactory |
| | | com.ruoyi.system.api.factory.RemoteLogFallbackFactory |
| | | com.ruoyi.system.api.factory.RemoteFileFallbackFactory |
| | | com.ruoyi.system.api.factory.SysUserFallbackFactory |
New file |
| | |
| | | target/ |
| | | !.mvn/wrapper/maven-wrapper.jar |
| | | !**/src/main/**/target/ |
| | | !**/src/test/**/target/ |
| | | |
| | | ### IntelliJ IDEA ### |
| | | .idea/modules.xml |
| | | .idea/jarRepositories.xml |
| | | .idea/compiler.xml |
| | | .idea/libraries/ |
| | | *.iws |
| | | *.iml |
| | | *.ipr |
| | | |
| | | ### Eclipse ### |
| | | .apt_generated |
| | | .classpath |
| | | .factorypath |
| | | .project |
| | | .settings |
| | | .springBeans |
| | | .sts4-cache |
| | | |
| | | ### NetBeans ### |
| | | /nbproject/private/ |
| | | /nbbuild/ |
| | | /dist/ |
| | | /nbdist/ |
| | | /.nb-gradle/ |
| | | build/ |
| | | !**/src/main/**/build/ |
| | | !**/src/test/**/build/ |
| | | |
| | | ### VS Code ### |
| | | .vscode/ |
| | | |
| | | ### Mac OS ### |
| | | .DS_Store |
New file |
| | |
| | | <?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> |
| | | <parent> |
| | | <groupId>com.ruoyi</groupId> |
| | | <artifactId>ruoyi</artifactId> |
| | | <version>3.6.4</version> |
| | | <relativePath>../../pom.xml</relativePath> |
| | | </parent> |
| | | |
| | | <artifactId>ruoyi-api-user</artifactId> |
| | | |
| | | <properties> |
| | | <maven.compiler.source>8</maven.compiler.source> |
| | | <maven.compiler.target>8</maven.compiler.target> |
| | | <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> |
| | | </properties> |
| | | |
| | | |
| | | <dependencies> |
| | | |
| | | <!-- RuoYi Common Core--> |
| | | <dependency> |
| | | <groupId>com.ruoyi</groupId> |
| | | <artifactId>ruoyi-common-core</artifactId> |
| | | <exclusions> |
| | | <exclusion> |
| | | <groupId>com.github.pagehelper</groupId> |
| | | <artifactId>pagehelper-spring-boot-starter</artifactId> |
| | | </exclusion> |
| | | </exclusions> |
| | | </dependency> |
| | | |
| | | </dependencies> |
| | | |
| | | </project> |
New file |
| | |
| | | package com.ruoyi.user.api.entity; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Getter; |
| | | import lombok.Setter; |
| | | |
| | | import java.util.Date; |
| | | |
| | | /** |
| | | * <p> |
| | | * 用户列表 |
| | | * </p> |
| | | * |
| | | * @author hjl |
| | | * @since 2024-05-29 |
| | | */ |
| | | @Getter |
| | | @Setter |
| | | @TableName("t_user") |
| | | @ApiModel(value = "User对象", description = "用户列表") |
| | | public class User { |
| | | |
| | | @TableId("id") |
| | | private Integer id; |
| | | |
| | | @ApiModelProperty("用户id") |
| | | @TableField("user_no") |
| | | private String userNo; |
| | | |
| | | @ApiModelProperty("用户昵称") |
| | | @TableField("nickname") |
| | | private String nickname; |
| | | |
| | | @ApiModelProperty("手机号") |
| | | @TableField("phone") |
| | | private String phone; |
| | | |
| | | @ApiModelProperty("头像") |
| | | @TableField("profile_picture") |
| | | private String profilePicture; |
| | | |
| | | @ApiModelProperty("账号状态 0未启用;1已启用") |
| | | @TableField("state") |
| | | private Integer state; |
| | | |
| | | @TableField("createBy") |
| | | private String createBy; |
| | | |
| | | @TableField("updateBy") |
| | | private String updateBy; |
| | | |
| | | @TableField("createTime") |
| | | private Date createTime; |
| | | |
| | | @TableField("updateTime") |
| | | private Date updateTime; |
| | | |
| | | @ApiModelProperty("是否删除 0未删除;1已删除") |
| | | @TableField("is_delete") |
| | | private Integer isDelete; |
| | | |
| | | |
| | | } |
New file |
| | |
| | | package com.ruoyi.user.api.factory; |
| | | |
| | | import com.ruoyi.user.api.feignClient.UserClient; |
| | | import org.springframework.cloud.openfeign.FallbackFactory; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | /** |
| | | * @author HJL |
| | | */ |
| | | @Component |
| | | public class UserFallbackFactory implements FallbackFactory<UserClient> { |
| | | |
| | | @Override |
| | | public UserClient create(Throwable cause) { |
| | | return null; |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.user.api.feignClient; |
| | | |
| | | import com.ruoyi.common.core.constant.ServiceNameConstants; |
| | | import com.ruoyi.user.api.factory.UserFallbackFactory; |
| | | import org.springframework.cloud.openfeign.FeignClient; |
| | | |
| | | /** |
| | | * @author HJL |
| | | * @since 2024.05.29 |
| | | */ |
| | | @FeignClient(contextId = "UserClient", value = ServiceNameConstants.USER_SERVICE, fallbackFactory = UserFallbackFactory.class) |
| | | public interface UserClient { |
| | | |
| | | } |
New file |
| | |
| | | com.ruoyi.user.api.factory.UserFallbackFactory |
New file |
| | |
| | | target/ |
| | | !.mvn/wrapper/maven-wrapper.jar |
| | | !**/src/main/**/target/ |
| | | !**/src/test/**/target/ |
| | | |
| | | ### IntelliJ IDEA ### |
| | | .idea/modules.xml |
| | | .idea/jarRepositories.xml |
| | | .idea/compiler.xml |
| | | .idea/libraries/ |
| | | *.iws |
| | | *.iml |
| | | *.ipr |
| | | |
| | | ### Eclipse ### |
| | | .apt_generated |
| | | .classpath |
| | | .factorypath |
| | | .project |
| | | .settings |
| | | .springBeans |
| | | .sts4-cache |
| | | |
| | | ### NetBeans ### |
| | | /nbproject/private/ |
| | | /nbbuild/ |
| | | /dist/ |
| | | /nbdist/ |
| | | /.nb-gradle/ |
| | | build/ |
| | | !**/src/main/**/build/ |
| | | !**/src/test/**/build/ |
| | | |
| | | ### VS Code ### |
| | | .vscode/ |
| | | |
| | | ### Mac OS ### |
| | | .DS_Store |
New file |
| | |
| | | <?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> |
| | | <parent> |
| | | <groupId>com.ruoyi</groupId> |
| | | <artifactId>ruoyi</artifactId> |
| | | <version>3.6.4</version> |
| | | <relativePath>../../pom.xml</relativePath> |
| | | </parent> |
| | | |
| | | <artifactId>ruoyi-api-worker</artifactId> |
| | | |
| | | <properties> |
| | | <maven.compiler.source>8</maven.compiler.source> |
| | | <maven.compiler.target>8</maven.compiler.target> |
| | | <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> |
| | | </properties> |
| | | |
| | | <dependencies> |
| | | |
| | | <!-- RuoYi Common Core--> |
| | | <dependency> |
| | | <groupId>com.ruoyi</groupId> |
| | | <artifactId>ruoyi-common-core</artifactId> |
| | | <exclusions> |
| | | <exclusion> |
| | | <groupId>com.github.pagehelper</groupId> |
| | | <artifactId>pagehelper-spring-boot-starter</artifactId> |
| | | </exclusion> |
| | | </exclusions> |
| | | </dependency> |
| | | |
| | | </dependencies> |
| | | |
| | | </project> |
New file |
| | |
| | | <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> |
| | | <groupId>com.ruoyi</groupId> |
| | | <artifactId>ruoyi</artifactId> |
| | | <version>3.6.4</version> |
| | | </parent> |
| | | <modelVersion>4.0.0</modelVersion> |
| | | |
| | | <artifactId>ruoyi-auth</artifactId> |
| | | |
| | | <description> |
| | | ruoyi-auth认证授权中心 |
| | | </description> |
| | | |
| | | <dependencies> |
| | | |
| | | <!-- SpringCloud Alibaba Nacos --> |
| | | <dependency> |
| | | <groupId>com.alibaba.cloud</groupId> |
| | | <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> |
| | | </dependency> |
| | | |
| | | <!-- SpringCloud Alibaba Nacos Config --> |
| | | <dependency> |
| | | <groupId>com.alibaba.cloud</groupId> |
| | | <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> |
| | | </dependency> |
| | | |
| | | <!-- SpringCloud Alibaba Sentinel --> |
| | | <dependency> |
| | | <groupId>com.alibaba.cloud</groupId> |
| | | <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> |
| | | </dependency> |
| | | |
| | | <!-- SpringBoot Web --> |
| | | <dependency> |
| | | <groupId>org.springframework.boot</groupId> |
| | | <artifactId>spring-boot-starter-web</artifactId> |
| | | </dependency> |
| | | |
| | | <!-- SpringBoot Actuator --> |
| | | <dependency> |
| | | <groupId>org.springframework.boot</groupId> |
| | | <artifactId>spring-boot-starter-actuator</artifactId> |
| | | </dependency> |
| | | |
| | | <!-- RuoYi Common Security--> |
| | | <dependency> |
| | | <groupId>com.ruoyi</groupId> |
| | | <artifactId>ruoyi-common-security</artifactId> |
| | | </dependency> |
| | | |
| | | </dependencies> |
| | | |
| | | <build> |
| | | <finalName>${project.artifactId}</finalName> |
| | | <plugins> |
| | | <plugin> |
| | | <groupId>org.springframework.boot</groupId> |
| | | <artifactId>spring-boot-maven-plugin</artifactId> |
| | | <executions> |
| | | <execution> |
| | | <goals> |
| | | <goal>repackage</goal> |
| | | </goals> |
| | | </execution> |
| | | </executions> |
| | | </plugin> |
| | | </plugins> |
| | | </build> |
| | | |
| | | </project> |
New file |
| | |
| | | package com.ruoyi.auth; |
| | | |
| | | import org.springframework.boot.SpringApplication; |
| | | import org.springframework.boot.autoconfigure.SpringBootApplication; |
| | | import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; |
| | | import com.ruoyi.common.security.annotation.EnableRyFeignClients; |
| | | |
| | | /** |
| | | * 认证授权中心 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | @EnableRyFeignClients |
| | | @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class }) |
| | | public class RuoYiAuthApplication |
| | | { |
| | | public static void main(String[] args) |
| | | { |
| | | SpringApplication.run(RuoYiAuthApplication.class, args); |
| | | System.out.println("(♥◠‿◠)ノ゙ 认证授权中心启动成功 ლ(´ڡ`ლ)゙ \n" + |
| | | " .-------. ____ __ \n" + |
| | | " | _ _ \\ \\ \\ / / \n" + |
| | | " | ( ' ) | \\ _. / ' \n" + |
| | | " |(_ o _) / _( )_ .' \n" + |
| | | " | (_,_).' __ ___(_ o _)' \n" + |
| | | " | |\\ \\ | || |(_,_)' \n" + |
| | | " | | \\ `' /| `-' / \n" + |
| | | " | | \\ / \\ / \n" + |
| | | " ''-' `'-' `-..-' "); |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.auth.controller; |
| | | |
| | | import com.ruoyi.auth.form.LoginBody; |
| | | import com.ruoyi.auth.service.SysLoginService; |
| | | import com.ruoyi.common.core.domain.R; |
| | | import com.ruoyi.common.core.utils.JwtUtils; |
| | | import com.ruoyi.common.core.utils.StringUtils; |
| | | import com.ruoyi.common.security.auth.AuthUtil; |
| | | import com.ruoyi.common.security.service.TokenService; |
| | | import com.ruoyi.common.security.utils.SecurityUtils; |
| | | import com.ruoyi.system.api.client.SysUserClient; |
| | | import com.ruoyi.system.api.domain.SysUser; |
| | | import com.ruoyi.system.api.model.LoginUser; |
| | | import io.swagger.annotations.Api; |
| | | import io.swagger.annotations.ApiOperation; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.web.bind.annotation.DeleteMapping; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | | import org.springframework.web.bind.annotation.RequestBody; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | import javax.annotation.Resource; |
| | | import javax.servlet.http.HttpServletRequest; |
| | | import java.util.Date; |
| | | import java.util.HashMap; |
| | | |
| | | /** |
| | | * token 控制 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | @RestController |
| | | @Api(tags = "后台-登录") |
| | | public class TokenController { |
| | | @Autowired |
| | | private TokenService tokenService; |
| | | |
| | | @Autowired |
| | | private SysLoginService sysLoginService; |
| | | |
| | | @Resource |
| | | private SysUserClient sysUserClient; |
| | | |
| | | @PostMapping("login") |
| | | @ApiOperation(value = "登录", tags = "后台-登录") |
| | | public R<?> login(@RequestBody LoginBody form) { |
| | | // 用户登录 |
| | | LoginUser userInfo = sysLoginService.login(form.getUsername(), form.getPassword()); |
| | | HashMap<String, Object> map = new HashMap<>(8); |
| | | map.put("token", tokenService.createToken(userInfo)); |
| | | // 获取登录token |
| | | String roles = userInfo.getRole(); |
| | | if (null == roles) { |
| | | return R.fail("请关联角色!"); |
| | | } |
| | | map.put("roleName", roles); |
| | | map.put("info", userInfo); |
| | | // 修改用户最后登录时间 |
| | | SysUser sysUser = new SysUser(); |
| | | sysUser.setUserId(userInfo.getSysUser().getUserId()); |
| | | sysUser.setLoginDate(new Date()); |
| | | sysUserClient.updateSysUser(sysUser); |
| | | return R.ok(map); |
| | | } |
| | | |
| | | @DeleteMapping("logout") |
| | | @ApiOperation(value = "退出登录", tags = "后台-登录") |
| | | public R<?> logout(HttpServletRequest request) { |
| | | String token = SecurityUtils.getToken(request); |
| | | if (StringUtils.isNotEmpty(token)) { |
| | | String username = JwtUtils.getUserName(token); |
| | | // 删除用户缓存记录 |
| | | AuthUtil.logoutByToken(token); |
| | | // 记录用户退出日志 |
| | | sysLoginService.logout(username); |
| | | } |
| | | return R.ok(); |
| | | } |
| | | |
| | | @PostMapping("refresh") |
| | | @ApiOperation(value = "刷新token", tags = "后台-登录") |
| | | public R<?> refresh(HttpServletRequest request) { |
| | | LoginUser loginUser = tokenService.getLoginUser(request); |
| | | if (StringUtils.isNotNull(loginUser)) { |
| | | // 刷新令牌有效期 |
| | | tokenService.refreshToken(loginUser); |
| | | return R.ok(); |
| | | } |
| | | return R.ok(); |
| | | } |
| | | |
| | | } |
New file |
| | |
| | | package com.ruoyi.auth.form; |
| | | |
| | | /** |
| | | * 用户登录对象 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class LoginBody { |
| | | /** |
| | | * 用户名 |
| | | */ |
| | | private String username; |
| | | |
| | | /** |
| | | * 用户密码 |
| | | */ |
| | | private String password; |
| | | |
| | | 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; |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.auth.form; |
| | | |
| | | /** |
| | | * 用户注册对象 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class RegisterBody extends LoginBody |
| | | { |
| | | |
| | | } |
New file |
| | |
| | | package com.ruoyi.auth.service; |
| | | |
| | | import com.ruoyi.common.core.constant.Constants; |
| | | import com.ruoyi.common.core.constant.SecurityConstants; |
| | | import com.ruoyi.common.core.constant.UserConstants; |
| | | import com.ruoyi.common.core.domain.R; |
| | | import com.ruoyi.common.core.enums.UserStatus; |
| | | import com.ruoyi.common.core.exception.ServiceException; |
| | | import com.ruoyi.common.core.utils.StringUtils; |
| | | import com.ruoyi.common.security.utils.SecurityUtils; |
| | | import com.ruoyi.system.api.RemoteUserService; |
| | | import com.ruoyi.system.api.domain.SysUser; |
| | | import com.ruoyi.system.api.model.LoginUser; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | /** |
| | | * 登录校验方法 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | @Component |
| | | public class SysLoginService { |
| | | @Autowired |
| | | private RemoteUserService remoteUserService; |
| | | |
| | | @Autowired |
| | | private SysPasswordService passwordService; |
| | | |
| | | @Autowired |
| | | private SysRecordLogService recordLogService; |
| | | |
| | | /** |
| | | * 登录 |
| | | */ |
| | | public LoginUser login(String username, String password) { |
| | | // 用户名或密码为空 错误 |
| | | if (StringUtils.isAnyBlank(username, password)) { |
| | | recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户/密码必须填写"); |
| | | throw new ServiceException("用户/密码必须填写"); |
| | | } |
| | | // 密码如果不在指定范围内 错误 |
| | | if (password.length() < UserConstants.PASSWORD_MIN_LENGTH |
| | | || password.length() > UserConstants.PASSWORD_MAX_LENGTH) { |
| | | recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户密码不在指定范围"); |
| | | throw new ServiceException("用户密码不在指定范围"); |
| | | } |
| | | // 用户名不在指定范围内 错误 |
| | | if (username.length() < UserConstants.USERNAME_MIN_LENGTH |
| | | || username.length() > UserConstants.USERNAME_MAX_LENGTH) { |
| | | recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户名不在指定范围"); |
| | | throw new ServiceException("用户名不在指定范围"); |
| | | } |
| | | // 查询用户信息 |
| | | R<LoginUser> userResult = remoteUserService.getUserInfo(username, SecurityConstants.INNER); |
| | | |
| | | if (StringUtils.isNull(userResult) || StringUtils.isNull(userResult.getData())) { |
| | | recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "登录用户不存在"); |
| | | throw new ServiceException("登录用户:" + username + " 不存在"); |
| | | } |
| | | |
| | | if (R.FAIL == userResult.getCode()) { |
| | | throw new ServiceException(userResult.getMsg()); |
| | | } |
| | | |
| | | LoginUser userInfo = userResult.getData(); |
| | | SysUser user = userResult.getData().getSysUser(); |
| | | if (UserStatus.DELETED.getCode().equals(user.getIsDelete())) { |
| | | recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "对不起,您的账号已被删除"); |
| | | throw new ServiceException("对不起,您的账号:" + username + " 已被删除"); |
| | | } |
| | | if (!UserStatus.ENABLE.getCode().equals(user.getIsEnable())) { |
| | | recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户已停用,请联系管理员"); |
| | | throw new ServiceException("对不起,您的账号:" + username + " 已停用"); |
| | | } |
| | | passwordService.validate(user, password); |
| | | recordLogService.recordLogininfor(username, Constants.LOGIN_SUCCESS, "登录成功"); |
| | | return userInfo; |
| | | } |
| | | |
| | | public void logout(String loginName) { |
| | | recordLogService.recordLogininfor(loginName, Constants.LOGOUT, "退出成功"); |
| | | } |
| | | |
| | | /** |
| | | * 注册 |
| | | */ |
| | | public void register(String username, String password) { |
| | | // 用户名或密码为空 错误 |
| | | if (StringUtils.isAnyBlank(username, password)) { |
| | | throw new ServiceException("用户/密码必须填写"); |
| | | } |
| | | if (username.length() < UserConstants.USERNAME_MIN_LENGTH |
| | | || username.length() > UserConstants.USERNAME_MAX_LENGTH) { |
| | | throw new ServiceException("账户长度必须在2到20个字符之间"); |
| | | } |
| | | if (password.length() < UserConstants.PASSWORD_MIN_LENGTH |
| | | || password.length() > UserConstants.PASSWORD_MAX_LENGTH) { |
| | | throw new ServiceException("密码长度必须在5到20个字符之间"); |
| | | } |
| | | |
| | | // 注册用户信息 |
| | | SysUser sysUser = new SysUser(); |
| | | sysUser.setAccount(username); |
| | | sysUser.setNickName(username); |
| | | sysUser.setPassword(SecurityUtils.encryptPassword(password)); |
| | | R<?> registerResult = remoteUserService.registerUserInfo(sysUser, SecurityConstants.INNER); |
| | | |
| | | if (R.FAIL == registerResult.getCode()) { |
| | | throw new ServiceException(registerResult.getMsg()); |
| | | } |
| | | recordLogService.recordLogininfor(username, Constants.REGISTER, "注册成功"); |
| | | } |
| | | |
| | | } |
New file |
| | |
| | | package com.ruoyi.auth.service; |
| | | |
| | | import com.ruoyi.common.core.constant.CacheConstants; |
| | | import com.ruoyi.common.core.constant.Constants; |
| | | import com.ruoyi.common.core.exception.ServiceException; |
| | | import com.ruoyi.common.redis.service.RedisService; |
| | | import com.ruoyi.common.security.utils.SecurityUtils; |
| | | import com.ruoyi.system.api.domain.SysUser; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | import java.util.concurrent.TimeUnit; |
| | | |
| | | /** |
| | | * 登录密码方法 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | @Component |
| | | public class SysPasswordService { |
| | | @Autowired |
| | | private RedisService redisService; |
| | | |
| | | private int maxRetryCount = CacheConstants.PASSWORD_MAX_RETRY_COUNT; |
| | | |
| | | private Long lockTime = CacheConstants.PASSWORD_LOCK_TIME; |
| | | |
| | | @Autowired |
| | | private SysRecordLogService recordLogService; |
| | | |
| | | /** |
| | | * 登录账户密码错误次数缓存键名 |
| | | * |
| | | * @param username 用户名 |
| | | * @return 缓存键key |
| | | */ |
| | | private String getCacheKey(String username) { |
| | | return CacheConstants.PWD_ERR_CNT_KEY + username; |
| | | } |
| | | |
| | | public void validate(SysUser user, String password) { |
| | | String username = user.getNickName(); |
| | | |
| | | Integer retryCount = redisService.getCacheObject(getCacheKey(username)); |
| | | |
| | | if (retryCount == null) { |
| | | retryCount = 0; |
| | | } |
| | | |
| | | if (retryCount >= Integer.valueOf(maxRetryCount).intValue()) { |
| | | String errMsg = String.format("密码输入错误%s次,帐户锁定%s分钟", maxRetryCount, lockTime); |
| | | recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, errMsg); |
| | | throw new ServiceException(errMsg); |
| | | } |
| | | |
| | | if (!matches(user, password)) { |
| | | retryCount = retryCount + 1; |
| | | recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, String.format("密码输入错误%s次", retryCount)); |
| | | redisService.setCacheObject(getCacheKey(username), retryCount, lockTime, TimeUnit.MINUTES); |
| | | throw new ServiceException("用户不存在/密码错误"); |
| | | } else { |
| | | clearLoginRecordCache(username); |
| | | } |
| | | } |
| | | |
| | | public boolean matches(SysUser user, String rawPassword) { |
| | | return SecurityUtils.matchesPassword(rawPassword, user.getPassword()); |
| | | } |
| | | |
| | | public void clearLoginRecordCache(String loginName) { |
| | | if (redisService.hasKey(getCacheKey(loginName))) { |
| | | redisService.deleteObject(getCacheKey(loginName)); |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.auth.service; |
| | | |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Component; |
| | | import com.ruoyi.common.core.constant.Constants; |
| | | import com.ruoyi.common.core.constant.SecurityConstants; |
| | | import com.ruoyi.common.core.utils.StringUtils; |
| | | import com.ruoyi.common.core.utils.ip.IpUtils; |
| | | import com.ruoyi.system.api.RemoteLogService; |
| | | import com.ruoyi.system.api.domain.SysLogininfor; |
| | | |
| | | /** |
| | | * 记录日志方法 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | @Component |
| | | public class SysRecordLogService { |
| | | @Autowired |
| | | private RemoteLogService remoteLogService; |
| | | |
| | | /** |
| | | * 记录登录信息 |
| | | * |
| | | * @param username 用户名 |
| | | * @param status 状态 |
| | | * @param message 消息内容 |
| | | * @return |
| | | */ |
| | | public void recordLogininfor(String username, String status, String message) { |
| | | SysLogininfor logininfor = new SysLogininfor(); |
| | | logininfor.setUserName(username); |
| | | logininfor.setIpaddr(IpUtils.getIpAddr()); |
| | | logininfor.setMsg(message); |
| | | // 日志状态 |
| | | if (StringUtils.equalsAny(status, Constants.LOGIN_SUCCESS, Constants.LOGOUT, Constants.REGISTER)) { |
| | | logininfor.setStatus(Constants.LOGIN_SUCCESS_STATUS); |
| | | } else if (Constants.LOGIN_FAIL.equals(status)) { |
| | | logininfor.setStatus(Constants.LOGIN_FAIL_STATUS); |
| | | } |
| | | remoteLogService.saveLogininfor(logininfor, SecurityConstants.INNER); |
| | | } |
| | | } |
New file |
| | |
| | | Spring Boot Version: ${spring-boot.version} |
| | | Spring Application Name: ${spring.application.name} |
| | | _ _ _ |
| | | (_) | | | | |
| | | _ __ _ _ ___ _ _ _ ______ __ _ _ _ | |_ | |__ |
| | | | '__|| | | | / _ \ | | | || ||______| / _` || | | || __|| '_ \ |
| | | | | | |_| || (_) || |_| || | | (_| || |_| || |_ | | | | |
| | | |_| \__,_| \___/ \__, ||_| \__,_| \__,_| \__||_| |_| |
| | | __/ | |
| | | |___/ |
New file |
| | |
| | | # Spring |
| | | spring: |
| | | application: |
| | | # 应用名称 |
| | | name: ruoyi-auth |
| | | main: |
| | | allow-bean-definition-overriding: true |
| | | profiles: |
| | | # 环境配置 |
| | | active: dev |
| | | |
| | | --- |
| | | spring: |
| | | config: |
| | | activate: |
| | | on-profile: dev |
| | | cloud: |
| | | nacos: |
| | | discovery: |
| | | # 服务注册地址 |
| | | server-addr: 127.0.0.1:8848 |
| | | service: ${spring.application.name} |
| | | group: DEFAULT_GROUP |
| | | namespace: 5b0cc96f-d058-4986-a46f-64644ff8a467 |
| | | config: |
| | | # 配置中心地址 |
| | | server-addr: 127.0.0.1:8848 |
| | | group: DEFAULT_GROUP |
| | | namespace: 5b0cc96f-d058-4986-a46f-64644ff8a467 |
| | | name: ${spring.application.name} |
| | | # 配置文件格式 |
| | | file-extension: yml |
| | | # 共享配置 |
| | | shared-configs: |
| | | - application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} |
New file |
| | |
| | | <?xml version="1.0" encoding="UTF-8"?> |
| | | <configuration scan="true" scanPeriod="60 seconds" debug="false"> |
| | | <!-- 日志存放路径 --> |
| | | <property name="log.path" value="logs/ruoyi-auth" /> |
| | | <!-- 日志输出格式 --> |
| | | <property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" /> |
| | | |
| | | <!-- 控制台输出 --> |
| | | <appender name="console" class="ch.qos.logback.core.ConsoleAppender"> |
| | | <encoder> |
| | | <pattern>${log.pattern}</pattern> |
| | | </encoder> |
| | | </appender> |
| | | |
| | | <!-- 系统日志输出 --> |
| | | <appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender"> |
| | | <file>${log.path}/info.log</file> |
| | | <!-- 循环政策:基于时间创建日志文件 --> |
| | | <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> |
| | | <!-- 日志文件名格式 --> |
| | | <fileNamePattern>${log.path}/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}/error.log</file> |
| | | <!-- 循环政策:基于时间创建日志文件 --> |
| | | <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> |
| | | <!-- 日志文件名格式 --> |
| | | <fileNamePattern>${log.path}/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> |
| | | |
| | | <!-- 系统模块日志级别控制 --> |
| | | <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> |
| | | </configuration> |
New file |
| | |
| | | <?xml version="1.0" encoding="UTF-8"?> |
| | | <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" |
| | | xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
| | | <parent> |
| | | <groupId>com.ruoyi</groupId> |
| | | <artifactId>ruoyi</artifactId> |
| | | <version>3.6.4</version> |
| | | </parent> |
| | | <modelVersion>4.0.0</modelVersion> |
| | | |
| | | <modules> |
| | | <module>ruoyi-common-log</module> |
| | | <module>ruoyi-common-core</module> |
| | | <module>ruoyi-common-redis</module> |
| | | <module>ruoyi-common-seata</module> |
| | | <module>ruoyi-common-swagger</module> |
| | | <module>ruoyi-common-security</module> |
| | | <module>ruoyi-common-sensitive</module> |
| | | <module>ruoyi-common-datascope</module> |
| | | <module>ruoyi-common-datasource</module> |
| | | </modules> |
| | | |
| | | <artifactId>ruoyi-common</artifactId> |
| | | <packaging>pom</packaging> |
| | | |
| | | <description> |
| | | ruoyi-common通用模块 |
| | | </description> |
| | | |
| | | <dependencies> |
| | | <!--lombok--> |
| | | <dependency> |
| | | <groupId>org.projectlombok</groupId> |
| | | <artifactId>lombok</artifactId> |
| | | </dependency> |
| | | </dependencies> |
| | | |
| | | </project> |
New file |
| | |
| | | <?xml version="1.0" encoding="UTF-8"?> |
| | | <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
| | | xmlns="http://maven.apache.org/POM/4.0.0" |
| | | xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
| | | <parent> |
| | | <groupId>com.ruoyi</groupId> |
| | | <artifactId>ruoyi-common</artifactId> |
| | | <version>3.6.4</version> |
| | | </parent> |
| | | <modelVersion>4.0.0</modelVersion> |
| | | |
| | | <artifactId>ruoyi-common-core</artifactId> |
| | | |
| | | <description> |
| | | ruoyi-common-core核心模块 |
| | | </description> |
| | | |
| | | <dependencies> |
| | | |
| | | <!-- SpringCloud Openfeign --> |
| | | <dependency> |
| | | <groupId>org.springframework.cloud</groupId> |
| | | <artifactId>spring-cloud-starter-openfeign</artifactId> |
| | | </dependency> |
| | | |
| | | <!-- SpringCloud Loadbalancer --> |
| | | <dependency> |
| | | <groupId>org.springframework.cloud</groupId> |
| | | <artifactId>spring-cloud-starter-loadbalancer</artifactId> |
| | | </dependency> |
| | | |
| | | <!-- Spring Context Support --> |
| | | <dependency> |
| | | <groupId>org.springframework</groupId> |
| | | <artifactId>spring-context-support</artifactId> |
| | | </dependency> |
| | | |
| | | <!-- Spring Web --> |
| | | <dependency> |
| | | <groupId>org.springframework</groupId> |
| | | <artifactId>spring-web</artifactId> |
| | | </dependency> |
| | | |
| | | <!-- Transmittable ThreadLocal --> |
| | | <dependency> |
| | | <groupId>com.alibaba</groupId> |
| | | <artifactId>transmittable-thread-local</artifactId> |
| | | </dependency> |
| | | |
| | | <!-- Pagehelper --> |
| | | <dependency> |
| | | <groupId>com.github.pagehelper</groupId> |
| | | <artifactId>pagehelper-spring-boot-starter</artifactId> |
| | | </dependency> |
| | | |
| | | <!-- Hibernate Validator --> |
| | | <dependency> |
| | | <groupId>org.springframework.boot</groupId> |
| | | <artifactId>spring-boot-starter-validation</artifactId> |
| | | </dependency> |
| | | |
| | | <!-- Jackson --> |
| | | <dependency> |
| | | <groupId>com.fasterxml.jackson.core</groupId> |
| | | <artifactId>jackson-databind</artifactId> |
| | | </dependency> |
| | | |
| | | <!-- Alibaba Fastjson --> |
| | | <dependency> |
| | | <groupId>com.alibaba.fastjson2</groupId> |
| | | <artifactId>fastjson2</artifactId> |
| | | </dependency> |
| | | |
| | | <!-- Jwt --> |
| | | <dependency> |
| | | <groupId>io.jsonwebtoken</groupId> |
| | | <artifactId>jjwt</artifactId> |
| | | </dependency> |
| | | |
| | | <!-- Jaxb --> |
| | | <dependency> |
| | | <groupId>javax.xml.bind</groupId> |
| | | <artifactId>jaxb-api</artifactId> |
| | | </dependency> |
| | | |
| | | <!-- Apache Lang3 --> |
| | | <dependency> |
| | | <groupId>org.apache.commons</groupId> |
| | | <artifactId>commons-lang3</artifactId> |
| | | </dependency> |
| | | |
| | | <!-- Commons Io --> |
| | | <dependency> |
| | | <groupId>commons-io</groupId> |
| | | <artifactId>commons-io</artifactId> |
| | | </dependency> |
| | | |
| | | <!-- excel工具 --> |
| | | <dependency> |
| | | <groupId>org.apache.poi</groupId> |
| | | <artifactId>poi-ooxml</artifactId> |
| | | </dependency> |
| | | |
| | | <!-- Java Servlet --> |
| | | <dependency> |
| | | <groupId>javax.servlet</groupId> |
| | | <artifactId>javax.servlet-api</artifactId> |
| | | </dependency> |
| | | |
| | | <!-- Swagger --> |
| | | <dependency> |
| | | <groupId>io.swagger</groupId> |
| | | <artifactId>swagger-annotations</artifactId> |
| | | </dependency> |
| | | |
| | | <!--mybatis-plus--> |
| | | <dependency> |
| | | <groupId>com.baomidou</groupId> |
| | | <artifactId>mybatis-plus-boot-starter</artifactId> |
| | | <version>3.5.2</version> |
| | | </dependency> |
| | | |
| | | |
| | | <!--华为云的obs--> |
| | | <dependency> |
| | | <groupId>com.huaweicloud</groupId> |
| | | <artifactId>esdk-obs-java</artifactId> |
| | | <version>3.19.5</version> |
| | | </dependency> |
| | | |
| | | <dependency> |
| | | <groupId>com.squareup.okhttp3</groupId> |
| | | <artifactId>okhttp</artifactId> |
| | | <version>4.9.3</version> |
| | | </dependency> |
| | | |
| | | <dependency> |
| | | <groupId>com.squareup.okio</groupId> |
| | | <artifactId>okio</artifactId> |
| | | <version>2.8.0</version> |
| | | </dependency> |
| | | |
| | | </dependencies> |
| | | |
| | | </project> |
New file |
| | |
| | | package com.ruoyi.common.core.annotation; |
| | | |
| | | import java.lang.annotation.ElementType; |
| | | import java.lang.annotation.Retention; |
| | | import java.lang.annotation.RetentionPolicy; |
| | | import java.lang.annotation.Target; |
| | | import java.math.BigDecimal; |
| | | import org.apache.poi.ss.usermodel.HorizontalAlignment; |
| | | import org.apache.poi.ss.usermodel.IndexedColors; |
| | | import com.ruoyi.common.core.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 ""; |
| | | |
| | | /** |
| | | * 读取内容转表达式 (如: 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 int roundingMode() default BigDecimal.ROUND_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 String[] combo() default {}; |
| | | |
| | | /** |
| | | * 是否需要纵向合并单元格,应对需求:含有list集合单元格) |
| | | */ |
| | | public boolean needMerge() default false; |
| | | |
| | | /** |
| | | * 是否导出数据,应对需求:有时我们需要导出一份模板,这是标题需要但内容需要用户手工填写. |
| | | */ |
| | | public boolean isExport() default true; |
| | | |
| | | /** |
| | | * 另一个类中的属性名称,支持多级获取,以小数点隔开 |
| | | */ |
| | | public String targetAttr() default ""; |
| | | |
| | | /** |
| | | * 是否自动统计数据,在最后追加一行统计数据总和 |
| | | */ |
| | | public boolean isStatistics() default false; |
| | | |
| | | /** |
| | | * 导出类型(0数字 1字符串) |
| | | */ |
| | | 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; |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.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 |
| | | { |
| | | Excel[] value(); |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.constant; |
| | | |
| | | /** |
| | | * 缓存常量信息 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class CacheConstants |
| | | { |
| | | /** |
| | | * 缓存有效期,默认720(分钟) |
| | | */ |
| | | public final static long EXPIRATION = 720; |
| | | |
| | | /** |
| | | * 缓存刷新时间,默认120(分钟) |
| | | */ |
| | | public final static long REFRESH_TIME = 120; |
| | | |
| | | /** |
| | | * 密码最大错误次数 |
| | | */ |
| | | public final static int PASSWORD_MAX_RETRY_COUNT = 5; |
| | | |
| | | /** |
| | | * 密码锁定时间,默认10(分钟) |
| | | */ |
| | | public final static long PASSWORD_LOCK_TIME = 10; |
| | | |
| | | /** |
| | | * 权限缓存前缀 |
| | | */ |
| | | public final static String LOGIN_TOKEN_KEY = "login_tokens:"; |
| | | |
| | | /** |
| | | * 用户端缓存前缀 |
| | | */ |
| | | public final static String LOGIN_USER_TOKEN_KEY = "login_user:"; |
| | | |
| | | /** |
| | | * 师傅端缓存前缀 |
| | | */ |
| | | public final static String LOGIN_WORKER_TOKEN_KEY = "login_worker:"; |
| | | |
| | | /** |
| | | * 验证码 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 PWD_ERR_CNT_KEY = "pwd_err_cnt:"; |
| | | |
| | | /** |
| | | * 登录IP黑名单 cache key |
| | | */ |
| | | public static final String SYS_LOGIN_BLACKIPLIST = SYS_CONFIG_KEY + "sys.login.blackIPList"; |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.constant; |
| | | |
| | | /** |
| | | * 通用常量信息 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class Constants { |
| | | /** |
| | | * 微信获取授权openId地址 |
| | | */ |
| | | public static final String WX_OPENID_ADDRESS = "https://api.weixin.qq.com/sns/jscode2sess"; |
| | | |
| | | /** |
| | | * UTF-8 字符集 |
| | | */ |
| | | public static final String UTF8 = "UTF-8"; |
| | | |
| | | /** |
| | | * GBK 字符集 |
| | | */ |
| | | public static final String GBK = "GBK"; |
| | | |
| | | /** |
| | | * www主域 |
| | | */ |
| | | public static final String WWW = "www."; |
| | | |
| | | /** |
| | | * RMI 远程方法调用 |
| | | */ |
| | | public static final String LOOKUP_RMI = "rmi:"; |
| | | |
| | | /** |
| | | * LDAP 远程方法调用 |
| | | */ |
| | | public static final String LOOKUP_LDAP = "ldap:"; |
| | | |
| | | /** |
| | | * LDAPS 远程方法调用 |
| | | */ |
| | | public static final String LOOKUP_LDAPS = "ldaps:"; |
| | | |
| | | /** |
| | | * http请求 |
| | | */ |
| | | public static final String HTTP = "http://"; |
| | | |
| | | /** |
| | | * https请求 |
| | | */ |
| | | public static final String HTTPS = "https://"; |
| | | |
| | | /** |
| | | * 成功标记 |
| | | */ |
| | | public static final Integer SUCCESS = 200; |
| | | |
| | | /** |
| | | * 失败标记 |
| | | */ |
| | | public static final Integer FAIL = 500; |
| | | |
| | | /** |
| | | * 登录成功状态 |
| | | */ |
| | | public static final String LOGIN_SUCCESS_STATUS = "0"; |
| | | |
| | | /** |
| | | * 登录失败状态 |
| | | */ |
| | | public static final String LOGIN_FAIL_STATUS = "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 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 long CAPTCHA_EXPIRATION = 2; |
| | | |
| | | /** |
| | | * 资源映射路径 前缀 |
| | | */ |
| | | public static final String RESOURCE_PREFIX = "/profile"; |
| | | |
| | | /** |
| | | * 自动识别json对象白名单配置(仅允许解析的包名,范围越小越安全) |
| | | */ |
| | | public static final String[] JSON_WHITELIST_STR = {"org.springframework", "com.ruoyi"}; |
| | | |
| | | /** |
| | | * 定时任务白名单配置(仅允许访问的包名,如其他需要可以自行添加) |
| | | */ |
| | | public static final String[] JOB_WHITELIST_STR = {"com.ruoyi.job.task"}; |
| | | |
| | | /** |
| | | * 定时任务违规的字符 |
| | | */ |
| | | public static final String[] JOB_ERROR_STR = {"java.net.URL", "javax.naming.InitialContext", "org.yaml.snakeyaml", |
| | | "org.springframework", "org.apache", "com.ruoyi.common.core.utils.file"}; |
| | | |
| | | /** |
| | | * 提现记录excel模板名称 |
| | | */ |
| | | public static final String EXCEL_WITHDRAWAL_FILE_NAME = "提现记录"; |
| | | |
| | | /** |
| | | * 订单记录excel模板名称 |
| | | */ |
| | | public static final String EXCEL_ORDER_FILE_NAME = "订单记录"; |
| | | |
| | | /** |
| | | * 文件上传常量后缀 |
| | | */ |
| | | public static final String APK = "apk"; |
| | | |
| | | /** |
| | | * 用户编号生成前缀 |
| | | */ |
| | | public static final String USER_NO_PRE = "用户%s"; |
| | | |
| | | /** |
| | | * 待审核 数字状态 |
| | | */ |
| | | public static final Integer AUDIT_NUMBER = 0; |
| | | |
| | | /** |
| | | * 待审核 中文状态 |
| | | */ |
| | | public static final String AUDIT_STR = "待审核"; |
| | | |
| | | /** |
| | | * 已通过 数字状态 |
| | | */ |
| | | public static final Integer PASS_THROUGH_NUMBER = 1; |
| | | |
| | | /** |
| | | * 已通过 中文状态 |
| | | */ |
| | | public static final String PASS_THROUGH_STR = "已通过"; |
| | | |
| | | /** |
| | | * 已驳回 数字状态 |
| | | */ |
| | | public static final Integer REJECT_NUMBER = 2; |
| | | |
| | | /** |
| | | * 已驳回 中文状态 |
| | | */ |
| | | public static final String REJECT_STR = "已驳回"; |
| | | |
| | | /** |
| | | * 数字60 |
| | | */ |
| | | public static final Integer SIXTY = 60; |
| | | |
| | | /** |
| | | * 数字0 |
| | | */ |
| | | public static final Integer ZERO = 0; |
| | | |
| | | /** |
| | | * 数字1 |
| | | */ |
| | | public static final Integer ONE = 1; |
| | | |
| | | /** |
| | | * 数字2 |
| | | */ |
| | | public static final Integer TWO = 2; |
| | | |
| | | /** |
| | | * 数字3 |
| | | */ |
| | | public static final Integer THREE = 3; |
| | | |
| | | /** |
| | | * 数字5 |
| | | */ |
| | | public static final Integer FIVE = 5; |
| | | |
| | | /** |
| | | * 数字6 |
| | | */ |
| | | public static final Integer SIX = 6; |
| | | |
| | | /** |
| | | * 数字18 |
| | | */ |
| | | public static final Integer EIGHTEEN = 18; |
| | | |
| | | /** |
| | | * 数字18 |
| | | */ |
| | | public static final Integer EIGHT = 18; |
| | | |
| | | /** |
| | | * 数字 3000 |
| | | */ |
| | | public static final Integer THREE_THOUSAND = 3000; |
| | | |
| | | /** |
| | | * 师傅端默认用户ID前缀 |
| | | */ |
| | | public static final String DEFAULT_USER_NUMBER_PRE = "用户"; |
| | | |
| | | /** |
| | | * 师傅端默认密码 |
| | | */ |
| | | public static final String DEFAULT_PASSWORD = "123456"; |
| | | |
| | | /** |
| | | * 手机号码正则 |
| | | */ |
| | | public static final String PHONE = ("^((13[0-9])|(14[0,1,4-9])|(15[0-3,5-9])|(16[2,5,6,7])|(17[0-8])|(18[0-9])|(19[0-3,5-9]))\\d{8}$"); |
| | | |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.constant; |
| | | |
| | | /** |
| | | * 代码生成通用常量 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class GenConstants |
| | | { |
| | | /** 单表(增删改查) */ |
| | | public static final String TPL_CRUD = "crud"; |
| | | |
| | | /** 树表(增删改查) */ |
| | | public static final String TPL_TREE = "tree"; |
| | | |
| | | /** 主子表(增删改查) */ |
| | | public static final String TPL_SUB = "sub"; |
| | | |
| | | /** 树编码字段 */ |
| | | public static final String TREE_CODE = "treeCode"; |
| | | |
| | | /** 树父编码字段 */ |
| | | public static final String TREE_PARENT_CODE = "treeParentCode"; |
| | | |
| | | /** 树名称字段 */ |
| | | public static final String TREE_NAME = "treeName"; |
| | | |
| | | /** 上级菜单ID字段 */ |
| | | public static final String PARENT_MENU_ID = "parentMenuId"; |
| | | |
| | | /** 上级菜单名称字段 */ |
| | | public static final String PARENT_MENU_NAME = "parentMenuName"; |
| | | |
| | | /** 数据库字符串类型 */ |
| | | public static final String[] COLUMNTYPE_STR = { "char", "varchar", "nvarchar", "varchar2" }; |
| | | |
| | | /** 数据库文本类型 */ |
| | | public static final String[] COLUMNTYPE_TEXT = { "tinytext", "text", "mediumtext", "longtext" }; |
| | | |
| | | /** 数据库时间类型 */ |
| | | public static final String[] COLUMNTYPE_TIME = { "datetime", "time", "date", "timestamp" }; |
| | | |
| | | /** 数据库数字类型 */ |
| | | public static final String[] COLUMNTYPE_NUMBER = { "tinyint", "smallint", "mediumint", "int", "number", "integer", |
| | | "bit", "bigint", "float", "double", "decimal" }; |
| | | |
| | | /** 页面不需要编辑字段 */ |
| | | public static final String[] COLUMNNAME_NOT_EDIT = { "id", "create_by", "create_time", "del_flag" }; |
| | | |
| | | /** 页面不需要显示的列表字段 */ |
| | | public static final String[] COLUMNNAME_NOT_LIST = { "id", "create_by", "create_time", "del_flag", "update_by", |
| | | "update_time" }; |
| | | |
| | | /** 页面不需要查询字段 */ |
| | | public static final String[] COLUMNNAME_NOT_QUERY = { "id", "create_by", "create_time", "del_flag", "update_by", |
| | | "update_time", "remark" }; |
| | | |
| | | /** Entity基类字段 */ |
| | | public static final String[] BASE_ENTITY = { "createBy", "createTime", "updateBy", "updateTime", "remark" }; |
| | | |
| | | /** Tree基类字段 */ |
| | | public static final String[] TREE_ENTITY = { "parentName", "parentId", "orderNum", "ancestors" }; |
| | | |
| | | /** 文本框 */ |
| | | public static final String HTML_INPUT = "input"; |
| | | |
| | | /** 文本域 */ |
| | | public static final String HTML_TEXTAREA = "textarea"; |
| | | |
| | | /** 下拉框 */ |
| | | public static final String HTML_SELECT = "select"; |
| | | |
| | | /** 单选框 */ |
| | | public static final String HTML_RADIO = "radio"; |
| | | |
| | | /** 复选框 */ |
| | | public static final String HTML_CHECKBOX = "checkbox"; |
| | | |
| | | /** 日期控件 */ |
| | | public static final String HTML_DATETIME = "datetime"; |
| | | |
| | | /** 图片上传控件 */ |
| | | public static final String HTML_IMAGE_UPLOAD = "imageUpload"; |
| | | |
| | | /** 文件上传控件 */ |
| | | public static final String HTML_FILE_UPLOAD = "fileUpload"; |
| | | |
| | | /** 富文本控件 */ |
| | | public static final String HTML_EDITOR = "editor"; |
| | | |
| | | /** 字符串类型 */ |
| | | public static final String TYPE_STRING = "String"; |
| | | |
| | | /** 整型 */ |
| | | public static final String TYPE_INTEGER = "Integer"; |
| | | |
| | | /** 长整型 */ |
| | | public static final String TYPE_LONG = "Long"; |
| | | |
| | | /** 浮点型 */ |
| | | public static final String TYPE_DOUBLE = "Double"; |
| | | |
| | | /** 高精度计算类型 */ |
| | | public static final String TYPE_BIGDECIMAL = "BigDecimal"; |
| | | |
| | | /** 时间类型 */ |
| | | public static final String TYPE_DATE = "Date"; |
| | | |
| | | /** 模糊查询 */ |
| | | public static final String QUERY_LIKE = "LIKE"; |
| | | |
| | | /** 相等查询 */ |
| | | public static final String QUERY_EQ = "EQ"; |
| | | |
| | | /** 需要 */ |
| | | public static final String REQUIRE = "1"; |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.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; |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.constant; |
| | | |
| | | /** |
| | | * 订单相关状态常量/接口常量 |
| | | * @author HJL |
| | | * @version 1.0 |
| | | * @since 2024-05-30 15:54 |
| | | */ |
| | | public class OrderConstants { |
| | | |
| | | /** |
| | | * 待派单 |
| | | */ |
| | | public static final Integer TO_BE_DISPATCHED = 0; |
| | | |
| | | /** |
| | | * 待接单 |
| | | */ |
| | | public static final Integer PENDING_ORDERS = 1; |
| | | |
| | | /** |
| | | * 待上门 |
| | | */ |
| | | public static final Integer STAY_AT_DOORSTEP = 2; |
| | | |
| | | /** |
| | | * 待完工 |
| | | */ |
| | | public static final Integer TO_BE_COMPLETED = 3; |
| | | |
| | | /** |
| | | * 已完工 |
| | | */ |
| | | public static final Integer COMPLETED = 4; |
| | | |
| | | /** |
| | | * 已取消 |
| | | */ |
| | | public static final Integer CANCEL = 5; |
| | | |
| | | /** |
| | | * 年 |
| | | */ |
| | | public static final String YEAR = "year"; |
| | | |
| | | /** |
| | | * 本月 |
| | | */ |
| | | public static final String MONTH = "month"; |
| | | |
| | | /** |
| | | * 周 |
| | | */ |
| | | public static final String WEEK = "week"; |
| | | |
| | | /** |
| | | * 当天 |
| | | */ |
| | | public static final String TODAY = "today"; |
| | | |
| | | /** |
| | | * 季度 |
| | | */ |
| | | public static final String QUARTER = "quarter"; |
| | | |
| | | |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.constant; |
| | | |
| | | /** |
| | | * @author HJL |
| | | * @version 1.0 |
| | | * @since 2024-05-31 14:47 |
| | | */ |
| | | public class RedisConstants { |
| | | |
| | | /** |
| | | * 入驻申请 |
| | | */ |
| | | public static final String WORKER_SETTLE_KEY = "worker_settle:"; |
| | | |
| | | /** |
| | | * 找回密码/验证手机号/验证码登录 |
| | | */ |
| | | public static final String WORKER_APPLY_KEY = "worker_apply:"; |
| | | |
| | | /** |
| | | * 用户端,登录验证码前缀 |
| | | */ |
| | | public static final String USER_LOGIN_PHONE_CODE = "user_phone_login:"; |
| | | |
| | | /** |
| | | * 菜单树 |
| | | */ |
| | | public final static String MENU_TREE = "menu_tree"; |
| | | |
| | | /** |
| | | * 省市区三级树 |
| | | */ |
| | | public final static String ADDRESS_TREE = "regin_tree"; |
| | | |
| | | |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.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; |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.constant; |
| | | |
| | | /** |
| | | * 权限相关通用常量 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class SecurityConstants { |
| | | /** |
| | | * 用户ID字段 |
| | | */ |
| | | public static final String DETAILS_USER_ID = "user_id"; |
| | | |
| | | /** |
| | | * 用户名字段 |
| | | */ |
| | | public static final String DETAILS_USERNAME = "username"; |
| | | |
| | | /** |
| | | * 授权信息字段 |
| | | */ |
| | | public static final String AUTHORIZATION_HEADER = "authorization"; |
| | | |
| | | /** |
| | | * 请求来源 |
| | | */ |
| | | public static final String FROM_SOURCE = "from-source"; |
| | | |
| | | /** |
| | | * 内部请求 |
| | | */ |
| | | public static final String INNER = "inner"; |
| | | |
| | | /** |
| | | * 用户标识 |
| | | */ |
| | | public static final String USER_KEY = "user_key"; |
| | | |
| | | /** |
| | | * 用户端 |
| | | */ |
| | | public static final String USER_USER_KEY = "user_user_key"; |
| | | /** |
| | | * 师傅端 |
| | | */ |
| | | public static final String USER_WORKER_KEY = "user_worker_key"; |
| | | |
| | | /** |
| | | * 登录用户 |
| | | */ |
| | | public static final String LOGIN_USER = "login_user"; |
| | | |
| | | /** |
| | | * 角色权限 |
| | | */ |
| | | public static final String ROLE_PERMISSION = "role_permission"; |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.constant; |
| | | |
| | | /** |
| | | * 服务名称 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class ServiceNameConstants |
| | | { |
| | | /** |
| | | * 认证服务的serviceid |
| | | */ |
| | | public static final String AUTH_SERVICE = "ruoyi-auth"; |
| | | |
| | | /** |
| | | * 系统模块的serviceid |
| | | */ |
| | | public static final String SYSTEM_SERVICE = "ruoyi-system"; |
| | | |
| | | /** |
| | | * 文件服务的serviceid |
| | | */ |
| | | public static final String FILE_SERVICE = "ruoyi-file"; |
| | | |
| | | /** |
| | | * 用户端 |
| | | */ |
| | | public static final String USER_SERVICE = "ruoyi-user"; |
| | | |
| | | /** |
| | | * 后台服务 |
| | | */ |
| | | public static final String ADMIN_SERVICE = "ruoyi-admin"; |
| | | |
| | | /** |
| | | * 师傅端服务 |
| | | */ |
| | | public static final String WORKER_SERVICE = "ruoyi-worker"; |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.constant; |
| | | |
| | | /** |
| | | * Token的Key常量 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class TokenConstants |
| | | { |
| | | /** |
| | | * 令牌自定义标识 |
| | | */ |
| | | public static final String AUTHENTICATION = "Authorization"; |
| | | |
| | | /** |
| | | * 令牌前缀 |
| | | */ |
| | | public static final String PREFIX = "Bearer "; |
| | | |
| | | /** |
| | | * 令牌秘钥 |
| | | */ |
| | | public final static String SECRET = "abcdefghijklmnopqrstuvwxyz"; |
| | | |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.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_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; |
| | | |
| | | /** |
| | | * 师傅端 登录账号未注册 |
| | | */ |
| | | public static final int WORKER_PHONE_UNREGISTERED = 501; |
| | | |
| | | /** |
| | | * 手机号已注册 |
| | | */ |
| | | public static final int WORKER_PHONE_REGISTERED = 502; |
| | | |
| | | /** |
| | | * 师傅端 登录账号未启用 |
| | | */ |
| | | public static final int WORKER_PHONE_NOT_ENABLE = 503; |
| | | |
| | | /** |
| | | * 师傅端 登录账号未启用 |
| | | */ |
| | | public static final int WORKER_PHONE_PASSWORD_ERROR = 504; |
| | | |
| | | /** |
| | | * 短信验证码错误 |
| | | */ |
| | | public static final int WORKER_PHONE_CODE_ERROR = 505; |
| | | |
| | | /** |
| | | * 身份证号码不合法 |
| | | */ |
| | | public static final int WORKER_ID_NUMBER_ILLEGAL = 506; |
| | | |
| | | /** |
| | | * 入驻申请重复提交 |
| | | */ |
| | | public static final int WORKER_REPEATED_SUBMISSION = 507; |
| | | |
| | | /** |
| | | * 登录密码不符合规则 |
| | | */ |
| | | public static final int WORKER_PASSWORD_ILLEGALITY = 508; |
| | | |
| | | /** |
| | | * 登录已过期 |
| | | */ |
| | | public static final int LOGIN_EXPIRE = 510; |
| | | |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.context; |
| | | |
| | | import java.util.Map; |
| | | import java.util.concurrent.ConcurrentHashMap; |
| | | import com.alibaba.ttl.TransmittableThreadLocal; |
| | | import com.ruoyi.common.core.constant.SecurityConstants; |
| | | import com.ruoyi.common.core.text.Convert; |
| | | import com.ruoyi.common.core.utils.StringUtils; |
| | | |
| | | /** |
| | | * 获取当前线程变量中的 用户id、用户名称、Token等信息 |
| | | * 注意: 必须在网关通过请求头的方法传入,同时在HeaderInterceptor拦截器设置值。 否则这里无法获取 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class SecurityContextHolder |
| | | { |
| | | private static final TransmittableThreadLocal<Map<String, Object>> THREAD_LOCAL = new TransmittableThreadLocal<>(); |
| | | |
| | | public static void set(String key, Object value) |
| | | { |
| | | Map<String, Object> map = getLocalMap(); |
| | | map.put(key, value == null ? StringUtils.EMPTY : value); |
| | | } |
| | | |
| | | public static String get(String key) |
| | | { |
| | | Map<String, Object> map = getLocalMap(); |
| | | return Convert.toStr(map.getOrDefault(key, StringUtils.EMPTY)); |
| | | } |
| | | |
| | | public static <T> T get(String key, Class<T> clazz) |
| | | { |
| | | Map<String, Object> map = getLocalMap(); |
| | | return StringUtils.cast(map.getOrDefault(key, null)); |
| | | } |
| | | |
| | | public static Map<String, Object> getLocalMap() |
| | | { |
| | | Map<String, Object> map = THREAD_LOCAL.get(); |
| | | if (map == null) |
| | | { |
| | | map = new ConcurrentHashMap<String, Object>(); |
| | | THREAD_LOCAL.set(map); |
| | | } |
| | | return map; |
| | | } |
| | | |
| | | public static void setLocalMap(Map<String, Object> threadLocalMap) |
| | | { |
| | | THREAD_LOCAL.set(threadLocalMap); |
| | | } |
| | | |
| | | public static Long getUserId() |
| | | { |
| | | return Convert.toLong(get(SecurityConstants.DETAILS_USER_ID), 0L); |
| | | } |
| | | |
| | | public static void setUserId(String account) |
| | | { |
| | | set(SecurityConstants.DETAILS_USER_ID, account); |
| | | } |
| | | |
| | | public static String getUserName() |
| | | { |
| | | return get(SecurityConstants.DETAILS_USERNAME); |
| | | } |
| | | |
| | | public static void setUserName(String username) |
| | | { |
| | | set(SecurityConstants.DETAILS_USERNAME, username); |
| | | } |
| | | |
| | | public static String getUserKey() |
| | | { |
| | | return get(SecurityConstants.USER_KEY); |
| | | } |
| | | |
| | | public static void setUserKey(String userKey) |
| | | { |
| | | set(SecurityConstants.USER_KEY, userKey); |
| | | } |
| | | |
| | | public static String getPermission() |
| | | { |
| | | return get(SecurityConstants.ROLE_PERMISSION); |
| | | } |
| | | |
| | | public static void setPermission(String permissions) |
| | | { |
| | | set(SecurityConstants.ROLE_PERMISSION, permissions); |
| | | } |
| | | |
| | | public static void remove() |
| | | { |
| | | THREAD_LOCAL.remove(); |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.domain; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.FieldFill; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Data; |
| | | |
| | | import java.util.Date; |
| | | |
| | | /** |
| | | * @author HJL |
| | | * @version 1.0 |
| | | * @since 2024-05-29 15:43 |
| | | */ |
| | | |
| | | @Data |
| | | public class BaseEntity { |
| | | |
| | | @TableField(value = "createBy", fill = FieldFill.INSERT) |
| | | @ApiModelProperty("创建人") |
| | | private String createBy; |
| | | |
| | | @TableField(value = "updateBy", fill = FieldFill.INSERT_UPDATE) |
| | | @ApiModelProperty("修改人") |
| | | private String updateBy; |
| | | |
| | | @TableField(value = "createTime", fill = FieldFill.INSERT) |
| | | @ApiModelProperty("创建时间") |
| | | private Date createTime; |
| | | |
| | | @TableField(value = "updateTime", fill = FieldFill.INSERT_UPDATE) |
| | | @ApiModelProperty("修改时间") |
| | | private Date updateTime; |
| | | |
| | | @TableField(value = "is_delete", fill = FieldFill.INSERT) |
| | | @ApiModelProperty("是否软删除 0未删除;1已删除") |
| | | private Integer isDelete; |
| | | |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.domain; |
| | | |
| | | import com.ruoyi.common.core.constant.Constants; |
| | | import com.ruoyi.common.core.constant.UserConstants; |
| | | |
| | | import java.io.Serializable; |
| | | |
| | | /** |
| | | * 响应信息主体 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class R<T> implements Serializable { |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | /** |
| | | * 成功 |
| | | */ |
| | | public static final int SUCCESS = Constants.SUCCESS; |
| | | |
| | | /** |
| | | * 失败 |
| | | */ |
| | | public static final int FAIL = Constants.FAIL; |
| | | |
| | | private int code; |
| | | |
| | | private String msg; |
| | | |
| | | private T data; |
| | | |
| | | public static <T> R<T> ok() { |
| | | return restResult(null, SUCCESS, null); |
| | | } |
| | | |
| | | public static <T> R<T> ok(T data) { |
| | | return restResult(data, SUCCESS, null); |
| | | } |
| | | |
| | | public static <T> R<T> ok(T data, String msg) { |
| | | return restResult(data, SUCCESS, msg); |
| | | } |
| | | |
| | | public static <T> R<T> ok(String msg) { |
| | | return restResult(null, SUCCESS, msg); |
| | | } |
| | | |
| | | public static <T> R<T> fail() { |
| | | return restResult(null, FAIL, null); |
| | | } |
| | | |
| | | 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, null); |
| | | } |
| | | |
| | | 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); |
| | | } |
| | | |
| | | /** |
| | | * 账号未注册 |
| | | */ |
| | | public static <T> R<T> unregistered(String msg) { |
| | | return restResult(null, UserConstants.WORKER_PHONE_UNREGISTERED, msg); |
| | | } |
| | | |
| | | /** |
| | | * 账号未启用 |
| | | */ |
| | | public static <T> R<T> notEnabled(String msg) { |
| | | return restResult(null, UserConstants.WORKER_PHONE_NOT_ENABLE, msg); |
| | | } |
| | | |
| | | /** |
| | | * 密码错误 |
| | | */ |
| | | public static <T> R<T> passwordError(String msg) { |
| | | return restResult(null, UserConstants.WORKER_PHONE_PASSWORD_ERROR, msg); |
| | | } |
| | | |
| | | /** |
| | | * 短信验证码错误 |
| | | */ |
| | | public static <T> R<T> errorCode(String msg) { |
| | | return restResult(null, UserConstants.WORKER_PHONE_CODE_ERROR, msg); |
| | | } |
| | | |
| | | /** |
| | | * 身份证号码不合法 |
| | | */ |
| | | public static <T> R<T> idNumberIllegal(String msg) { |
| | | return restResult(null, UserConstants.WORKER_ID_NUMBER_ILLEGAL, msg); |
| | | } |
| | | |
| | | /** |
| | | * 入驻申请重复提交 |
| | | */ |
| | | public static <T> R<T> repeatedSubmission(String msg) { |
| | | return restResult(null, UserConstants.WORKER_REPEATED_SUBMISSION, msg); |
| | | } |
| | | |
| | | /** |
| | | * 账号未注册 |
| | | */ |
| | | public static <T> R<T> registered(String msg) { |
| | | return restResult(null, UserConstants.WORKER_PHONE_REGISTERED, msg); |
| | | } |
| | | |
| | | /** |
| | | * 登录密码不符合规则 |
| | | */ |
| | | public static <T> R<T> passwordIllegality(String msg) { |
| | | return restResult(null, UserConstants.WORKER_PASSWORD_ILLEGALITY, msg); |
| | | } |
| | | |
| | | /** |
| | | * 登录密码不符合规则 |
| | | */ |
| | | public static <T> R<T> loginExpire(String msg) { |
| | | return restResult(null, UserConstants.LOGIN_EXPIRE, 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(); |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.domain; |
| | | |
| | | import com.ruoyi.common.core.enums.CodeEnum; |
| | | |
| | | import java.io.Serializable; |
| | | |
| | | public class Result<T> implements Serializable { |
| | | private T datas; |
| | | private Integer resp_code; |
| | | private String resp_msg; |
| | | |
| | | public static <T> Result<T> succeed(String msg) { |
| | | return of(null, CodeEnum.SUCCESS.getCode(), msg); |
| | | } |
| | | |
| | | public static <T> Result<T> succeed(T model, String msg) { |
| | | return of(model, CodeEnum.SUCCESS.getCode(), msg); |
| | | } |
| | | |
| | | public static <T> Result<T> succeed(T model) { |
| | | return of(model, CodeEnum.SUCCESS.getCode(), "SUCCESS"); |
| | | } |
| | | |
| | | public static <T> Result<T> of(T datas, Integer code, String msg) { |
| | | return new Result(datas, code, msg); |
| | | } |
| | | |
| | | public static <T> Result<T> failed(String msg) { |
| | | return of(null, CodeEnum.ERROR.getCode(), msg); |
| | | } |
| | | |
| | | public static <T> Result<T> failed(T model, String msg) { |
| | | return of(model, CodeEnum.ERROR.getCode(), msg); |
| | | } |
| | | |
| | | public T getDatas() { |
| | | return this.datas; |
| | | } |
| | | |
| | | public Integer getResp_code() { |
| | | return this.resp_code; |
| | | } |
| | | |
| | | public String getResp_msg() { |
| | | return this.resp_msg; |
| | | } |
| | | |
| | | public void setDatas(T datas) { |
| | | this.datas = datas; |
| | | } |
| | | |
| | | public void setResp_code(Integer resp_code) { |
| | | this.resp_code = resp_code; |
| | | } |
| | | |
| | | public void setResp_msg(String resp_msg) { |
| | | this.resp_msg = resp_msg; |
| | | } |
| | | |
| | | public boolean equals(Object o) { |
| | | if (o == this) { |
| | | return true; |
| | | } else if (!(o instanceof Result)) { |
| | | return false; |
| | | } else { |
| | | Result<?> other = (Result) o; |
| | | if (!other.canEqual(this)) { |
| | | return false; |
| | | } else { |
| | | label47: |
| | | { |
| | | Object this$resp_code = this.getResp_code(); |
| | | Object other$resp_code = other.getResp_code(); |
| | | if (this$resp_code == null) { |
| | | if (other$resp_code == null) { |
| | | break label47; |
| | | } |
| | | } else if (this$resp_code.equals(other$resp_code)) { |
| | | break label47; |
| | | } |
| | | |
| | | return false; |
| | | } |
| | | |
| | | Object this$datas = this.getDatas(); |
| | | Object other$datas = other.getDatas(); |
| | | if (this$datas == null) { |
| | | if (other$datas != null) { |
| | | return false; |
| | | } |
| | | } else if (!this$datas.equals(other$datas)) { |
| | | return false; |
| | | } |
| | | |
| | | Object this$resp_msg = this.getResp_msg(); |
| | | Object other$resp_msg = other.getResp_msg(); |
| | | if (this$resp_msg == null) { |
| | | if (other$resp_msg != null) { |
| | | return false; |
| | | } |
| | | } else if (!this$resp_msg.equals(other$resp_msg)) { |
| | | return false; |
| | | } |
| | | |
| | | return true; |
| | | } |
| | | } |
| | | } |
| | | |
| | | protected boolean canEqual(Object other) { |
| | | return other instanceof Result; |
| | | } |
| | | |
| | | public int hashCode() { |
| | | int result = 1; |
| | | Object $resp_code = this.getResp_code(); |
| | | result = result * 59 + ($resp_code == null ? 43 : $resp_code.hashCode()); |
| | | Object $datas = this.getDatas(); |
| | | result = result * 59 + ($datas == null ? 43 : $datas.hashCode()); |
| | | Object $resp_msg = this.getResp_msg(); |
| | | result = result * 59 + ($resp_msg == null ? 43 : $resp_msg.hashCode()); |
| | | return result; |
| | | } |
| | | |
| | | public String toString() { |
| | | return "Result(datas=" + this.getDatas() + ", resp_code=" + this.getResp_code() + ", resp_msg=" + this.getResp_msg() + ")"; |
| | | } |
| | | |
| | | public Result() { |
| | | } |
| | | |
| | | public Result(T datas, Integer resp_code, String resp_msg) { |
| | | this.datas = datas; |
| | | this.resp_code = resp_code; |
| | | this.resp_msg = resp_msg; |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.enums; |
| | | |
| | | /** |
| | | * @author HJL |
| | | */ |
| | | |
| | | public enum CodeEnum { |
| | | /** |
| | | * 成功 |
| | | */ |
| | | SUCCESS(0), |
| | | /** |
| | | * 失败 |
| | | */ |
| | | ERROR(1); |
| | | |
| | | private final Integer code; |
| | | |
| | | private CodeEnum(Integer code) { |
| | | this.code = code; |
| | | } |
| | | |
| | | public Integer getCode() { |
| | | return this.code; |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.enums; |
| | | |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Getter; |
| | | |
| | | /** |
| | | * 高德地图枚举 |
| | | * |
| | | * @author HJL |
| | | */ |
| | | |
| | | @AllArgsConstructor |
| | | @Getter |
| | | public enum GaoDeEnum { |
| | | |
| | | // 高德地图固定字段 |
| | | STATUS("status"), |
| | | INT_ONE("1"), |
| | | RE_GEO_CODE("regeocode"), |
| | | GEO_CODES("geocodes"), |
| | | LOCATION("location"), |
| | | FORMATTED_ADDRESS("formatted_address"), |
| | | RESULTS("results"), |
| | | DISTANCE("distance"); |
| | | |
| | | private final String code; |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.enums; |
| | | |
| | | /** |
| | | * 用户状态 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public enum UserStatus { |
| | | /** |
| | | * 账号正常 |
| | | */ |
| | | OK(0, "正常"), |
| | | |
| | | /** |
| | | * 账号已被删除 |
| | | */ |
| | | DELETED(1, "删除"), |
| | | |
| | | /** |
| | | * 账号已启用 |
| | | */ |
| | | ENABLE(1, "启用"), |
| | | ; |
| | | |
| | | private final Integer code; |
| | | private final String info; |
| | | |
| | | UserStatus(Integer code, String info) { |
| | | this.code = code; |
| | | this.info = info; |
| | | } |
| | | |
| | | public Integer getCode() { |
| | | return code; |
| | | } |
| | | |
| | | public String getInfo() { |
| | | return info; |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.exception; |
| | | |
| | | /** |
| | | * 验证码错误异常类 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class CaptchaException extends RuntimeException |
| | | { |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | public CaptchaException(String msg) |
| | | { |
| | | super(msg); |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.exception; |
| | | |
| | | /** |
| | | * 检查异常 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class CheckedException extends RuntimeException |
| | | { |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | public CheckedException(String message) |
| | | { |
| | | super(message); |
| | | } |
| | | |
| | | public CheckedException(Throwable cause) |
| | | { |
| | | super(cause); |
| | | } |
| | | |
| | | public CheckedException(String message, Throwable cause) |
| | | { |
| | | super(message, cause); |
| | | } |
| | | |
| | | public CheckedException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) |
| | | { |
| | | super(message, cause, enableSuppression, writableStackTrace); |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.exception; |
| | | |
| | | /** |
| | | * 演示模式异常 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class DemoModeException extends RuntimeException |
| | | { |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | public DemoModeException() |
| | | { |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.exception; |
| | | |
| | | /** |
| | | * 全局异常 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class GlobalException extends RuntimeException { |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | /** |
| | | * 错误提示 |
| | | */ |
| | | private String message; |
| | | |
| | | /** |
| | | * 错误明细,内部调试错误 |
| | | * <p> |
| | | * 和 {@link CommonResult#getDetailMessage()} 一致的设计 |
| | | */ |
| | | private String detailMessage; |
| | | |
| | | /** |
| | | * 空构造方法,避免反序列化问题 |
| | | */ |
| | | public GlobalException() { |
| | | } |
| | | |
| | | public GlobalException(String message) { |
| | | this.message = message; |
| | | } |
| | | |
| | | public String getDetailMessage() { |
| | | return detailMessage; |
| | | } |
| | | |
| | | public GlobalException setDetailMessage(String detailMessage) { |
| | | this.detailMessage = detailMessage; |
| | | return this; |
| | | } |
| | | |
| | | @Override |
| | | public String getMessage() { |
| | | return message; |
| | | } |
| | | |
| | | public GlobalException setMessage(String message) { |
| | | this.message = message; |
| | | return this; |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.exception; |
| | | |
| | | /** |
| | | * 内部认证异常 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class InnerAuthException extends RuntimeException |
| | | { |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | public InnerAuthException(String message) |
| | | { |
| | | super(message); |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.exception; |
| | | |
| | | /** |
| | | * 权限异常 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class PreAuthorizeException extends RuntimeException |
| | | { |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | public PreAuthorizeException() |
| | | { |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.exception; |
| | | |
| | | /** |
| | | * 业务异常 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public final class ServiceException extends RuntimeException |
| | | { |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | /** |
| | | * 错误码 |
| | | */ |
| | | private Integer code; |
| | | |
| | | /** |
| | | * 错误提示 |
| | | */ |
| | | private String message; |
| | | |
| | | /** |
| | | * 错误明细,内部调试错误 |
| | | * |
| | | * 和 {@link CommonResult#getDetailMessage()} 一致的设计 |
| | | */ |
| | | private String detailMessage; |
| | | |
| | | /** |
| | | * 空构造方法,避免反序列化问题 |
| | | */ |
| | | public ServiceException() |
| | | { |
| | | } |
| | | |
| | | public ServiceException(String message) |
| | | { |
| | | this.message = message; |
| | | } |
| | | |
| | | public ServiceException(String message, Integer code) |
| | | { |
| | | this.message = message; |
| | | this.code = code; |
| | | } |
| | | |
| | | public String getDetailMessage() |
| | | { |
| | | return detailMessage; |
| | | } |
| | | |
| | | @Override |
| | | public String getMessage() |
| | | { |
| | | return message; |
| | | } |
| | | |
| | | public Integer getCode() |
| | | { |
| | | return code; |
| | | } |
| | | |
| | | public ServiceException setMessage(String message) |
| | | { |
| | | this.message = message; |
| | | return this; |
| | | } |
| | | |
| | | public ServiceException setDetailMessage(String detailMessage) |
| | | { |
| | | this.detailMessage = detailMessage; |
| | | return this; |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.exception; |
| | | |
| | | /** |
| | | * 工具类异常 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class UtilException extends RuntimeException |
| | | { |
| | | private static final long serialVersionUID = 8247610319171014183L; |
| | | |
| | | public UtilException(Throwable e) |
| | | { |
| | | super(e.getMessage(), e); |
| | | } |
| | | |
| | | public UtilException(String message) |
| | | { |
| | | super(message); |
| | | } |
| | | |
| | | public UtilException(String message, Throwable throwable) |
| | | { |
| | | super(message, throwable); |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.exception.auth; |
| | | |
| | | /** |
| | | * 未能通过的登录认证异常 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class NotLoginException extends RuntimeException |
| | | { |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | public NotLoginException(String message) |
| | | { |
| | | super(message); |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.exception.auth; |
| | | |
| | | import org.apache.commons.lang3.StringUtils; |
| | | |
| | | /** |
| | | * 未能通过的权限认证异常 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class NotPermissionException extends RuntimeException |
| | | { |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | public NotPermissionException(String permission) |
| | | { |
| | | super(permission); |
| | | } |
| | | |
| | | public NotPermissionException(String[] permissions) |
| | | { |
| | | super(StringUtils.join(permissions, ",")); |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.exception.auth; |
| | | |
| | | import org.apache.commons.lang3.StringUtils; |
| | | |
| | | /** |
| | | * 未能通过的角色认证异常 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class NotRoleException extends RuntimeException |
| | | { |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | public NotRoleException(String role) |
| | | { |
| | | super(role); |
| | | } |
| | | |
| | | public NotRoleException(String[] roles) |
| | | { |
| | | super(StringUtils.join(roles, ",")); |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.exception.base; |
| | | |
| | | /** |
| | | * 基础异常 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class BaseException extends RuntimeException |
| | | { |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | /** |
| | | * 所属模块 |
| | | */ |
| | | private String module; |
| | | |
| | | /** |
| | | * 错误码 |
| | | */ |
| | | private String code; |
| | | |
| | | /** |
| | | * 错误码对应的参数 |
| | | */ |
| | | private Object[] args; |
| | | |
| | | /** |
| | | * 错误消息 |
| | | */ |
| | | private String defaultMessage; |
| | | |
| | | public BaseException(String module, String code, Object[] args, String defaultMessage) |
| | | { |
| | | this.module = module; |
| | | this.code = code; |
| | | this.args = args; |
| | | this.defaultMessage = defaultMessage; |
| | | } |
| | | |
| | | public BaseException(String module, String code, Object[] args) |
| | | { |
| | | this(module, code, args, null); |
| | | } |
| | | |
| | | public BaseException(String module, String defaultMessage) |
| | | { |
| | | this(module, null, null, defaultMessage); |
| | | } |
| | | |
| | | public BaseException(String code, Object[] args) |
| | | { |
| | | this(null, code, args, null); |
| | | } |
| | | |
| | | public BaseException(String defaultMessage) |
| | | { |
| | | this(null, null, null, defaultMessage); |
| | | } |
| | | |
| | | public String getModule() |
| | | { |
| | | return module; |
| | | } |
| | | |
| | | public String getCode() |
| | | { |
| | | return code; |
| | | } |
| | | |
| | | public Object[] getArgs() |
| | | { |
| | | return args; |
| | | } |
| | | |
| | | public String getDefaultMessage() |
| | | { |
| | | return defaultMessage; |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.exception.file; |
| | | |
| | | import com.ruoyi.common.core.exception.base.BaseException; |
| | | |
| | | /** |
| | | * 文件信息异常类 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class FileException extends BaseException |
| | | { |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | public FileException(String code, Object[] args, String msg) |
| | | { |
| | | super("file", code, args, msg); |
| | | } |
| | | |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.exception.file; |
| | | |
| | | /** |
| | | * 文件名称超长限制异常类 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class FileNameLengthLimitExceededException extends FileException |
| | | { |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | public FileNameLengthLimitExceededException(int defaultFileNameLength) |
| | | { |
| | | super("upload.filename.exceed.length", new Object[] { defaultFileNameLength }, "the filename is too long"); |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.exception.file; |
| | | |
| | | /** |
| | | * 文件名大小限制异常类 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class FileSizeLimitExceededException extends FileException |
| | | { |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | public FileSizeLimitExceededException(long defaultMaxSize) |
| | | { |
| | | super("upload.exceed.maxSize", new Object[] { defaultMaxSize }, "the filesize is too large"); |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.exception.file; |
| | | |
| | | import java.io.PrintStream; |
| | | import java.io.PrintWriter; |
| | | |
| | | /** |
| | | * 文件上传异常类 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class FileUploadException extends Exception |
| | | { |
| | | |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | private final Throwable cause; |
| | | |
| | | public FileUploadException() |
| | | { |
| | | this(null, null); |
| | | } |
| | | |
| | | public FileUploadException(final String msg) |
| | | { |
| | | this(msg, null); |
| | | } |
| | | |
| | | public FileUploadException(String msg, Throwable cause) |
| | | { |
| | | super(msg); |
| | | this.cause = cause; |
| | | } |
| | | |
| | | @Override |
| | | public void printStackTrace(PrintStream stream) |
| | | { |
| | | super.printStackTrace(stream); |
| | | if (cause != null) |
| | | { |
| | | stream.println("Caused by:"); |
| | | cause.printStackTrace(stream); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public void printStackTrace(PrintWriter writer) |
| | | { |
| | | super.printStackTrace(writer); |
| | | if (cause != null) |
| | | { |
| | | writer.println("Caused by:"); |
| | | cause.printStackTrace(writer); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public Throwable getCause() |
| | | { |
| | | return cause; |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.exception.file; |
| | | |
| | | import java.util.Arrays; |
| | | |
| | | /** |
| | | * 文件上传 误异常类 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class InvalidExtensionException extends FileUploadException |
| | | { |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | private String[] allowedExtension; |
| | | private String extension; |
| | | private String filename; |
| | | |
| | | public InvalidExtensionException(String[] allowedExtension, String extension, String filename) |
| | | { |
| | | super("filename : [" + filename + "], extension : [" + extension + "], allowed extension : [" + Arrays.toString(allowedExtension) + "]"); |
| | | this.allowedExtension = allowedExtension; |
| | | this.extension = extension; |
| | | this.filename = filename; |
| | | } |
| | | |
| | | public String[] getAllowedExtension() |
| | | { |
| | | return allowedExtension; |
| | | } |
| | | |
| | | public String getExtension() |
| | | { |
| | | return extension; |
| | | } |
| | | |
| | | public String getFilename() |
| | | { |
| | | return filename; |
| | | } |
| | | |
| | | public static class InvalidImageExtensionException extends InvalidExtensionException |
| | | { |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | public InvalidImageExtensionException(String[] allowedExtension, String extension, String filename) |
| | | { |
| | | super(allowedExtension, extension, filename); |
| | | } |
| | | } |
| | | |
| | | public static class InvalidFlashExtensionException extends InvalidExtensionException |
| | | { |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | public InvalidFlashExtensionException(String[] allowedExtension, String extension, String filename) |
| | | { |
| | | super(allowedExtension, extension, filename); |
| | | } |
| | | } |
| | | |
| | | public static class InvalidMediaExtensionException extends InvalidExtensionException |
| | | { |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | public InvalidMediaExtensionException(String[] allowedExtension, String extension, String filename) |
| | | { |
| | | super(allowedExtension, extension, filename); |
| | | } |
| | | } |
| | | |
| | | public static class InvalidVideoExtensionException extends InvalidExtensionException |
| | | { |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | public InvalidVideoExtensionException(String[] allowedExtension, String extension, String filename) |
| | | { |
| | | super(allowedExtension, extension, filename); |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.exception.job; |
| | | |
| | | /** |
| | | * 计划策略异常 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class TaskException extends Exception |
| | | { |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | private Code code; |
| | | |
| | | public TaskException(String msg, Code code) |
| | | { |
| | | this(msg, code, null); |
| | | } |
| | | |
| | | public TaskException(String msg, Code code, Exception nestedEx) |
| | | { |
| | | super(msg, nestedEx); |
| | | this.code = code; |
| | | } |
| | | |
| | | public Code getCode() |
| | | { |
| | | return code; |
| | | } |
| | | |
| | | public enum Code |
| | | { |
| | | TASK_EXISTS, NO_TASK_EXISTS, TASK_ALREADY_STARTED, UNKNOWN, CONFIG_ERROR, TASK_NODE_NOT_AVAILABLE |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.exception.user; |
| | | |
| | | /** |
| | | * 验证码失效异常类 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class CaptchaExpireException extends UserException |
| | | { |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | public CaptchaExpireException() |
| | | { |
| | | super("user.jcaptcha.expire", null); |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.exception.user; |
| | | |
| | | import com.ruoyi.common.core.exception.base.BaseException; |
| | | |
| | | /** |
| | | * 用户信息异常类 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class UserException extends BaseException |
| | | { |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | public UserException(String code, Object[] args) |
| | | { |
| | | super("user", code, args, null); |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.exception.user; |
| | | |
| | | /** |
| | | * 用户密码不正确或不符合规范异常类 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class UserPasswordNotMatchException extends UserException |
| | | { |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | public UserPasswordNotMatchException() |
| | | { |
| | | super("user.password.not.match", null); |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.text; |
| | | |
| | | import java.nio.charset.Charset; |
| | | import java.nio.charset.StandardCharsets; |
| | | import com.ruoyi.common.core.utils.StringUtils; |
| | | |
| | | /** |
| | | * 字符集工具类 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class CharsetKit |
| | | { |
| | | /** ISO-8859-1 */ |
| | | public static final String ISO_8859_1 = "ISO-8859-1"; |
| | | /** UTF-8 */ |
| | | public static final String UTF_8 = "UTF-8"; |
| | | /** GBK */ |
| | | public static final String GBK = "GBK"; |
| | | |
| | | /** ISO-8859-1 */ |
| | | public static final Charset CHARSET_ISO_8859_1 = Charset.forName(ISO_8859_1); |
| | | /** UTF-8 */ |
| | | public static final Charset CHARSET_UTF_8 = Charset.forName(UTF_8); |
| | | /** GBK */ |
| | | public static final Charset CHARSET_GBK = Charset.forName(GBK); |
| | | |
| | | /** |
| | | * 转换为Charset对象 |
| | | * |
| | | * @param charset 字符集,为空则返回默认字符集 |
| | | * @return Charset |
| | | */ |
| | | public static Charset charset(String charset) |
| | | { |
| | | return StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset); |
| | | } |
| | | |
| | | /** |
| | | * 转换字符串的字符集编码 |
| | | * |
| | | * @param source 字符串 |
| | | * @param srcCharset 源字符集,默认ISO-8859-1 |
| | | * @param destCharset 目标字符集,默认UTF-8 |
| | | * @return 转换后的字符集 |
| | | */ |
| | | public static String convert(String source, String srcCharset, String destCharset) |
| | | { |
| | | return convert(source, Charset.forName(srcCharset), Charset.forName(destCharset)); |
| | | } |
| | | |
| | | /** |
| | | * 转换字符串的字符集编码 |
| | | * |
| | | * @param source 字符串 |
| | | * @param srcCharset 源字符集,默认ISO-8859-1 |
| | | * @param destCharset 目标字符集,默认UTF-8 |
| | | * @return 转换后的字符集 |
| | | */ |
| | | public static String convert(String source, Charset srcCharset, Charset destCharset) |
| | | { |
| | | if (null == srcCharset) |
| | | { |
| | | srcCharset = StandardCharsets.ISO_8859_1; |
| | | } |
| | | |
| | | if (null == destCharset) |
| | | { |
| | | destCharset = StandardCharsets.UTF_8; |
| | | } |
| | | |
| | | if (StringUtils.isEmpty(source) || srcCharset.equals(destCharset)) |
| | | { |
| | | return source; |
| | | } |
| | | return new String(source.getBytes(srcCharset), destCharset); |
| | | } |
| | | |
| | | /** |
| | | * @return 系统字符集编码 |
| | | */ |
| | | public static String systemCharset() |
| | | { |
| | | return Charset.defaultCharset().name(); |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.text; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.math.BigInteger; |
| | | import java.math.RoundingMode; |
| | | import java.nio.ByteBuffer; |
| | | import java.nio.charset.Charset; |
| | | import java.text.NumberFormat; |
| | | import java.util.Set; |
| | | import com.ruoyi.common.core.utils.StringUtils; |
| | | |
| | | /** |
| | | * 类型转换器 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class Convert |
| | | { |
| | | /** |
| | | * 转换为字符串<br> |
| | | * 如果给定的值为null,或者转换失败,返回默认值<br> |
| | | * 转换失败不会报错 |
| | | * |
| | | * @param value 被转换的值 |
| | | * @param defaultValue 转换错误时的默认值 |
| | | * @return 结果 |
| | | */ |
| | | public static String toStr(Object value, String defaultValue) |
| | | { |
| | | if (null == value) |
| | | { |
| | | return defaultValue; |
| | | } |
| | | if (value instanceof String) |
| | | { |
| | | return (String) value; |
| | | } |
| | | return value.toString(); |
| | | } |
| | | |
| | | /** |
| | | * 转换为字符串<br> |
| | | * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br> |
| | | * 转换失败不会报错 |
| | | * |
| | | * @param value 被转换的值 |
| | | * @return 结果 |
| | | */ |
| | | public static String toStr(Object value) |
| | | { |
| | | return toStr(value, null); |
| | | } |
| | | |
| | | /** |
| | | * 转换为字符<br> |
| | | * 如果给定的值为null,或者转换失败,返回默认值<br> |
| | | * 转换失败不会报错 |
| | | * |
| | | * @param value 被转换的值 |
| | | * @param defaultValue 转换错误时的默认值 |
| | | * @return 结果 |
| | | */ |
| | | public static Character toChar(Object value, Character defaultValue) |
| | | { |
| | | if (null == value) |
| | | { |
| | | return defaultValue; |
| | | } |
| | | if (value instanceof Character) |
| | | { |
| | | return (Character) value; |
| | | } |
| | | |
| | | final String valueStr = toStr(value, null); |
| | | return StringUtils.isEmpty(valueStr) ? defaultValue : valueStr.charAt(0); |
| | | } |
| | | |
| | | /** |
| | | * 转换为字符<br> |
| | | * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br> |
| | | * 转换失败不会报错 |
| | | * |
| | | * @param value 被转换的值 |
| | | * @return 结果 |
| | | */ |
| | | public static Character toChar(Object value) |
| | | { |
| | | return toChar(value, null); |
| | | } |
| | | |
| | | /** |
| | | * 转换为byte<br> |
| | | * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<br> |
| | | * 转换失败不会报错 |
| | | * |
| | | * @param value 被转换的值 |
| | | * @param defaultValue 转换错误时的默认值 |
| | | * @return 结果 |
| | | */ |
| | | public static Byte toByte(Object value, Byte defaultValue) |
| | | { |
| | | if (value == null) |
| | | { |
| | | return defaultValue; |
| | | } |
| | | if (value instanceof Byte) |
| | | { |
| | | return (Byte) value; |
| | | } |
| | | if (value instanceof Number) |
| | | { |
| | | return ((Number) value).byteValue(); |
| | | } |
| | | final String valueStr = toStr(value, null); |
| | | if (StringUtils.isEmpty(valueStr)) |
| | | { |
| | | return defaultValue; |
| | | } |
| | | try |
| | | { |
| | | return Byte.parseByte(valueStr); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | return defaultValue; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 转换为byte<br> |
| | | * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br> |
| | | * 转换失败不会报错 |
| | | * |
| | | * @param value 被转换的值 |
| | | * @return 结果 |
| | | */ |
| | | public static Byte toByte(Object value) |
| | | { |
| | | return toByte(value, null); |
| | | } |
| | | |
| | | /** |
| | | * 转换为Short<br> |
| | | * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<br> |
| | | * 转换失败不会报错 |
| | | * |
| | | * @param value 被转换的值 |
| | | * @param defaultValue 转换错误时的默认值 |
| | | * @return 结果 |
| | | */ |
| | | public static Short toShort(Object value, Short defaultValue) |
| | | { |
| | | if (value == null) |
| | | { |
| | | return defaultValue; |
| | | } |
| | | if (value instanceof Short) |
| | | { |
| | | return (Short) value; |
| | | } |
| | | if (value instanceof Number) |
| | | { |
| | | return ((Number) value).shortValue(); |
| | | } |
| | | final String valueStr = toStr(value, null); |
| | | if (StringUtils.isEmpty(valueStr)) |
| | | { |
| | | return defaultValue; |
| | | } |
| | | try |
| | | { |
| | | return Short.parseShort(valueStr.trim()); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | return defaultValue; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 转换为Short<br> |
| | | * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br> |
| | | * 转换失败不会报错 |
| | | * |
| | | * @param value 被转换的值 |
| | | * @return 结果 |
| | | */ |
| | | public static Short toShort(Object value) |
| | | { |
| | | return toShort(value, null); |
| | | } |
| | | |
| | | /** |
| | | * 转换为Number<br> |
| | | * 如果给定的值为空,或者转换失败,返回默认值<br> |
| | | * 转换失败不会报错 |
| | | * |
| | | * @param value 被转换的值 |
| | | * @param defaultValue 转换错误时的默认值 |
| | | * @return 结果 |
| | | */ |
| | | public static Number toNumber(Object value, Number defaultValue) |
| | | { |
| | | if (value == null) |
| | | { |
| | | return defaultValue; |
| | | } |
| | | if (value instanceof Number) |
| | | { |
| | | return (Number) value; |
| | | } |
| | | final String valueStr = toStr(value, null); |
| | | if (StringUtils.isEmpty(valueStr)) |
| | | { |
| | | return defaultValue; |
| | | } |
| | | try |
| | | { |
| | | return NumberFormat.getInstance().parse(valueStr); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | return defaultValue; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 转换为Number<br> |
| | | * 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br> |
| | | * 转换失败不会报错 |
| | | * |
| | | * @param value 被转换的值 |
| | | * @return 结果 |
| | | */ |
| | | public static Number toNumber(Object value) |
| | | { |
| | | return toNumber(value, null); |
| | | } |
| | | |
| | | /** |
| | | * 转换为int<br> |
| | | * 如果给定的值为空,或者转换失败,返回默认值<br> |
| | | * 转换失败不会报错 |
| | | * |
| | | * @param value 被转换的值 |
| | | * @param defaultValue 转换错误时的默认值 |
| | | * @return 结果 |
| | | */ |
| | | public static Integer toInt(Object value, Integer defaultValue) |
| | | { |
| | | if (value == null) |
| | | { |
| | | return defaultValue; |
| | | } |
| | | if (value instanceof Integer) |
| | | { |
| | | return (Integer) value; |
| | | } |
| | | if (value instanceof Number) |
| | | { |
| | | return ((Number) value).intValue(); |
| | | } |
| | | final String valueStr = toStr(value, null); |
| | | if (StringUtils.isEmpty(valueStr)) |
| | | { |
| | | return defaultValue; |
| | | } |
| | | try |
| | | { |
| | | return Integer.parseInt(valueStr.trim()); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | return defaultValue; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 转换为int<br> |
| | | * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br> |
| | | * 转换失败不会报错 |
| | | * |
| | | * @param value 被转换的值 |
| | | * @return 结果 |
| | | */ |
| | | public static Integer toInt(Object value) |
| | | { |
| | | return toInt(value, null); |
| | | } |
| | | |
| | | /** |
| | | * 转换为Integer数组<br> |
| | | * |
| | | * @param str 被转换的值 |
| | | * @return 结果 |
| | | */ |
| | | public static Integer[] toIntArray(String str) |
| | | { |
| | | return toIntArray(",", str); |
| | | } |
| | | |
| | | /** |
| | | * 转换为Long数组<br> |
| | | * |
| | | * @param str 被转换的值 |
| | | * @return 结果 |
| | | */ |
| | | public static Long[] toLongArray(String str) |
| | | { |
| | | return toLongArray(",", str); |
| | | } |
| | | |
| | | /** |
| | | * 转换为Integer数组<br> |
| | | * |
| | | * @param split 分隔符 |
| | | * @param str 被转换的值 |
| | | * @return 结果 |
| | | */ |
| | | public static Integer[] toIntArray(String split, String str) |
| | | { |
| | | if (StringUtils.isEmpty(str)) |
| | | { |
| | | return new Integer[] {}; |
| | | } |
| | | String[] arr = str.split(split); |
| | | final Integer[] ints = new Integer[arr.length]; |
| | | for (int i = 0; i < arr.length; i++) |
| | | { |
| | | final Integer v = toInt(arr[i], 0); |
| | | ints[i] = v; |
| | | } |
| | | return ints; |
| | | } |
| | | |
| | | /** |
| | | * 转换为Long数组<br> |
| | | * |
| | | * @param split 分隔符 |
| | | * @param str 被转换的值 |
| | | * @return 结果 |
| | | */ |
| | | public static Long[] toLongArray(String split, String str) |
| | | { |
| | | if (StringUtils.isEmpty(str)) |
| | | { |
| | | return new Long[] {}; |
| | | } |
| | | String[] arr = str.split(split); |
| | | final Long[] longs = new Long[arr.length]; |
| | | for (int i = 0; i < arr.length; i++) |
| | | { |
| | | final Long v = toLong(arr[i], null); |
| | | longs[i] = v; |
| | | } |
| | | return longs; |
| | | } |
| | | |
| | | /** |
| | | * 转换为String数组<br> |
| | | * |
| | | * @param str 被转换的值 |
| | | * @return 结果 |
| | | */ |
| | | public static String[] toStrArray(String str) |
| | | { |
| | | return toStrArray(",", str); |
| | | } |
| | | |
| | | /** |
| | | * 转换为String数组<br> |
| | | * |
| | | * @param split 分隔符 |
| | | * @param str 被转换的值 |
| | | * @return 结果 |
| | | */ |
| | | public static String[] toStrArray(String split, String str) |
| | | { |
| | | return str.split(split); |
| | | } |
| | | |
| | | /** |
| | | * 转换为long<br> |
| | | * 如果给定的值为空,或者转换失败,返回默认值<br> |
| | | * 转换失败不会报错 |
| | | * |
| | | * @param value 被转换的值 |
| | | * @param defaultValue 转换错误时的默认值 |
| | | * @return 结果 |
| | | */ |
| | | public static Long toLong(Object value, Long defaultValue) |
| | | { |
| | | if (value == null) |
| | | { |
| | | return defaultValue; |
| | | } |
| | | if (value instanceof Long) |
| | | { |
| | | return (Long) value; |
| | | } |
| | | if (value instanceof Number) |
| | | { |
| | | return ((Number) value).longValue(); |
| | | } |
| | | final String valueStr = toStr(value, null); |
| | | if (StringUtils.isEmpty(valueStr)) |
| | | { |
| | | return defaultValue; |
| | | } |
| | | try |
| | | { |
| | | // 支持科学计数法 |
| | | return new BigDecimal(valueStr.trim()).longValue(); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | return defaultValue; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 转换为long<br> |
| | | * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br> |
| | | * 转换失败不会报错 |
| | | * |
| | | * @param value 被转换的值 |
| | | * @return 结果 |
| | | */ |
| | | public static Long toLong(Object value) |
| | | { |
| | | return toLong(value, null); |
| | | } |
| | | |
| | | /** |
| | | * 转换为double<br> |
| | | * 如果给定的值为空,或者转换失败,返回默认值<br> |
| | | * 转换失败不会报错 |
| | | * |
| | | * @param value 被转换的值 |
| | | * @param defaultValue 转换错误时的默认值 |
| | | * @return 结果 |
| | | */ |
| | | public static Double toDouble(Object value, Double defaultValue) |
| | | { |
| | | if (value == null) |
| | | { |
| | | return defaultValue; |
| | | } |
| | | if (value instanceof Double) |
| | | { |
| | | return (Double) value; |
| | | } |
| | | if (value instanceof Number) |
| | | { |
| | | return ((Number) value).doubleValue(); |
| | | } |
| | | final String valueStr = toStr(value, null); |
| | | if (StringUtils.isEmpty(valueStr)) |
| | | { |
| | | return defaultValue; |
| | | } |
| | | try |
| | | { |
| | | // 支持科学计数法 |
| | | return new BigDecimal(valueStr.trim()).doubleValue(); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | return defaultValue; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 转换为double<br> |
| | | * 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br> |
| | | * 转换失败不会报错 |
| | | * |
| | | * @param value 被转换的值 |
| | | * @return 结果 |
| | | */ |
| | | public static Double toDouble(Object value) |
| | | { |
| | | return toDouble(value, null); |
| | | } |
| | | |
| | | /** |
| | | * 转换为Float<br> |
| | | * 如果给定的值为空,或者转换失败,返回默认值<br> |
| | | * 转换失败不会报错 |
| | | * |
| | | * @param value 被转换的值 |
| | | * @param defaultValue 转换错误时的默认值 |
| | | * @return 结果 |
| | | */ |
| | | public static Float toFloat(Object value, Float defaultValue) |
| | | { |
| | | if (value == null) |
| | | { |
| | | return defaultValue; |
| | | } |
| | | if (value instanceof Float) |
| | | { |
| | | return (Float) value; |
| | | } |
| | | if (value instanceof Number) |
| | | { |
| | | return ((Number) value).floatValue(); |
| | | } |
| | | final String valueStr = toStr(value, null); |
| | | if (StringUtils.isEmpty(valueStr)) |
| | | { |
| | | return defaultValue; |
| | | } |
| | | try |
| | | { |
| | | return Float.parseFloat(valueStr.trim()); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | return defaultValue; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 转换为Float<br> |
| | | * 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br> |
| | | * 转换失败不会报错 |
| | | * |
| | | * @param value 被转换的值 |
| | | * @return 结果 |
| | | */ |
| | | public static Float toFloat(Object value) |
| | | { |
| | | return toFloat(value, null); |
| | | } |
| | | |
| | | /** |
| | | * 转换为boolean<br> |
| | | * String支持的值为:true、false、yes、ok、no,1,0 如果给定的值为空,或者转换失败,返回默认值<br> |
| | | * 转换失败不会报错 |
| | | * |
| | | * @param value 被转换的值 |
| | | * @param defaultValue 转换错误时的默认值 |
| | | * @return 结果 |
| | | */ |
| | | public static Boolean toBool(Object value, Boolean defaultValue) |
| | | { |
| | | if (value == null) |
| | | { |
| | | return defaultValue; |
| | | } |
| | | if (value instanceof Boolean) |
| | | { |
| | | return (Boolean) value; |
| | | } |
| | | String valueStr = toStr(value, null); |
| | | if (StringUtils.isEmpty(valueStr)) |
| | | { |
| | | return defaultValue; |
| | | } |
| | | valueStr = valueStr.trim().toLowerCase(); |
| | | switch (valueStr) |
| | | { |
| | | case "true": |
| | | case "yes": |
| | | case "ok": |
| | | case "1": |
| | | return true; |
| | | case "false": |
| | | case "no": |
| | | case "0": |
| | | return false; |
| | | default: |
| | | return defaultValue; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 转换为boolean<br> |
| | | * 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br> |
| | | * 转换失败不会报错 |
| | | * |
| | | * @param value 被转换的值 |
| | | * @return 结果 |
| | | */ |
| | | public static Boolean toBool(Object value) |
| | | { |
| | | return toBool(value, null); |
| | | } |
| | | |
| | | /** |
| | | * 转换为Enum对象<br> |
| | | * 如果给定的值为空,或者转换失败,返回默认值<br> |
| | | * |
| | | * @param clazz Enum的Class |
| | | * @param value 值 |
| | | * @param defaultValue 默认值 |
| | | * @return Enum |
| | | */ |
| | | public static <E extends Enum<E>> E toEnum(Class<E> clazz, Object value, E defaultValue) |
| | | { |
| | | if (value == null) |
| | | { |
| | | return defaultValue; |
| | | } |
| | | if (clazz.isAssignableFrom(value.getClass())) |
| | | { |
| | | @SuppressWarnings("unchecked") |
| | | E myE = (E) value; |
| | | return myE; |
| | | } |
| | | final String valueStr = toStr(value, null); |
| | | if (StringUtils.isEmpty(valueStr)) |
| | | { |
| | | return defaultValue; |
| | | } |
| | | try |
| | | { |
| | | return Enum.valueOf(clazz, valueStr); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | return defaultValue; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 转换为Enum对象<br> |
| | | * 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br> |
| | | * |
| | | * @param clazz Enum的Class |
| | | * @param value 值 |
| | | * @return Enum |
| | | */ |
| | | public static <E extends Enum<E>> E toEnum(Class<E> clazz, Object value) |
| | | { |
| | | return toEnum(clazz, value, null); |
| | | } |
| | | |
| | | /** |
| | | * 转换为BigInteger<br> |
| | | * 如果给定的值为空,或者转换失败,返回默认值<br> |
| | | * 转换失败不会报错 |
| | | * |
| | | * @param value 被转换的值 |
| | | * @param defaultValue 转换错误时的默认值 |
| | | * @return 结果 |
| | | */ |
| | | public static BigInteger toBigInteger(Object value, BigInteger defaultValue) |
| | | { |
| | | if (value == null) |
| | | { |
| | | return defaultValue; |
| | | } |
| | | if (value instanceof BigInteger) |
| | | { |
| | | return (BigInteger) value; |
| | | } |
| | | if (value instanceof Long) |
| | | { |
| | | return BigInteger.valueOf((Long) value); |
| | | } |
| | | final String valueStr = toStr(value, null); |
| | | if (StringUtils.isEmpty(valueStr)) |
| | | { |
| | | return defaultValue; |
| | | } |
| | | try |
| | | { |
| | | return new BigInteger(valueStr); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | return defaultValue; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 转换为BigInteger<br> |
| | | * 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br> |
| | | * 转换失败不会报错 |
| | | * |
| | | * @param value 被转换的值 |
| | | * @return 结果 |
| | | */ |
| | | public static BigInteger toBigInteger(Object value) |
| | | { |
| | | return toBigInteger(value, null); |
| | | } |
| | | |
| | | /** |
| | | * 转换为BigDecimal<br> |
| | | * 如果给定的值为空,或者转换失败,返回默认值<br> |
| | | * 转换失败不会报错 |
| | | * |
| | | * @param value 被转换的值 |
| | | * @param defaultValue 转换错误时的默认值 |
| | | * @return 结果 |
| | | */ |
| | | public static BigDecimal toBigDecimal(Object value, BigDecimal defaultValue) |
| | | { |
| | | if (value == null) |
| | | { |
| | | return defaultValue; |
| | | } |
| | | if (value instanceof BigDecimal) |
| | | { |
| | | return (BigDecimal) value; |
| | | } |
| | | if (value instanceof Long) |
| | | { |
| | | return new BigDecimal((Long) value); |
| | | } |
| | | if (value instanceof Double) |
| | | { |
| | | return BigDecimal.valueOf((Double) value); |
| | | } |
| | | if (value instanceof Integer) |
| | | { |
| | | return new BigDecimal((Integer) value); |
| | | } |
| | | final String valueStr = toStr(value, null); |
| | | if (StringUtils.isEmpty(valueStr)) |
| | | { |
| | | return defaultValue; |
| | | } |
| | | try |
| | | { |
| | | return new BigDecimal(valueStr); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | return defaultValue; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 转换为BigDecimal<br> |
| | | * 如果给定的值为空,或者转换失败,返回默认值<br> |
| | | * 转换失败不会报错 |
| | | * |
| | | * @param value 被转换的值 |
| | | * @return 结果 |
| | | */ |
| | | public static BigDecimal toBigDecimal(Object value) |
| | | { |
| | | return toBigDecimal(value, null); |
| | | } |
| | | |
| | | /** |
| | | * 将对象转为字符串<br> |
| | | * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法 |
| | | * |
| | | * @param obj 对象 |
| | | * @return 字符串 |
| | | */ |
| | | public static String utf8Str(Object obj) |
| | | { |
| | | return str(obj, CharsetKit.CHARSET_UTF_8); |
| | | } |
| | | |
| | | /** |
| | | * 将对象转为字符串<br> |
| | | * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法 |
| | | * |
| | | * @param obj 对象 |
| | | * @param charsetName 字符集 |
| | | * @return 字符串 |
| | | */ |
| | | public static String str(Object obj, String charsetName) |
| | | { |
| | | return str(obj, Charset.forName(charsetName)); |
| | | } |
| | | |
| | | /** |
| | | * 将对象转为字符串<br> |
| | | * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法 |
| | | * |
| | | * @param obj 对象 |
| | | * @param charset 字符集 |
| | | * @return 字符串 |
| | | */ |
| | | public static String str(Object obj, Charset charset) |
| | | { |
| | | if (null == obj) |
| | | { |
| | | return null; |
| | | } |
| | | |
| | | if (obj instanceof String) |
| | | { |
| | | return (String) obj; |
| | | } |
| | | else if (obj instanceof byte[] || obj instanceof Byte[]) |
| | | { |
| | | if (obj instanceof byte[]) |
| | | { |
| | | return str((byte[]) obj, charset); |
| | | } |
| | | else |
| | | { |
| | | Byte[] bytes = (Byte[]) obj; |
| | | int length = bytes.length; |
| | | byte[] dest = new byte[length]; |
| | | for (int i = 0; i < length; i++) |
| | | { |
| | | dest[i] = bytes[i]; |
| | | } |
| | | return str(dest, charset); |
| | | } |
| | | } |
| | | else if (obj instanceof ByteBuffer) |
| | | { |
| | | return str((ByteBuffer) obj, charset); |
| | | } |
| | | return obj.toString(); |
| | | } |
| | | |
| | | /** |
| | | * 将byte数组转为字符串 |
| | | * |
| | | * @param bytes byte数组 |
| | | * @param charset 字符集 |
| | | * @return 字符串 |
| | | */ |
| | | public static String str(byte[] bytes, String charset) |
| | | { |
| | | return str(bytes, StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset)); |
| | | } |
| | | |
| | | /** |
| | | * 解码字节码 |
| | | * |
| | | * @param data 字符串 |
| | | * @param charset 字符集,如果此字段为空,则解码的结果取决于平台 |
| | | * @return 解码后的字符串 |
| | | */ |
| | | public static String str(byte[] data, Charset charset) |
| | | { |
| | | if (data == null) |
| | | { |
| | | return null; |
| | | } |
| | | |
| | | if (null == charset) |
| | | { |
| | | return new String(data); |
| | | } |
| | | return new String(data, charset); |
| | | } |
| | | |
| | | /** |
| | | * 将编码的byteBuffer数据转换为字符串 |
| | | * |
| | | * @param data 数据 |
| | | * @param charset 字符集,如果为空使用当前系统字符集 |
| | | * @return 字符串 |
| | | */ |
| | | public static String str(ByteBuffer data, String charset) |
| | | { |
| | | if (data == null) |
| | | { |
| | | return null; |
| | | } |
| | | |
| | | return str(data, Charset.forName(charset)); |
| | | } |
| | | |
| | | /** |
| | | * 将编码的byteBuffer数据转换为字符串 |
| | | * |
| | | * @param data 数据 |
| | | * @param charset 字符集,如果为空使用当前系统字符集 |
| | | * @return 字符串 |
| | | */ |
| | | public static String str(ByteBuffer data, Charset charset) |
| | | { |
| | | if (null == charset) |
| | | { |
| | | charset = Charset.defaultCharset(); |
| | | } |
| | | return charset.decode(data).toString(); |
| | | } |
| | | |
| | | // ----------------------------------------------------------------------- 全角半角转换 |
| | | /** |
| | | * 半角转全角 |
| | | * |
| | | * @param input String. |
| | | * @return 全角字符串. |
| | | */ |
| | | public static String toSBC(String input) |
| | | { |
| | | return toSBC(input, null); |
| | | } |
| | | |
| | | /** |
| | | * 半角转全角 |
| | | * |
| | | * @param input String |
| | | * @param notConvertSet 不替换的字符集合 |
| | | * @return 全角字符串. |
| | | */ |
| | | public static String toSBC(String input, Set<Character> notConvertSet) |
| | | { |
| | | char[] c = input.toCharArray(); |
| | | for (int i = 0; i < c.length; i++) |
| | | { |
| | | if (null != notConvertSet && notConvertSet.contains(c[i])) |
| | | { |
| | | // 跳过不替换的字符 |
| | | continue; |
| | | } |
| | | |
| | | if (c[i] == ' ') |
| | | { |
| | | c[i] = '\u3000'; |
| | | } |
| | | else if (c[i] < '\177') |
| | | { |
| | | c[i] = (char) (c[i] + 65248); |
| | | |
| | | } |
| | | } |
| | | return new String(c); |
| | | } |
| | | |
| | | /** |
| | | * 全角转半角 |
| | | * |
| | | * @param input String. |
| | | * @return 半角字符串 |
| | | */ |
| | | public static String toDBC(String input) |
| | | { |
| | | return toDBC(input, null); |
| | | } |
| | | |
| | | /** |
| | | * 替换全角为半角 |
| | | * |
| | | * @param text 文本 |
| | | * @param notConvertSet 不替换的字符集合 |
| | | * @return 替换后的字符 |
| | | */ |
| | | public static String toDBC(String text, Set<Character> notConvertSet) |
| | | { |
| | | char[] c = text.toCharArray(); |
| | | for (int i = 0; i < c.length; i++) |
| | | { |
| | | if (null != notConvertSet && notConvertSet.contains(c[i])) |
| | | { |
| | | // 跳过不替换的字符 |
| | | continue; |
| | | } |
| | | |
| | | if (c[i] == '\u3000') |
| | | { |
| | | c[i] = ' '; |
| | | } |
| | | else if (c[i] > '\uFF00' && c[i] < '\uFF5F') |
| | | { |
| | | c[i] = (char) (c[i] - 65248); |
| | | } |
| | | } |
| | | return new String(c); |
| | | } |
| | | |
| | | /** |
| | | * 数字金额大写转换 先写个完整的然后将如零拾替换成零 |
| | | * |
| | | * @param n 数字 |
| | | * @return 中文大写数字 |
| | | */ |
| | | public static String digitUppercase(double n) |
| | | { |
| | | String[] fraction = { "角", "分" }; |
| | | String[] digit = { "零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖" }; |
| | | String[][] unit = { { "元", "万", "亿" }, { "", "拾", "佰", "仟" } }; |
| | | |
| | | String head = n < 0 ? "负" : ""; |
| | | n = Math.abs(n); |
| | | |
| | | String s = ""; |
| | | for (int i = 0; i < fraction.length; i++) |
| | | { |
| | | // 优化double计算精度丢失问题 |
| | | BigDecimal nNum = new BigDecimal(n); |
| | | BigDecimal decimal = new BigDecimal(10); |
| | | BigDecimal scale = nNum.multiply(decimal).setScale(2, RoundingMode.HALF_EVEN); |
| | | double d = scale.doubleValue(); |
| | | s += (digit[(int) (Math.floor(d * Math.pow(10, i)) % 10)] + fraction[i]).replaceAll("(零.)+", ""); |
| | | } |
| | | if (s.length() < 1) |
| | | { |
| | | s = "整"; |
| | | } |
| | | int integerPart = (int) Math.floor(n); |
| | | |
| | | for (int i = 0; i < unit[0].length && integerPart > 0; i++) |
| | | { |
| | | String p = ""; |
| | | for (int j = 0; j < unit[1].length && n > 0; j++) |
| | | { |
| | | p = digit[integerPart % 10] + unit[1][j] + p; |
| | | integerPart = integerPart / 10; |
| | | } |
| | | s = p.replaceAll("(零.)*零$", "").replaceAll("^$", "零") + unit[0][i] + s; |
| | | } |
| | | return head + s.replaceAll("(零.)*零元", "元").replaceFirst("(零.)+", "").replaceAll("(零.)+", "零").replaceAll("^整$", "零元整"); |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.text; |
| | | |
| | | import com.ruoyi.common.core.utils.StringUtils; |
| | | |
| | | /** |
| | | * 字符串格式化 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class StrFormatter |
| | | { |
| | | public static final String EMPTY_JSON = "{}"; |
| | | public static final char C_BACKSLASH = '\\'; |
| | | public static final char C_DELIM_START = '{'; |
| | | public static final char C_DELIM_END = '}'; |
| | | |
| | | /** |
| | | * 格式化字符串<br> |
| | | * 此方法只是简单将占位符 {} 按照顺序替换为参数<br> |
| | | * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可<br> |
| | | * 例:<br> |
| | | * 通常使用:format("this is {} for {}", "a", "b") -> this is a for b<br> |
| | | * 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a<br> |
| | | * 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b<br> |
| | | * |
| | | * @param strPattern 字符串模板 |
| | | * @param argArray 参数列表 |
| | | * @return 结果 |
| | | */ |
| | | public static String format(final String strPattern, final Object... argArray) |
| | | { |
| | | if (StringUtils.isEmpty(strPattern) || StringUtils.isEmpty(argArray)) |
| | | { |
| | | return strPattern; |
| | | } |
| | | final int strPatternLength = strPattern.length(); |
| | | |
| | | // 初始化定义好的长度以获得更好的性能 |
| | | StringBuilder sbuf = new StringBuilder(strPatternLength + 50); |
| | | |
| | | int handledPosition = 0; |
| | | int delimIndex;// 占位符所在位置 |
| | | for (int argIndex = 0; argIndex < argArray.length; argIndex++) |
| | | { |
| | | delimIndex = strPattern.indexOf(EMPTY_JSON, handledPosition); |
| | | if (delimIndex == -1) |
| | | { |
| | | if (handledPosition == 0) |
| | | { |
| | | return strPattern; |
| | | } |
| | | else |
| | | { // 字符串模板剩余部分不再包含占位符,加入剩余部分后返回结果 |
| | | sbuf.append(strPattern, handledPosition, strPatternLength); |
| | | return sbuf.toString(); |
| | | } |
| | | } |
| | | else |
| | | { |
| | | if (delimIndex > 0 && strPattern.charAt(delimIndex - 1) == C_BACKSLASH) |
| | | { |
| | | if (delimIndex > 1 && strPattern.charAt(delimIndex - 2) == C_BACKSLASH) |
| | | { |
| | | // 转义符之前还有一个转义符,占位符依旧有效 |
| | | sbuf.append(strPattern, handledPosition, delimIndex - 1); |
| | | sbuf.append(Convert.utf8Str(argArray[argIndex])); |
| | | handledPosition = delimIndex + 2; |
| | | } |
| | | else |
| | | { |
| | | // 占位符被转义 |
| | | argIndex--; |
| | | sbuf.append(strPattern, handledPosition, delimIndex - 1); |
| | | sbuf.append(C_DELIM_START); |
| | | handledPosition = delimIndex + 1; |
| | | } |
| | | } |
| | | else |
| | | { |
| | | // 正常占位符 |
| | | sbuf.append(strPattern, handledPosition, delimIndex); |
| | | sbuf.append(Convert.utf8Str(argArray[argIndex])); |
| | | handledPosition = delimIndex + 2; |
| | | } |
| | | } |
| | | } |
| | | // 加入最后一个占位符后所有的字符 |
| | | sbuf.append(strPattern, handledPosition, strPattern.length()); |
| | | |
| | | return sbuf.toString(); |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.utils; |
| | | |
| | | import org.apache.commons.lang3.time.DateFormatUtils; |
| | | |
| | | import java.lang.management.ManagementFactory; |
| | | import java.text.ParseException; |
| | | import java.text.SimpleDateFormat; |
| | | import java.time.*; |
| | | import java.util.Date; |
| | | |
| | | /** |
| | | * 时间工具类 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class DateUtils extends org.apache.commons.lang3.time.DateUtils { |
| | | public static String YYYY = "yyyy"; |
| | | |
| | | public static String YYYY_MM = "yyyy-MM"; |
| | | |
| | | public static String YYYY_MM_DD = "yyyy-MM-dd"; |
| | | |
| | | public static String YYYYMMDDHHMMSS = "yyyyMMddHHmmss"; |
| | | |
| | | public static String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss"; |
| | | |
| | | private static String[] parsePatterns = { |
| | | "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM", |
| | | "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM", |
| | | "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"}; |
| | | |
| | | /** |
| | | * 获取当前Date型日期 |
| | | * |
| | | * @return Date() 当前日期 |
| | | */ |
| | | public static Date getNowDate() { |
| | | return new Date(); |
| | | } |
| | | |
| | | /** |
| | | * 获取当前日期, 默认格式为yyyy-MM-dd |
| | | * |
| | | * @return String |
| | | */ |
| | | public static String getDate() { |
| | | return dateTimeNow(YYYY_MM_DD); |
| | | } |
| | | |
| | | public static final String getTime() { |
| | | return dateTimeNow(YYYY_MM_DD_HH_MM_SS); |
| | | } |
| | | |
| | | public static final String dateTimeNow() { |
| | | return dateTimeNow(YYYYMMDDHHMMSS); |
| | | } |
| | | |
| | | public static final String dateTimeNow(final String format) { |
| | | return parseDateToStr(format, new Date()); |
| | | } |
| | | |
| | | public static final String dateTime(final Date date) { |
| | | return parseDateToStr(YYYY_MM_DD, date); |
| | | } |
| | | |
| | | public static final String parseDateToStr(final String format, final Date date) { |
| | | return new SimpleDateFormat(format).format(date); |
| | | } |
| | | |
| | | public static final Date dateTime(final String format, final String ts) { |
| | | try { |
| | | return new SimpleDateFormat(format).parse(ts); |
| | | } catch (ParseException e) { |
| | | throw new RuntimeException(e); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 日期路径 即年/月/日 如2018/08/08 |
| | | */ |
| | | public static final String datePath() { |
| | | Date now = new Date(); |
| | | return DateFormatUtils.format(now, "yyyy/MM/dd"); |
| | | } |
| | | |
| | | /** |
| | | * 日期路径 即年/月/日 如20180808 |
| | | */ |
| | | public static final String dateTime() { |
| | | Date now = new Date(); |
| | | return DateFormatUtils.format(now, "yyyyMMdd"); |
| | | } |
| | | |
| | | /** |
| | | * 日期型字符串转化为日期 格式 |
| | | */ |
| | | public static Date parseDate(Object str) { |
| | | if (str == null) { |
| | | return null; |
| | | } |
| | | try { |
| | | return parseDate(str.toString(), parsePatterns); |
| | | } catch (ParseException e) { |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 获取服务器启动时间 |
| | | */ |
| | | public static Date getServerStartDate() { |
| | | long time = ManagementFactory.getRuntimeMXBean().getStartTime(); |
| | | return new Date(time); |
| | | } |
| | | |
| | | /** |
| | | * 计算时间差 |
| | | * |
| | | * @param endDate 最后时间 |
| | | * @param startTime 开始时间 |
| | | * @return 时间差(天/小时/分钟) |
| | | */ |
| | | public static String timeDistance(Date endDate, Date startTime) { |
| | | long nd = 1000 * 24 * 60 * 60; |
| | | long nh = 1000 * 60 * 60; |
| | | long nm = 1000 * 60; |
| | | // long ns = 1000; |
| | | // 获得两个时间的毫秒时间差异 |
| | | long diff = endDate.getTime() - startTime.getTime(); |
| | | // 计算差多少天 |
| | | long day = diff / nd; |
| | | // 计算差多少小时 |
| | | long hour = diff % nd / nh; |
| | | // 计算差多少分钟 |
| | | long min = diff % nd % nh / nm; |
| | | // 计算差多少秒//输出结果 |
| | | // long sec = diff % nd % nh % nm / ns; |
| | | return day + "天" + hour + "小时" + min + "分钟"; |
| | | } |
| | | |
| | | /** |
| | | * 增加 LocalDateTime ==> Date |
| | | */ |
| | | public static Date toDate(LocalDateTime temporalAccessor) { |
| | | ZonedDateTime zdt = temporalAccessor.atZone(ZoneId.systemDefault()); |
| | | return Date.from(zdt.toInstant()); |
| | | } |
| | | |
| | | /** |
| | | * 增加 LocalDate ==> Date |
| | | */ |
| | | public static Date toDate(LocalDate temporalAccessor) { |
| | | LocalDateTime localDateTime = LocalDateTime.of(temporalAccessor, LocalTime.of(0, 0, 0)); |
| | | ZonedDateTime zdt = localDateTime.atZone(ZoneId.systemDefault()); |
| | | return Date.from(zdt.toInstant()); |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.utils; |
| | | |
| | | import java.io.PrintWriter; |
| | | import java.io.StringWriter; |
| | | |
| | | import org.apache.commons.lang3.exception.ExceptionUtils; |
| | | |
| | | /** |
| | | * 错误信息处理类。 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class ExceptionUtil { |
| | | /** |
| | | * 获取exception的详细错误信息。 |
| | | */ |
| | | public static String getExceptionMessage(Throwable e) { |
| | | StringWriter sw = new StringWriter(); |
| | | e.printStackTrace(new PrintWriter(sw, true)); |
| | | return sw.toString(); |
| | | } |
| | | |
| | | public static String getRootErrorMessage(Exception e) { |
| | | Throwable root = ExceptionUtils.getRootCause(e); |
| | | root = (root == null ? e : root); |
| | | if (root == null) { |
| | | return ""; |
| | | } |
| | | String msg = root.getMessage(); |
| | | if (msg == null) { |
| | | return "null"; |
| | | } |
| | | return StringUtils.defaultString(msg); |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.utils; |
| | | |
| | | import com.alibaba.fastjson2.JSONArray; |
| | | import com.alibaba.fastjson2.JSONObject; |
| | | import com.baomidou.mybatisplus.core.toolkit.ObjectUtils; |
| | | import com.ruoyi.common.core.domain.Result; |
| | | import com.ruoyi.common.core.enums.GaoDeEnum; |
| | | |
| | | import java.io.BufferedReader; |
| | | import java.io.IOException; |
| | | import java.io.InputStreamReader; |
| | | import java.net.URL; |
| | | import java.net.URLConnection; |
| | | |
| | | /** |
| | | * @author HJL |
| | | */ |
| | | public class GaoDeMapUtil { |
| | | |
| | | /** |
| | | * 功能描述: 高德地图Key |
| | | */ |
| | | private static final String GAO_DE_KEY = "3f00f94b6e9f470a5c6f510c3df814ea"; |
| | | |
| | | //申请的账户Key |
| | | |
| | | /** |
| | | * 功能描述: 根据地址名称得到两个地址间的距离 |
| | | * |
| | | * @param start 起始位置 |
| | | * @param end 结束位置 |
| | | * @return long 两个地址间的距离 |
| | | * @author isymikasan |
| | | */ |
| | | public static Long getDistanceByAddress(String start, String end) { |
| | | String startLonLat = getLonLat(start).getDatas(); |
| | | String endLonLat = getLonLat(end).getDatas(); |
| | | return Long.valueOf(getDistance(startLonLat, endLonLat).getDatas().toString()); |
| | | } |
| | | |
| | | /** |
| | | * 功能描述: 地址转换为经纬度 |
| | | * |
| | | * @param address 地址 |
| | | * @return 经纬度 |
| | | */ |
| | | public static Result<String> getLonLat(String address) { |
| | | try { |
| | | // 返回输入地址address的经纬度信息, 格式是 经度,纬度 |
| | | String queryUrl = "http://restapi.amap.com/v3/geocode/geo?key=" + GAO_DE_KEY + "&address=" + address; |
| | | // 高德接口返回的是JSON格式的字符串 |
| | | String queryResult = getResponse(queryUrl); |
| | | JSONObject job = JSONObject.parseObject(queryResult); |
| | | JSONObject json = JSONObject |
| | | .parseObject( |
| | | job.get("geocodes").toString().substring(1, job.get("geocodes").toString().length() - 1)); |
| | | String lngAndLat = json.get("location").toString(); |
| | | return Result.succeed(lngAndLat, "经纬度转换成功!"); |
| | | } catch (Exception e) { |
| | | return Result.failed(e.toString()); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 将经纬度 转换为 地址 |
| | | * |
| | | * @param longitude 经度 |
| | | * @param latitude 纬度 |
| | | * @return 地址名称 |
| | | */ |
| | | public static Result<String> getAddress(String longitude, String latitude) { |
| | | String url; |
| | | try { |
| | | url = "http://restapi.amap.com/v3/geocode/regeo?output=JSON&location=" + longitude + "," + latitude |
| | | + "&key=" + GAO_DE_KEY + "&radius=0&extensions=base"; |
| | | // 高德接口返回的是JSON格式的字符串 |
| | | String queryResult = getResponse(url); |
| | | if (ObjectUtils.isNull(queryResult)) { |
| | | return Result.failed("查询结果为空"); |
| | | } |
| | | // 将获取结果转为json 数据 |
| | | JSONObject obj = JSONObject.parseObject(queryResult); |
| | | if (obj.get(GaoDeEnum.STATUS.getCode()).toString().equals(GaoDeEnum.INT_ONE.getCode())) { |
| | | // 如果没有返回-1 |
| | | JSONObject reGeoCode = obj.getJSONObject(GaoDeEnum.RE_GEO_CODE.getCode()); |
| | | if (reGeoCode.size() > 0) { |
| | | // 在regeocode中拿到 formatted_address 具体位置 |
| | | String formatted = reGeoCode.get("formatted_address").toString(); |
| | | return Result.succeed(formatted, "地址获取成功!"); |
| | | |
| | | } else { |
| | | return Result.failed("未找到相匹配的地址!"); |
| | | } |
| | | } else { |
| | | return Result.failed("请求错误!"); |
| | | } |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | return Result.failed("系统未知异常,请稍后再试"); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 功能描述: 根据两个定位点的经纬度算出两点间的距离 |
| | | * 经纬度格式为: 经度,纬度 |
| | | * |
| | | * @param startLonLat 起始经纬度 |
| | | * @param endLonLat 结束经纬度(目标经纬度) |
| | | * @return 两个定位点之间的距离 |
| | | */ |
| | | public static Result<Long> getDistance(String startLonLat, String endLonLat) { |
| | | // 返回起始地startAddr与目的地endAddr之间的距离,单位:米 |
| | | String queryUrl = |
| | | "http://restapi.amap.com/v3/distance?key=" + GAO_DE_KEY + "&origins=" + startLonLat |
| | | + "&destination=" |
| | | + endLonLat; |
| | | String queryResult = getResponse(queryUrl); |
| | | JSONObject job = JSONObject.parseObject(queryResult); |
| | | JSONArray ja = job.getJSONArray("results"); |
| | | JSONObject jobO = JSONObject.parseObject(ja.getString(0)); |
| | | long result = Long.parseLong(jobO.get("distance").toString()); |
| | | return Result.succeed(result, "距离计算成功!"); |
| | | } |
| | | |
| | | /** |
| | | * 功能描述: 发送请求 |
| | | * |
| | | * @param serverUrl 请求地址 |
| | | * @return 请求结果 |
| | | */ |
| | | private static String getResponse(String serverUrl) { |
| | | // 用JAVA发起http请求,并返回json格式的结果 |
| | | StringBuilder result = new StringBuilder(); |
| | | try { |
| | | URL url = new URL(serverUrl); |
| | | URLConnection conn = url.openConnection(); |
| | | BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream())); |
| | | String line; |
| | | while ((line = in.readLine()) != null) { |
| | | result.append(line); |
| | | } |
| | | in.close(); |
| | | } catch (IOException e) { |
| | | e.printStackTrace(); |
| | | } |
| | | return result.toString(); |
| | | } |
| | | |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.utils; |
| | | |
| | | import com.ruoyi.common.core.constant.SecurityConstants; |
| | | import com.ruoyi.common.core.constant.TokenConstants; |
| | | import com.ruoyi.common.core.text.Convert; |
| | | import io.jsonwebtoken.Claims; |
| | | import io.jsonwebtoken.Jwts; |
| | | import io.jsonwebtoken.SignatureAlgorithm; |
| | | |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * Jwt工具类 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class JwtUtils { |
| | | public static String secret = TokenConstants.SECRET; |
| | | |
| | | /** |
| | | * 从数据声明生成令牌 |
| | | * |
| | | * @param claims 数据声明 |
| | | * @return 令牌 |
| | | */ |
| | | public static String createToken(Map<String, Object> claims) { |
| | | String token = Jwts.builder().setClaims(claims).signWith(SignatureAlgorithm.HS512, secret).compact(); |
| | | return token; |
| | | } |
| | | |
| | | /** |
| | | * 从令牌中获取数据声明 |
| | | * |
| | | * @param token 令牌 |
| | | * @return 数据声明 |
| | | */ |
| | | public static Claims parseToken(String token) { |
| | | return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody(); |
| | | } |
| | | |
| | | /** |
| | | * 根据令牌获取用户标识 |
| | | * |
| | | * @param token 令牌 |
| | | * @return 用户ID |
| | | */ |
| | | public static String getUserKey(String token) { |
| | | Claims claims = parseToken(token); |
| | | return getValue(claims, SecurityConstants.USER_KEY); |
| | | } |
| | | |
| | | /** |
| | | * 根据令牌获取用户标识 |
| | | * |
| | | * @param claims 身份信息 |
| | | * @return 用户ID |
| | | */ |
| | | public static String getUserKey(Claims claims) { |
| | | return getValue(claims, SecurityConstants.USER_KEY); |
| | | } |
| | | |
| | | /** |
| | | * 根据令牌获取用户标识 |
| | | * |
| | | * @param token 令牌 |
| | | * @return 用户ID |
| | | */ |
| | | public static String getUserKeyByUser(String token) { |
| | | Claims claims = parseToken(token); |
| | | return getValue(claims, SecurityConstants.USER_USER_KEY); |
| | | } |
| | | |
| | | /** |
| | | * 根据令牌获取用户标识 |
| | | * |
| | | * @param claims 身份信息 |
| | | * @return 用户ID |
| | | */ |
| | | public static String getUserKeyByUser(Claims claims) { |
| | | return getValue(claims, SecurityConstants.USER_USER_KEY); |
| | | } |
| | | |
| | | /** |
| | | * 根据令牌获取用户标识 |
| | | * |
| | | * @param token 令牌 |
| | | * @return 用户ID |
| | | */ |
| | | public static String getUserKeyByWorker(String token) { |
| | | Claims claims = parseToken(token); |
| | | return getValue(claims, SecurityConstants.USER_WORKER_KEY); |
| | | } |
| | | |
| | | /** |
| | | * 根据令牌获取用户标识 |
| | | * |
| | | * @param claims 身份信息 |
| | | * @return 用户ID |
| | | */ |
| | | public static String getUserKeyByWorker(Claims claims) { |
| | | return getValue(claims, SecurityConstants.USER_WORKER_KEY); |
| | | } |
| | | |
| | | /** |
| | | * 根据令牌获取用户ID |
| | | * |
| | | * @param token 令牌 |
| | | * @return 用户ID |
| | | */ |
| | | public static String getUserId(String token) { |
| | | Claims claims = parseToken(token); |
| | | return getValue(claims, SecurityConstants.DETAILS_USER_ID); |
| | | } |
| | | |
| | | /** |
| | | * 根据身份信息获取用户ID |
| | | * |
| | | * @param claims 身份信息 |
| | | * @return 用户ID |
| | | */ |
| | | public static String getUserId(Claims claims) { |
| | | return getValue(claims, SecurityConstants.DETAILS_USER_ID); |
| | | } |
| | | |
| | | /** |
| | | * 根据令牌获取用户名 |
| | | * |
| | | * @param token 令牌 |
| | | * @return 用户名 |
| | | */ |
| | | public static String getUserName(String token) { |
| | | Claims claims = parseToken(token); |
| | | return getValue(claims, SecurityConstants.DETAILS_USERNAME); |
| | | } |
| | | |
| | | /** |
| | | * 根据身份信息获取用户名 |
| | | * |
| | | * @param claims 身份信息 |
| | | * @return 用户名 |
| | | */ |
| | | public static String getUserName(Claims claims) { |
| | | return getValue(claims, SecurityConstants.DETAILS_USERNAME); |
| | | } |
| | | |
| | | /** |
| | | * 根据身份信息获取键值 |
| | | * |
| | | * @param claims 身份信息 |
| | | * @param key 键 |
| | | * @return 值 |
| | | */ |
| | | public static String getValue(Claims claims, String key) { |
| | | return Convert.toStr(claims.get(key), ""); |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.utils; |
| | | |
| | | import com.baomidou.mybatisplus.core.metadata.IPage; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.ruoyi.common.core.utils.bean.BeanUtils; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * IPage<T> 转IPage<B>封装工具类 |
| | | * |
| | | * @author HJL |
| | | */ |
| | | public class PageCovertUtil { |
| | | /** |
| | | * 将PageInfo对象泛型中的Po对象转化为Vo对象 |
| | | * |
| | | * @param pageInfoPo PageInfo<Po>对象</> |
| | | * @param <V> V类型 |
| | | * @return 分页封装 |
| | | */ |
| | | public static <P, V> IPage<V> pageVoCovert(IPage<P> pageInfoPo, Class<V> v) { |
| | | // 创建Page对象,实际上是一个ArrayList类型的集合 |
| | | try { |
| | | if (pageInfoPo != null) { |
| | | |
| | | IPage<V> page = new Page<>(pageInfoPo.getCurrent(), pageInfoPo.getSize()); |
| | | page.setTotal(pageInfoPo.getTotal()); |
| | | List<P> records = pageInfoPo.getRecords(); |
| | | List<V> list = new ArrayList<>(); |
| | | for (P record : records) { |
| | | if (record != null) { |
| | | V newV = v.newInstance(); |
| | | // 把原对象数据拷贝到新的对象 |
| | | BeanUtils.copyProperties(record, newV); |
| | | list.add(newV); |
| | | } |
| | | } |
| | | page.setRecords(list); |
| | | page.setTotal(pageInfoPo.getTotal()); |
| | | return page; |
| | | } |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | } |
| | | return null; |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.utils; |
| | | |
| | | import com.github.pagehelper.PageHelper; |
| | | import com.ruoyi.common.core.utils.sql.SqlUtil; |
| | | import com.ruoyi.common.core.web.page.PageDomain; |
| | | import com.ruoyi.common.core.web.page.TableSupport; |
| | | |
| | | /** |
| | | * 分页工具类 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class PageUtils extends PageHelper { |
| | | /** |
| | | * 设置请求分页数据 |
| | | */ |
| | | public static void startPage() { |
| | | PageDomain pageDomain = TableSupport.buildPageRequest(); |
| | | Integer pageNum = pageDomain.getPageNum(); |
| | | Integer pageSize = pageDomain.getPageSize(); |
| | | String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy()); |
| | | Boolean reasonable = pageDomain.getReasonable(); |
| | | PageHelper.startPage(pageNum, pageSize, orderBy).setReasonable(reasonable); |
| | | } |
| | | |
| | | /** |
| | | * 清理分页的线程变量 |
| | | */ |
| | | public static void clearPage() { |
| | | PageHelper.clearPage(); |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.utils; |
| | | |
| | | import com.alibaba.fastjson2.JSON; |
| | | import com.ruoyi.common.core.constant.Constants; |
| | | import com.ruoyi.common.core.domain.R; |
| | | import com.ruoyi.common.core.text.Convert; |
| | | import org.springframework.core.io.buffer.DataBuffer; |
| | | import org.springframework.http.HttpHeaders; |
| | | import org.springframework.http.HttpStatus; |
| | | import org.springframework.http.MediaType; |
| | | import org.springframework.http.server.reactive.ServerHttpResponse; |
| | | import org.springframework.util.LinkedCaseInsensitiveMap; |
| | | import org.springframework.web.context.request.RequestAttributes; |
| | | import org.springframework.web.context.request.RequestContextHolder; |
| | | import org.springframework.web.context.request.ServletRequestAttributes; |
| | | import reactor.core.publisher.Mono; |
| | | |
| | | import javax.servlet.ServletRequest; |
| | | import javax.servlet.http.HttpServletRequest; |
| | | import javax.servlet.http.HttpServletResponse; |
| | | import javax.servlet.http.HttpSession; |
| | | import java.io.IOException; |
| | | import java.io.UnsupportedEncodingException; |
| | | import java.net.URLDecoder; |
| | | import java.net.URLEncoder; |
| | | import java.util.Collections; |
| | | import java.util.Enumeration; |
| | | import java.util.HashMap; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * 客户端工具类 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class ServletUtils { |
| | | /** |
| | | * 获取String参数 |
| | | */ |
| | | public static String getParameter(String name) { |
| | | return getRequest().getParameter(name); |
| | | } |
| | | |
| | | /** |
| | | * 获取String参数 |
| | | */ |
| | | public static String getParameter(String name, String defaultValue) { |
| | | return Convert.toStr(getRequest().getParameter(name), defaultValue); |
| | | } |
| | | |
| | | /** |
| | | * 获取Integer参数 |
| | | */ |
| | | public static Integer getParameterToInt(String name) { |
| | | return Convert.toInt(getRequest().getParameter(name)); |
| | | } |
| | | |
| | | /** |
| | | * 获取Integer参数 |
| | | */ |
| | | public static Integer getParameterToInt(String name, Integer defaultValue) { |
| | | return Convert.toInt(getRequest().getParameter(name), defaultValue); |
| | | } |
| | | |
| | | /** |
| | | * 获取Boolean参数 |
| | | */ |
| | | public static Boolean getParameterToBool(String name) { |
| | | return Convert.toBool(getRequest().getParameter(name)); |
| | | } |
| | | |
| | | /** |
| | | * 获取Boolean参数 |
| | | */ |
| | | public static Boolean getParameterToBool(String name, Boolean defaultValue) { |
| | | return Convert.toBool(getRequest().getParameter(name), defaultValue); |
| | | } |
| | | |
| | | /** |
| | | * 获得所有请求参数 |
| | | * |
| | | * @param request 请求对象{@link ServletRequest} |
| | | * @return Map |
| | | */ |
| | | public static Map<String, String[]> getParams(ServletRequest request) { |
| | | final Map<String, String[]> map = request.getParameterMap(); |
| | | return Collections.unmodifiableMap(map); |
| | | } |
| | | |
| | | /** |
| | | * 获得所有请求参数 |
| | | * |
| | | * @param request 请求对象{@link ServletRequest} |
| | | * @return Map |
| | | */ |
| | | public static Map<String, String> getParamMap(ServletRequest request) { |
| | | Map<String, String> params = new HashMap<>(); |
| | | for (Map.Entry<String, String[]> entry : getParams(request).entrySet()) { |
| | | params.put(entry.getKey(), StringUtils.join(entry.getValue(), ",")); |
| | | } |
| | | return params; |
| | | } |
| | | |
| | | /** |
| | | * 获取request |
| | | */ |
| | | public static HttpServletRequest getRequest() { |
| | | try { |
| | | return getRequestAttributes().getRequest(); |
| | | } catch (Exception e) { |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 获取response |
| | | */ |
| | | public static HttpServletResponse getResponse() { |
| | | try { |
| | | return getRequestAttributes().getResponse(); |
| | | } catch (Exception e) { |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 获取session |
| | | */ |
| | | public static HttpSession getSession() { |
| | | return getRequest().getSession(); |
| | | } |
| | | |
| | | public static ServletRequestAttributes getRequestAttributes() { |
| | | try { |
| | | RequestAttributes attributes = RequestContextHolder.getRequestAttributes(); |
| | | return (ServletRequestAttributes) attributes; |
| | | } catch (Exception e) { |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | public static String getHeader(HttpServletRequest request, String name) { |
| | | String value = request.getHeader(name); |
| | | if (StringUtils.isEmpty(value)) { |
| | | return StringUtils.EMPTY; |
| | | } |
| | | return urlDecode(value); |
| | | } |
| | | |
| | | public static Map<String, String> getHeaders(HttpServletRequest request) { |
| | | Map<String, String> map = new LinkedCaseInsensitiveMap<>(); |
| | | Enumeration<String> enumeration = request.getHeaderNames(); |
| | | if (enumeration != null) { |
| | | while (enumeration.hasMoreElements()) { |
| | | String key = enumeration.nextElement(); |
| | | String value = request.getHeader(key); |
| | | map.put(key, value); |
| | | } |
| | | } |
| | | return map; |
| | | } |
| | | |
| | | /** |
| | | * 将字符串渲染到客户端 |
| | | * |
| | | * @param response 渲染对象 |
| | | * @param string 待渲染的字符串 |
| | | */ |
| | | public static void renderString(HttpServletResponse response, String string) { |
| | | try { |
| | | response.setStatus(200); |
| | | response.setContentType("application/json"); |
| | | response.setCharacterEncoding("utf-8"); |
| | | response.getWriter().print(string); |
| | | } catch (IOException e) { |
| | | e.printStackTrace(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 是否是Ajax异步请求 |
| | | * |
| | | * @param request |
| | | */ |
| | | public static boolean isAjaxRequest(HttpServletRequest request) { |
| | | String accept = request.getHeader("accept"); |
| | | if (accept != null && accept.contains("application/json")) { |
| | | return true; |
| | | } |
| | | |
| | | String xRequestedWith = request.getHeader("X-Requested-With"); |
| | | if (xRequestedWith != null && xRequestedWith.contains("XMLHttpRequest")) { |
| | | return true; |
| | | } |
| | | |
| | | String uri = request.getRequestURI(); |
| | | if (StringUtils.inStringIgnoreCase(uri, ".json", ".xml")) { |
| | | return true; |
| | | } |
| | | |
| | | String ajax = request.getParameter("__ajax"); |
| | | return StringUtils.inStringIgnoreCase(ajax, "json", "xml"); |
| | | } |
| | | |
| | | /** |
| | | * 内容编码 |
| | | * |
| | | * @param str 内容 |
| | | * @return 编码后的内容 |
| | | */ |
| | | public static String urlEncode(String str) { |
| | | try { |
| | | return URLEncoder.encode(str, Constants.UTF8); |
| | | } catch (UnsupportedEncodingException e) { |
| | | return StringUtils.EMPTY; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 内容解码 |
| | | * |
| | | * @param str 内容 |
| | | * @return 解码后的内容 |
| | | */ |
| | | public static String urlDecode(String str) { |
| | | try { |
| | | return URLDecoder.decode(str, Constants.UTF8); |
| | | } catch (UnsupportedEncodingException e) { |
| | | return StringUtils.EMPTY; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 设置webflux模型响应 |
| | | * |
| | | * @param response ServerHttpResponse |
| | | * @param value 响应内容 |
| | | * @return Mono<Void> |
| | | */ |
| | | public static Mono<Void> webFluxResponseWriter(ServerHttpResponse response, Object value) { |
| | | return webFluxResponseWriter(response, HttpStatus.OK, value, R.FAIL); |
| | | } |
| | | |
| | | /** |
| | | * 设置webflux模型响应 |
| | | * |
| | | * @param response ServerHttpResponse |
| | | * @param code 响应状态码 |
| | | * @param value 响应内容 |
| | | * @return Mono<Void> |
| | | */ |
| | | public static Mono<Void> webFluxResponseWriter(ServerHttpResponse response, Object value, int code) { |
| | | return webFluxResponseWriter(response, HttpStatus.OK, value, code); |
| | | } |
| | | |
| | | /** |
| | | * 设置webflux模型响应 |
| | | * |
| | | * @param response ServerHttpResponse |
| | | * @param status http状态码 |
| | | * @param code 响应状态码 |
| | | * @param value 响应内容 |
| | | * @return Mono<Void> |
| | | */ |
| | | public static Mono<Void> webFluxResponseWriter(ServerHttpResponse response, HttpStatus status, Object value, int code) { |
| | | return webFluxResponseWriter(response, MediaType.APPLICATION_JSON_VALUE, status, value, code); |
| | | } |
| | | |
| | | /** |
| | | * 设置webflux模型响应 |
| | | * |
| | | * @param response ServerHttpResponse |
| | | * @param contentType content-type |
| | | * @param status http状态码 |
| | | * @param code 响应状态码 |
| | | * @param value 响应内容 |
| | | * @return Mono<Void> |
| | | */ |
| | | public static Mono<Void> webFluxResponseWriter(ServerHttpResponse response, String contentType, HttpStatus status, Object value, int code) { |
| | | response.setStatusCode(status); |
| | | response.getHeaders().add(HttpHeaders.CONTENT_TYPE, contentType); |
| | | R<?> result = R.fail(code, value.toString()); |
| | | DataBuffer dataBuffer = response.bufferFactory().wrap(JSON.toJSONString(result).getBytes()); |
| | | return response.writeWith(Mono.just(dataBuffer)); |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.utils; |
| | | |
| | | /** |
| | | * 雪花算法生成订单编号 |
| | | * |
| | | * @author HJL |
| | | */ |
| | | public class SnowflakeIdWorker { |
| | | /** |
| | | * 开始时间截 (2015-01-01) |
| | | */ |
| | | private final long TWEPOCH = 1420041600000L; |
| | | /** |
| | | * 机器id所占的位数 |
| | | */ |
| | | private final long workerIdBits = 5L; |
| | | /** |
| | | * 数据标识id所占的位数 |
| | | */ |
| | | private final long datacenterIdBits = 5L; |
| | | /** |
| | | * 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数) |
| | | */ |
| | | private final long maxWorkerId = -1L ^ (-1L << workerIdBits); |
| | | /** |
| | | * 支持的最大数据标识id,结果是31 |
| | | */ |
| | | private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); |
| | | /** |
| | | * 序列在id中占的位数 |
| | | */ |
| | | private final long sequenceBits = 12L; |
| | | /** |
| | | * 机器ID向左移12位 |
| | | */ |
| | | private final long WORKER_ID_SHIFT = sequenceBits; |
| | | /** |
| | | * 数据标识id向左移17位(12+5) |
| | | */ |
| | | private final long datacenterIdShift = sequenceBits + workerIdBits; |
| | | /** |
| | | * 时间截向左移22位(5+5+12) |
| | | */ |
| | | private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits; |
| | | /** |
| | | * 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095) |
| | | */ |
| | | private final long sequenceMask = -1L ^ (-1L << sequenceBits); |
| | | /** |
| | | * 工作机器ID(0~31) |
| | | */ |
| | | private final long workerId; |
| | | /** |
| | | * 数据中心ID(0~31) |
| | | */ |
| | | private final long datacenterId; |
| | | /** |
| | | * 毫秒内序列(0~4095) |
| | | */ |
| | | private long sequence = 0L; |
| | | /** |
| | | * 上次生成ID的时间截 |
| | | */ |
| | | private long lastTimestamp = -1L; |
| | | |
| | | /** |
| | | * 构造函数 |
| | | * |
| | | * @param workerId 工作ID (0~31) |
| | | * @param datacenterId 数据中心ID (0~31) |
| | | */ |
| | | public SnowflakeIdWorker(long workerId, long datacenterId) { |
| | | if (workerId > maxWorkerId || workerId < 0) { |
| | | throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId)); |
| | | } |
| | | if (datacenterId > maxDatacenterId || datacenterId < 0) { |
| | | throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId)); |
| | | } |
| | | this.workerId = workerId; |
| | | this.datacenterId = datacenterId; |
| | | } |
| | | |
| | | /** |
| | | * 获得下一个ID (该方法是线程安全的) |
| | | * |
| | | * @return SnowflakeId |
| | | */ |
| | | public synchronized long nextId() { |
| | | long timestamp = timeGen(); |
| | | // 如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常 |
| | | if (timestamp < lastTimestamp) { |
| | | throw new RuntimeException( |
| | | String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp)); |
| | | } |
| | | // 如果是同一时间生成的,则进行毫秒内序列 |
| | | if (lastTimestamp == timestamp) { |
| | | sequence = (sequence + 1) & sequenceMask; |
| | | // 毫秒内序列溢出 |
| | | if (sequence == 0) { |
| | | //阻塞到下一个毫秒,获得新的时间戳 |
| | | timestamp = tilNextMillis(lastTimestamp); |
| | | } |
| | | } |
| | | // 时间戳改变,毫秒内序列重置 |
| | | else { |
| | | sequence = 0L; |
| | | } |
| | | // 上次生成ID的时间截 |
| | | lastTimestamp = timestamp; |
| | | // 移位并通过或运算拼到一起组成64位的ID |
| | | return ((timestamp - TWEPOCH) << timestampLeftShift) |
| | | | (datacenterId << datacenterIdShift) |
| | | | (workerId << WORKER_ID_SHIFT) |
| | | | sequence; |
| | | } |
| | | |
| | | /** |
| | | * 阻塞到下一个毫秒,直到获得新的时间戳 |
| | | * |
| | | * @param lastTimestamp 上次生成ID的时间截 |
| | | * @return 当前时间戳 |
| | | */ |
| | | protected long tilNextMillis(long lastTimestamp) { |
| | | long timestamp = timeGen(); |
| | | while (timestamp <= lastTimestamp) { |
| | | timestamp = timeGen(); |
| | | } |
| | | return timestamp; |
| | | } |
| | | |
| | | /** |
| | | * 返回以毫秒为单位的当前时间 |
| | | * |
| | | * @return 当前时间(毫秒) |
| | | */ |
| | | protected long timeGen() { |
| | | return System.currentTimeMillis(); |
| | | } |
| | | |
| | | public static void main(String[] args) throws InterruptedException { |
| | | SnowflakeIdWorker idWorker = new SnowflakeIdWorker(0, 0); |
| | | for (int i = 0; i < 10; i++) { |
| | | long id = idWorker.nextId(); |
| | | Thread.sleep(1); |
| | | System.out.println(id); |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.utils; |
| | | |
| | | import org.springframework.aop.framework.AopContext; |
| | | import org.springframework.beans.BeansException; |
| | | import org.springframework.beans.factory.NoSuchBeanDefinitionException; |
| | | import org.springframework.beans.factory.config.BeanFactoryPostProcessor; |
| | | import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | /** |
| | | * spring工具类 方便在非spring管理环境中获取bean |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | @Component |
| | | public final class SpringUtils implements BeanFactoryPostProcessor { |
| | | /** |
| | | * Spring应用上下文环境 |
| | | */ |
| | | private static ConfigurableListableBeanFactory beanFactory; |
| | | |
| | | @Override |
| | | public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { |
| | | SpringUtils.beanFactory = beanFactory; |
| | | } |
| | | |
| | | /** |
| | | * 获取对象 |
| | | * |
| | | * @param name |
| | | * @return Object 一个以所给名字注册的bean的实例 |
| | | * @throws org.springframework.beans.BeansException |
| | | */ |
| | | @SuppressWarnings("unchecked") |
| | | public static <T> T getBean(String name) throws BeansException { |
| | | return (T) beanFactory.getBean(name); |
| | | } |
| | | |
| | | /** |
| | | * 获取类型为requiredType的对象 |
| | | * |
| | | * @param clz |
| | | * @return |
| | | * @throws org.springframework.beans.BeansException |
| | | */ |
| | | public static <T> T getBean(Class<T> clz) throws BeansException { |
| | | T result = (T) beanFactory.getBean(clz); |
| | | return result; |
| | | } |
| | | |
| | | /** |
| | | * 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true |
| | | * |
| | | * @param name |
| | | * @return boolean |
| | | */ |
| | | public static boolean containsBean(String name) { |
| | | return beanFactory.containsBean(name); |
| | | } |
| | | |
| | | /** |
| | | * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException) |
| | | * |
| | | * @param name |
| | | * @return boolean |
| | | * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException |
| | | */ |
| | | public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException { |
| | | return beanFactory.isSingleton(name); |
| | | } |
| | | |
| | | /** |
| | | * @param name |
| | | * @return Class 注册对象的类型 |
| | | * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException |
| | | */ |
| | | public static Class<?> getType(String name) throws NoSuchBeanDefinitionException { |
| | | return beanFactory.getType(name); |
| | | } |
| | | |
| | | /** |
| | | * 如果给定的bean名字在bean定义中有别名,则返回这些别名 |
| | | * |
| | | * @param name |
| | | * @return |
| | | * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException |
| | | */ |
| | | public static String[] getAliases(String name) throws NoSuchBeanDefinitionException { |
| | | return beanFactory.getAliases(name); |
| | | } |
| | | |
| | | /** |
| | | * 获取aop代理对象 |
| | | * |
| | | * @param invoker |
| | | * @return |
| | | */ |
| | | @SuppressWarnings("unchecked") |
| | | public static <T> T getAopProxy(T invoker) { |
| | | return (T) AopContext.currentProxy(); |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.utils; |
| | | |
| | | import com.ruoyi.common.core.constant.Constants; |
| | | import com.ruoyi.common.core.text.StrFormatter; |
| | | import org.springframework.util.AntPathMatcher; |
| | | |
| | | import java.util.Collection; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * 字符串工具类 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class StringUtils extends org.apache.commons.lang3.StringUtils { |
| | | /** |
| | | * 空字符串 |
| | | */ |
| | | private static final String NULLSTR = ""; |
| | | |
| | | /** |
| | | * 下划线 |
| | | */ |
| | | private static final char SEPARATOR = '_'; |
| | | |
| | | /** |
| | | * 星号 |
| | | */ |
| | | private static final char ASTERISK = '*'; |
| | | |
| | | /** |
| | | * 获取参数不为空值 |
| | | * |
| | | * @param value defaultValue 要判断的value |
| | | * @return value 返回值 |
| | | */ |
| | | public static <T> T nvl(T value, T defaultValue) { |
| | | return value != null ? value : defaultValue; |
| | | } |
| | | |
| | | /** |
| | | * * 判断一个Collection是否为空, 包含List,Set,Queue |
| | | * |
| | | * @param coll 要判断的Collection |
| | | * @return true:为空 false:非空 |
| | | */ |
| | | public static boolean isEmpty(Collection<?> coll) { |
| | | return isNull(coll) || coll.isEmpty(); |
| | | } |
| | | |
| | | /** |
| | | * * 判断一个Collection是否非空,包含List,Set,Queue |
| | | * |
| | | * @param coll 要判断的Collection |
| | | * @return true:非空 false:空 |
| | | */ |
| | | public static boolean isNotEmpty(Collection<?> coll) { |
| | | return !isEmpty(coll); |
| | | } |
| | | |
| | | /** |
| | | * * 判断一个对象数组是否为空 |
| | | * |
| | | * @param objects 要判断的对象数组 |
| | | * * @return true:为空 false:非空 |
| | | */ |
| | | public static boolean isEmpty(Object[] objects) { |
| | | return isNull(objects) || (objects.length == 0); |
| | | } |
| | | |
| | | /** |
| | | * * 判断一个对象数组是否非空 |
| | | * |
| | | * @param objects 要判断的对象数组 |
| | | * @return true:非空 false:空 |
| | | */ |
| | | public static boolean isNotEmpty(Object[] objects) { |
| | | return !isEmpty(objects); |
| | | } |
| | | |
| | | /** |
| | | * * 判断一个Map是否为空 |
| | | * |
| | | * @param map 要判断的Map |
| | | * @return true:为空 false:非空 |
| | | */ |
| | | public static boolean isEmpty(Map<?, ?> map) { |
| | | return isNull(map) || map.isEmpty(); |
| | | } |
| | | |
| | | /** |
| | | * * 判断一个Map是否为空 |
| | | * |
| | | * @param map 要判断的Map |
| | | * @return true:非空 false:空 |
| | | */ |
| | | public static boolean isNotEmpty(Map<?, ?> map) { |
| | | return !isEmpty(map); |
| | | } |
| | | |
| | | /** |
| | | * * 判断一个字符串是否为空串 |
| | | * |
| | | * @param str String |
| | | * @return true:为空 false:非空 |
| | | */ |
| | | public static boolean isEmpty(String str) { |
| | | return isNull(str) || NULLSTR.equals(str.trim()); |
| | | } |
| | | |
| | | /** |
| | | * * 判断一个字符串是否为非空串 |
| | | * |
| | | * @param str String |
| | | * @return true:非空串 false:空串 |
| | | */ |
| | | public static boolean isNotEmpty(String str) { |
| | | return !isEmpty(str); |
| | | } |
| | | |
| | | /** |
| | | * * 判断一个对象是否为空 |
| | | * |
| | | * @param object Object |
| | | * @return true:为空 false:非空 |
| | | */ |
| | | public static boolean isNull(Object object) { |
| | | return object == null; |
| | | } |
| | | |
| | | /** |
| | | * * 判断一个对象是否非空 |
| | | * |
| | | * @param object Object |
| | | * @return true:非空 false:空 |
| | | */ |
| | | public static boolean isNotNull(Object object) { |
| | | return !isNull(object); |
| | | } |
| | | |
| | | /** |
| | | * * 判断一个对象是否是数组类型(Java基本型别的数组) |
| | | * |
| | | * @param object 对象 |
| | | * @return true:是数组 false:不是数组 |
| | | */ |
| | | public static boolean isArray(Object object) { |
| | | return isNotNull(object) && object.getClass().isArray(); |
| | | } |
| | | |
| | | /** |
| | | * 去空格 |
| | | */ |
| | | public static String trim(String str) { |
| | | return (str == null ? "" : str.trim()); |
| | | } |
| | | |
| | | /** |
| | | * 替换指定字符串的指定区间内字符为"*" |
| | | * |
| | | * @param str 字符串 |
| | | * @param startInclude 开始位置(包含) |
| | | * @param endExclude 结束位置(不包含) |
| | | * @return 替换后的字符串 |
| | | */ |
| | | public static String hide(CharSequence str, int startInclude, int endExclude) { |
| | | if (isEmpty(str)) { |
| | | return NULLSTR; |
| | | } |
| | | final int strLength = str.length(); |
| | | if (startInclude > strLength) { |
| | | return NULLSTR; |
| | | } |
| | | if (endExclude > strLength) { |
| | | endExclude = strLength; |
| | | } |
| | | if (startInclude > endExclude) { |
| | | // 如果起始位置大于结束位置,不替换 |
| | | return NULLSTR; |
| | | } |
| | | final char[] chars = new char[strLength]; |
| | | for (int i = 0; i < strLength; i++) { |
| | | if (i >= startInclude && i < endExclude) { |
| | | chars[i] = ASTERISK; |
| | | } else { |
| | | chars[i] = str.charAt(i); |
| | | } |
| | | } |
| | | return new String(chars); |
| | | } |
| | | |
| | | /** |
| | | * 截取字符串 |
| | | * |
| | | * @param str 字符串 |
| | | * @param start 开始 |
| | | * @return 结果 |
| | | */ |
| | | public static String substring(final String str, int start) { |
| | | if (str == null) { |
| | | return NULLSTR; |
| | | } |
| | | |
| | | if (start < 0) { |
| | | start = str.length() + start; |
| | | } |
| | | |
| | | if (start < 0) { |
| | | start = 0; |
| | | } |
| | | if (start > str.length()) { |
| | | return NULLSTR; |
| | | } |
| | | |
| | | return str.substring(start); |
| | | } |
| | | |
| | | /** |
| | | * 截取字符串 |
| | | * |
| | | * @param str 字符串 |
| | | * @param start 开始 |
| | | * @param end 结束 |
| | | * @return 结果 |
| | | */ |
| | | public static String substring(final String str, int start, int end) { |
| | | if (str == null) { |
| | | return NULLSTR; |
| | | } |
| | | |
| | | if (end < 0) { |
| | | end = str.length() + end; |
| | | } |
| | | if (start < 0) { |
| | | start = str.length() + start; |
| | | } |
| | | |
| | | if (end > str.length()) { |
| | | end = str.length(); |
| | | } |
| | | |
| | | if (start > end) { |
| | | return NULLSTR; |
| | | } |
| | | |
| | | if (start < 0) { |
| | | start = 0; |
| | | } |
| | | if (end < 0) { |
| | | end = 0; |
| | | } |
| | | |
| | | return str.substring(start, end); |
| | | } |
| | | |
| | | /** |
| | | * 判断是否为空,并且不是空白字符 |
| | | * |
| | | * @param str 要判断的value |
| | | * @return 结果 |
| | | */ |
| | | public static boolean hasText(String str) { |
| | | return (str != null && !str.isEmpty() && containsText(str)); |
| | | } |
| | | |
| | | private static boolean containsText(CharSequence str) { |
| | | int strLen = str.length(); |
| | | for (int i = 0; i < strLen; i++) { |
| | | if (!Character.isWhitespace(str.charAt(i))) { |
| | | return true; |
| | | } |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | /** |
| | | * 格式化文本, {} 表示占位符<br> |
| | | * 此方法只是简单将占位符 {} 按照顺序替换为参数<br> |
| | | * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可<br> |
| | | * 例:<br> |
| | | * 通常使用:format("this is {} for {}", "a", "b") -> this is a for b<br> |
| | | * 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a<br> |
| | | * 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b<br> |
| | | * |
| | | * @param template 文本模板,被替换的部分用 {} 表示 |
| | | * @param params 参数值 |
| | | * @return 格式化后的文本 |
| | | */ |
| | | public static String format(String template, Object... params) { |
| | | if (isEmpty(params) || isEmpty(template)) { |
| | | return template; |
| | | } |
| | | return StrFormatter.format(template, params); |
| | | } |
| | | |
| | | /** |
| | | * 是否为http(s)://开头 |
| | | * |
| | | * @param link 链接 |
| | | * @return 结果 |
| | | */ |
| | | public static boolean ishttp(String link) { |
| | | return StringUtils.startsWithAny(link, Constants.HTTP, Constants.HTTPS); |
| | | } |
| | | |
| | | /** |
| | | * 判断给定的collection列表中是否包含数组array 判断给定的数组array中是否包含给定的元素value |
| | | * |
| | | * @param collection 给定的集合 |
| | | * @param array 给定的数组 |
| | | * @return boolean 结果 |
| | | */ |
| | | public static boolean containsAny(Collection<String> collection, String... array) { |
| | | if (isEmpty(collection) || isEmpty(array)) { |
| | | return false; |
| | | } else { |
| | | for (String str : array) { |
| | | if (collection.contains(str)) { |
| | | return true; |
| | | } |
| | | } |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 驼峰转下划线命名 |
| | | */ |
| | | public static String toUnderScoreCase(String str) { |
| | | if (str == null) { |
| | | return null; |
| | | } |
| | | StringBuilder sb = new StringBuilder(); |
| | | // 前置字符是否大写 |
| | | boolean preCharIsUpperCase = true; |
| | | // 当前字符是否大写 |
| | | boolean curreCharIsUpperCase = true; |
| | | // 下一字符是否大写 |
| | | boolean nexteCharIsUpperCase = true; |
| | | for (int i = 0; i < str.length(); i++) { |
| | | char c = str.charAt(i); |
| | | if (i > 0) { |
| | | preCharIsUpperCase = Character.isUpperCase(str.charAt(i - 1)); |
| | | } else { |
| | | preCharIsUpperCase = false; |
| | | } |
| | | |
| | | curreCharIsUpperCase = Character.isUpperCase(c); |
| | | |
| | | if (i < (str.length() - 1)) { |
| | | nexteCharIsUpperCase = Character.isUpperCase(str.charAt(i + 1)); |
| | | } |
| | | |
| | | if (preCharIsUpperCase && curreCharIsUpperCase && !nexteCharIsUpperCase) { |
| | | sb.append(SEPARATOR); |
| | | } else if ((i != 0 && !preCharIsUpperCase) && curreCharIsUpperCase) { |
| | | sb.append(SEPARATOR); |
| | | } |
| | | sb.append(Character.toLowerCase(c)); |
| | | } |
| | | |
| | | return sb.toString(); |
| | | } |
| | | |
| | | /** |
| | | * 是否包含字符串 |
| | | * |
| | | * @param str 验证字符串 |
| | | * @param strs 字符串组 |
| | | * @return 包含返回true |
| | | */ |
| | | public static boolean inStringIgnoreCase(String str, String... strs) { |
| | | if (str != null && strs != null) { |
| | | for (String s : strs) { |
| | | if (str.equalsIgnoreCase(trim(s))) { |
| | | return true; |
| | | } |
| | | } |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | /** |
| | | * 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。 例如:HELLO_WORLD->HelloWorld |
| | | * |
| | | * @param name 转换前的下划线大写方式命名的字符串 |
| | | * @return 转换后的驼峰式命名的字符串 |
| | | */ |
| | | public static String convertToCamelCase(String name) { |
| | | StringBuilder result = new StringBuilder(); |
| | | // 快速检查 |
| | | if (name == null || name.isEmpty()) { |
| | | // 没必要转换 |
| | | return ""; |
| | | } else if (!name.contains("_")) { |
| | | // 不含下划线,仅将首字母大写 |
| | | return name.substring(0, 1).toUpperCase() + name.substring(1); |
| | | } |
| | | // 用下划线将原始字符串分割 |
| | | String[] camels = name.split("_"); |
| | | for (String camel : camels) { |
| | | // 跳过原始字符串中开头、结尾的下换线或双重下划线 |
| | | if (camel.isEmpty()) { |
| | | continue; |
| | | } |
| | | // 首字母大写 |
| | | result.append(camel.substring(0, 1).toUpperCase()); |
| | | result.append(camel.substring(1).toLowerCase()); |
| | | } |
| | | return result.toString(); |
| | | } |
| | | |
| | | /** |
| | | * 驼峰式命名法 |
| | | * 例如:user_name->userName |
| | | */ |
| | | public static String toCamelCase(String s) { |
| | | if (s == null) { |
| | | return null; |
| | | } |
| | | if (s.indexOf(SEPARATOR) == -1) { |
| | | return s; |
| | | } |
| | | s = s.toLowerCase(); |
| | | StringBuilder sb = new StringBuilder(s.length()); |
| | | boolean upperCase = false; |
| | | for (int i = 0; i < s.length(); i++) { |
| | | char c = s.charAt(i); |
| | | |
| | | if (c == SEPARATOR) { |
| | | upperCase = true; |
| | | } else if (upperCase) { |
| | | sb.append(Character.toUpperCase(c)); |
| | | upperCase = false; |
| | | } else { |
| | | sb.append(c); |
| | | } |
| | | } |
| | | return sb.toString(); |
| | | } |
| | | |
| | | /** |
| | | * 查找指定字符串是否匹配指定字符串列表中的任意一个字符串 |
| | | * |
| | | * @param str 指定字符串 |
| | | * @param strs 需要检查的字符串数组 |
| | | * @return 是否匹配 |
| | | */ |
| | | public static boolean matches(String str, List<String> strs) { |
| | | if (isEmpty(str) || isEmpty(strs)) { |
| | | return false; |
| | | } |
| | | for (String pattern : strs) { |
| | | if (isMatch(pattern, str)) { |
| | | return true; |
| | | } |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | /** |
| | | * 判断url是否与规则配置: |
| | | * ? 表示单个字符; |
| | | * * 表示一层路径内的任意字符串,不可跨层级; |
| | | * ** 表示任意层路径; |
| | | * |
| | | * @param pattern 匹配规则 |
| | | * @param url 需要匹配的url |
| | | * @return |
| | | */ |
| | | public static boolean isMatch(String pattern, String url) { |
| | | AntPathMatcher matcher = new AntPathMatcher(); |
| | | return matcher.match(pattern, url); |
| | | } |
| | | |
| | | @SuppressWarnings("unchecked") |
| | | public static <T> T cast(Object obj) { |
| | | return (T) obj; |
| | | } |
| | | |
| | | /** |
| | | * 数字左边补齐0,使之达到指定长度。注意,如果数字转换为字符串后,长度大于size,则只保留 最后size个字符。 |
| | | * |
| | | * @param num 数字对象 |
| | | * @param size 字符串指定长度 |
| | | * @return 返回数字的字符串格式,该字符串为指定长度。 |
| | | */ |
| | | public static final String padl(final Number num, final int size) { |
| | | return padl(num.toString(), size, '0'); |
| | | } |
| | | |
| | | /** |
| | | * 字符串左补齐。如果原始字符串s长度大于size,则只保留最后size个字符。 |
| | | * |
| | | * @param s 原始字符串 |
| | | * @param size 字符串指定长度 |
| | | * @param c 用于补齐的字符 |
| | | * @return 返回指定长度的字符串,由原字符串左补齐或截取得到。 |
| | | */ |
| | | public static final String padl(final String s, final int size, final char c) { |
| | | final StringBuilder sb = new StringBuilder(size); |
| | | if (s != null) { |
| | | final int len = s.length(); |
| | | if (s.length() <= size) { |
| | | for (int i = size - len; i > 0; i--) { |
| | | sb.append(c); |
| | | } |
| | | sb.append(s); |
| | | } else { |
| | | return s.substring(len - size, len); |
| | | } |
| | | } else { |
| | | for (int i = size; i > 0; i--) { |
| | | sb.append(c); |
| | | } |
| | | } |
| | | return sb.toString(); |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.utils.bean; |
| | | |
| | | import java.lang.reflect.Method; |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | import java.util.regex.Matcher; |
| | | import java.util.regex.Pattern; |
| | | |
| | | /** |
| | | * Bean 工具类 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class BeanUtils extends org.springframework.beans.BeanUtils |
| | | { |
| | | /** Bean方法名中属性名开始的下标 */ |
| | | private static final int BEAN_METHOD_PROP_INDEX = 3; |
| | | |
| | | /** * 匹配getter方法的正则表达式 */ |
| | | private static final Pattern GET_PATTERN = Pattern.compile("get(\\p{javaUpperCase}\\w*)"); |
| | | |
| | | /** * 匹配setter方法的正则表达式 */ |
| | | private static final Pattern SET_PATTERN = Pattern.compile("set(\\p{javaUpperCase}\\w*)"); |
| | | |
| | | /** |
| | | * Bean属性复制工具方法。 |
| | | * |
| | | * @param dest 目标对象 |
| | | * @param src 源对象 |
| | | */ |
| | | public static void copyBeanProp(Object dest, Object src) |
| | | { |
| | | try |
| | | { |
| | | copyProperties(src, dest); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | e.printStackTrace(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 获取对象的setter方法。 |
| | | * |
| | | * @param obj 对象 |
| | | * @return 对象的setter方法列表 |
| | | */ |
| | | public static List<Method> getSetterMethods(Object obj) |
| | | { |
| | | // setter方法列表 |
| | | List<Method> setterMethods = new ArrayList<Method>(); |
| | | |
| | | // 获取所有方法 |
| | | Method[] methods = obj.getClass().getMethods(); |
| | | |
| | | // 查找setter方法 |
| | | |
| | | for (Method method : methods) |
| | | { |
| | | Matcher m = SET_PATTERN.matcher(method.getName()); |
| | | if (m.matches() && (method.getParameterTypes().length == 1)) |
| | | { |
| | | setterMethods.add(method); |
| | | } |
| | | } |
| | | // 返回setter方法列表 |
| | | return setterMethods; |
| | | } |
| | | |
| | | /** |
| | | * 获取对象的getter方法。 |
| | | * |
| | | * @param obj 对象 |
| | | * @return 对象的getter方法列表 |
| | | */ |
| | | |
| | | public static List<Method> getGetterMethods(Object obj) |
| | | { |
| | | // getter方法列表 |
| | | List<Method> getterMethods = new ArrayList<Method>(); |
| | | // 获取所有方法 |
| | | Method[] methods = obj.getClass().getMethods(); |
| | | // 查找getter方法 |
| | | for (Method method : methods) |
| | | { |
| | | Matcher m = GET_PATTERN.matcher(method.getName()); |
| | | if (m.matches() && (method.getParameterTypes().length == 0)) |
| | | { |
| | | getterMethods.add(method); |
| | | } |
| | | } |
| | | // 返回getter方法列表 |
| | | return getterMethods; |
| | | } |
| | | |
| | | /** |
| | | * 检查Bean方法名中的属性名是否相等。<br> |
| | | * 如getName()和setName()属性名一样,getName()和setAge()属性名不一样。 |
| | | * |
| | | * @param m1 方法名1 |
| | | * @param m2 方法名2 |
| | | * @return 属性名一样返回true,否则返回false |
| | | */ |
| | | |
| | | public static boolean isMethodPropEquals(String m1, String m2) |
| | | { |
| | | return m1.substring(BEAN_METHOD_PROP_INDEX).equals(m2.substring(BEAN_METHOD_PROP_INDEX)); |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.utils.bean; |
| | | |
| | | import java.util.Set; |
| | | import javax.validation.ConstraintViolation; |
| | | import javax.validation.ConstraintViolationException; |
| | | import javax.validation.Validator; |
| | | |
| | | /** |
| | | * bean对象属性验证 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class BeanValidators |
| | | { |
| | | public static void validateWithException(Validator validator, Object object, Class<?>... groups) |
| | | throws ConstraintViolationException |
| | | { |
| | | Set<ConstraintViolation<Object>> constraintViolations = validator.validate(object, groups); |
| | | if (!constraintViolations.isEmpty()) |
| | | { |
| | | throw new ConstraintViolationException(constraintViolations); |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.utils.file; |
| | | |
| | | import java.io.File; |
| | | import java.util.Objects; |
| | | import org.apache.commons.io.FilenameUtils; |
| | | import org.apache.commons.lang3.StringUtils; |
| | | import org.springframework.web.multipart.MultipartFile; |
| | | |
| | | /** |
| | | * 文件类型工具类 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class FileTypeUtils |
| | | { |
| | | /** |
| | | * 获取文件类型 |
| | | * <p> |
| | | * 例如: ruoyi.txt, 返回: txt |
| | | * |
| | | * @param file 文件名 |
| | | * @return 后缀(不含".") |
| | | */ |
| | | public static String getFileType(File file) |
| | | { |
| | | if (null == file) |
| | | { |
| | | return StringUtils.EMPTY; |
| | | } |
| | | return getFileType(file.getName()); |
| | | } |
| | | |
| | | /** |
| | | * 获取文件类型 |
| | | * <p> |
| | | * 例如: ruoyi.txt, 返回: txt |
| | | * |
| | | * @param fileName 文件名 |
| | | * @return 后缀(不含".") |
| | | */ |
| | | public static String getFileType(String fileName) |
| | | { |
| | | int separatorIndex = fileName.lastIndexOf("."); |
| | | if (separatorIndex < 0) |
| | | { |
| | | return ""; |
| | | } |
| | | return fileName.substring(separatorIndex + 1).toLowerCase(); |
| | | } |
| | | |
| | | /** |
| | | * 获取文件名的后缀 |
| | | * |
| | | * @param file 表单文件 |
| | | * @return 后缀名 |
| | | */ |
| | | public static final String getExtension(MultipartFile file) |
| | | { |
| | | String extension = FilenameUtils.getExtension(file.getOriginalFilename()); |
| | | if (StringUtils.isEmpty(extension)) |
| | | { |
| | | extension = MimeTypeUtils.getExtension(Objects.requireNonNull(file.getContentType())); |
| | | } |
| | | return extension; |
| | | } |
| | | |
| | | /** |
| | | * 获取文件类型 |
| | | * |
| | | * @param photoByte 文件字节码 |
| | | * @return 后缀(不含".") |
| | | */ |
| | | public static String getFileExtendName(byte[] photoByte) |
| | | { |
| | | String strFileExtendName = "JPG"; |
| | | if ((photoByte[0] == 71) && (photoByte[1] == 73) && (photoByte[2] == 70) && (photoByte[3] == 56) |
| | | && ((photoByte[4] == 55) || (photoByte[4] == 57)) && (photoByte[5] == 97)) |
| | | { |
| | | strFileExtendName = "GIF"; |
| | | } |
| | | else if ((photoByte[6] == 74) && (photoByte[7] == 70) && (photoByte[8] == 73) && (photoByte[9] == 70)) |
| | | { |
| | | strFileExtendName = "JPG"; |
| | | } |
| | | else if ((photoByte[0] == 66) && (photoByte[1] == 77)) |
| | | { |
| | | strFileExtendName = "BMP"; |
| | | } |
| | | else if ((photoByte[1] == 80) && (photoByte[2] == 78) && (photoByte[3] == 71)) |
| | | { |
| | | strFileExtendName = "PNG"; |
| | | } |
| | | return strFileExtendName; |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.utils.file; |
| | | |
| | | import java.io.File; |
| | | import java.io.FileInputStream; |
| | | import java.io.FileNotFoundException; |
| | | import java.io.IOException; |
| | | import java.io.OutputStream; |
| | | import java.io.UnsupportedEncodingException; |
| | | import java.net.URLEncoder; |
| | | import java.nio.charset.StandardCharsets; |
| | | import javax.servlet.http.HttpServletRequest; |
| | | import javax.servlet.http.HttpServletResponse; |
| | | import org.apache.commons.lang3.ArrayUtils; |
| | | import com.ruoyi.common.core.utils.StringUtils; |
| | | |
| | | /** |
| | | * 文件处理工具类 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class FileUtils |
| | | { |
| | | /** 字符常量:斜杠 {@code '/'} */ |
| | | public static final char SLASH = '/'; |
| | | |
| | | /** 字符常量:反斜杠 {@code '\\'} */ |
| | | public static final char BACKSLASH = '\\'; |
| | | |
| | | public static String FILENAME_PATTERN = "[a-zA-Z0-9_\\-\\|\\.\\u4e00-\\u9fa5]+"; |
| | | |
| | | /** |
| | | * 输出指定文件的byte数组 |
| | | * |
| | | * @param filePath 文件路径 |
| | | * @param os 输出流 |
| | | * @return |
| | | */ |
| | | public static void writeBytes(String filePath, OutputStream os) throws IOException |
| | | { |
| | | FileInputStream fis = null; |
| | | try |
| | | { |
| | | File file = new File(filePath); |
| | | if (!file.exists()) |
| | | { |
| | | throw new FileNotFoundException(filePath); |
| | | } |
| | | fis = new FileInputStream(file); |
| | | byte[] b = new byte[1024]; |
| | | int length; |
| | | while ((length = fis.read(b)) > 0) |
| | | { |
| | | os.write(b, 0, length); |
| | | } |
| | | } |
| | | catch (IOException e) |
| | | { |
| | | throw e; |
| | | } |
| | | finally |
| | | { |
| | | if (os != null) |
| | | { |
| | | try |
| | | { |
| | | os.close(); |
| | | } |
| | | catch (IOException e1) |
| | | { |
| | | e1.printStackTrace(); |
| | | } |
| | | } |
| | | if (fis != null) |
| | | { |
| | | try |
| | | { |
| | | fis.close(); |
| | | } |
| | | catch (IOException e1) |
| | | { |
| | | e1.printStackTrace(); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 删除文件 |
| | | * |
| | | * @param filePath 文件 |
| | | * @return |
| | | */ |
| | | public static boolean deleteFile(String filePath) |
| | | { |
| | | boolean flag = false; |
| | | File file = new File(filePath); |
| | | // 路径为文件且不为空则进行删除 |
| | | if (file.isFile() && file.exists()) |
| | | { |
| | | flag = file.delete(); |
| | | } |
| | | return flag; |
| | | } |
| | | |
| | | /** |
| | | * 文件名称验证 |
| | | * |
| | | * @param filename 文件名称 |
| | | * @return true 正常 false 非法 |
| | | */ |
| | | public static boolean isValidFilename(String filename) |
| | | { |
| | | return filename.matches(FILENAME_PATTERN); |
| | | } |
| | | |
| | | /** |
| | | * 检查文件是否可下载 |
| | | * |
| | | * @param resource 需要下载的文件 |
| | | * @return true 正常 false 非法 |
| | | */ |
| | | public static boolean checkAllowDownload(String resource) |
| | | { |
| | | // 禁止目录上跳级别 |
| | | if (StringUtils.contains(resource, "..")) |
| | | { |
| | | return false; |
| | | } |
| | | // 判断是否在允许下载的文件规则内 |
| | | return ArrayUtils.contains(MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION, FileTypeUtils.getFileType(resource)); |
| | | } |
| | | |
| | | /** |
| | | * 下载文件名重新编码 |
| | | * |
| | | * @param request 请求对象 |
| | | * @param fileName 文件名 |
| | | * @return 编码后的文件名 |
| | | */ |
| | | public static String setFileDownloadHeader(HttpServletRequest request, String fileName) throws UnsupportedEncodingException |
| | | { |
| | | final String agent = request.getHeader("USER-AGENT"); |
| | | String filename = fileName; |
| | | if (agent.contains("MSIE")) |
| | | { |
| | | // IE浏览器 |
| | | filename = URLEncoder.encode(filename, "utf-8"); |
| | | filename = filename.replace("+", " "); |
| | | } |
| | | else if (agent.contains("Firefox")) |
| | | { |
| | | // 火狐浏览器 |
| | | filename = new String(fileName.getBytes(), "ISO8859-1"); |
| | | } |
| | | else if (agent.contains("Chrome")) |
| | | { |
| | | // google浏览器 |
| | | filename = URLEncoder.encode(filename, "utf-8"); |
| | | } |
| | | else |
| | | { |
| | | // 其它浏览器 |
| | | filename = URLEncoder.encode(filename, "utf-8"); |
| | | } |
| | | return filename; |
| | | } |
| | | |
| | | /** |
| | | * 返回文件名 |
| | | * |
| | | * @param filePath 文件 |
| | | * @return 文件名 |
| | | */ |
| | | public static String getName(String filePath) |
| | | { |
| | | if (null == filePath) |
| | | { |
| | | return null; |
| | | } |
| | | int len = filePath.length(); |
| | | if (0 == len) |
| | | { |
| | | return filePath; |
| | | } |
| | | if (isFileSeparator(filePath.charAt(len - 1))) |
| | | { |
| | | // 以分隔符结尾的去掉结尾分隔符 |
| | | len--; |
| | | } |
| | | |
| | | int begin = 0; |
| | | char c; |
| | | for (int i = len - 1; i > -1; i--) |
| | | { |
| | | c = filePath.charAt(i); |
| | | if (isFileSeparator(c)) |
| | | { |
| | | // 查找最后一个路径分隔符(/或者\) |
| | | begin = i + 1; |
| | | break; |
| | | } |
| | | } |
| | | |
| | | return filePath.substring(begin, len); |
| | | } |
| | | |
| | | /** |
| | | * 是否为Windows或者Linux(Unix)文件分隔符<br> |
| | | * Windows平台下分隔符为\,Linux(Unix)为/ |
| | | * |
| | | * @param c 字符 |
| | | * @return 是否为Windows或者Linux(Unix)文件分隔符 |
| | | */ |
| | | public static boolean isFileSeparator(char c) |
| | | { |
| | | return SLASH == c || BACKSLASH == c; |
| | | } |
| | | |
| | | /** |
| | | * 下载文件名重新编码 |
| | | * |
| | | * @param response 响应对象 |
| | | * @param realFileName 真实文件名 |
| | | * @return |
| | | */ |
| | | public static void setAttachmentResponseHeader(HttpServletResponse response, String realFileName) throws UnsupportedEncodingException |
| | | { |
| | | String percentEncodedFileName = percentEncode(realFileName); |
| | | |
| | | StringBuilder contentDispositionValue = new StringBuilder(); |
| | | contentDispositionValue.append("attachment; filename=") |
| | | .append(percentEncodedFileName) |
| | | .append(";") |
| | | .append("filename*=") |
| | | .append("utf-8''") |
| | | .append(percentEncodedFileName); |
| | | |
| | | response.setHeader("Content-disposition", contentDispositionValue.toString()); |
| | | response.setHeader("download-filename", percentEncodedFileName); |
| | | } |
| | | |
| | | /** |
| | | * 百分号编码工具方法 |
| | | * |
| | | * @param s 需要百分号编码的字符串 |
| | | * @return 百分号编码后的字符串 |
| | | */ |
| | | public static String percentEncode(String s) throws UnsupportedEncodingException |
| | | { |
| | | String encode = URLEncoder.encode(s, StandardCharsets.UTF_8.toString()); |
| | | return encode.replaceAll("\\+", "%20"); |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.utils.file; |
| | | |
| | | import java.io.ByteArrayInputStream; |
| | | import java.io.InputStream; |
| | | import java.net.URL; |
| | | import java.net.URLConnection; |
| | | import java.util.Arrays; |
| | | import org.apache.poi.util.IOUtils; |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | |
| | | /** |
| | | * 图片处理工具类 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class ImageUtils |
| | | { |
| | | private static final Logger log = LoggerFactory.getLogger(ImageUtils.class); |
| | | |
| | | public static byte[] getImage(String imagePath) |
| | | { |
| | | InputStream is = getFile(imagePath); |
| | | try |
| | | { |
| | | return IOUtils.toByteArray(is); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | log.error("图片加载异常 {}", e); |
| | | return null; |
| | | } |
| | | finally |
| | | { |
| | | IOUtils.closeQuietly(is); |
| | | } |
| | | } |
| | | |
| | | public static InputStream getFile(String imagePath) |
| | | { |
| | | try |
| | | { |
| | | byte[] result = readFile(imagePath); |
| | | result = Arrays.copyOf(result, result.length); |
| | | return new ByteArrayInputStream(result); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | log.error("获取图片异常 {}", e); |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | /** |
| | | * 读取文件为字节数据 |
| | | * |
| | | * @param url 地址 |
| | | * @return 字节数据 |
| | | */ |
| | | public static byte[] readFile(String url) |
| | | { |
| | | InputStream in = null; |
| | | try |
| | | { |
| | | // 网络地址 |
| | | URL urlObj = new URL(url); |
| | | URLConnection urlConnection = urlObj.openConnection(); |
| | | urlConnection.setConnectTimeout(30 * 1000); |
| | | urlConnection.setReadTimeout(60 * 1000); |
| | | urlConnection.setDoInput(true); |
| | | in = urlConnection.getInputStream(); |
| | | return IOUtils.toByteArray(in); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | log.error("访问文件异常 {}", e); |
| | | return null; |
| | | } |
| | | finally |
| | | { |
| | | IOUtils.closeQuietly(in); |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.utils.file; |
| | | |
| | | /** |
| | | * 媒体类型工具类 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class MimeTypeUtils |
| | | { |
| | | public static final String IMAGE_PNG = "image/png"; |
| | | |
| | | public static final String IMAGE_JPG = "image/jpg"; |
| | | |
| | | public static final String IMAGE_JPEG = "image/jpeg"; |
| | | |
| | | public static final String IMAGE_BMP = "image/bmp"; |
| | | |
| | | public static final String IMAGE_GIF = "image/gif"; |
| | | |
| | | public static final String[] IMAGE_EXTENSION = { "bmp", "gif", "jpg", "jpeg", "png" }; |
| | | |
| | | public static final String[] FLASH_EXTENSION = { "swf", "flv" }; |
| | | |
| | | public static final String[] MEDIA_EXTENSION = { "swf", "flv", "mp3", "wav", "wma", "wmv", "mid", "avi", "mpg", |
| | | "asf", "rm", "rmvb" }; |
| | | |
| | | public static final String[] VIDEO_EXTENSION = { "mp4", "avi", "rmvb" }; |
| | | |
| | | public static final String[] DEFAULT_ALLOWED_EXTENSION = { |
| | | // 图片 |
| | | "bmp", "gif", "jpg", "jpeg", "png", |
| | | // word excel powerpoint |
| | | "doc", "docx", "xls", "xlsx", "ppt", "pptx", "html", "htm", "txt", |
| | | // 压缩文件 |
| | | "rar", "zip", "gz", "bz2", |
| | | // 视频格式 |
| | | "mp4", "avi", "rmvb", |
| | | // pdf |
| | | "pdf" }; |
| | | |
| | | public static String getExtension(String prefix) |
| | | { |
| | | switch (prefix) |
| | | { |
| | | case IMAGE_PNG: |
| | | return "png"; |
| | | case IMAGE_JPG: |
| | | return "jpg"; |
| | | case IMAGE_JPEG: |
| | | return "jpeg"; |
| | | case IMAGE_BMP: |
| | | return "bmp"; |
| | | case IMAGE_GIF: |
| | | return "gif"; |
| | | default: |
| | | return ""; |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.utils.html; |
| | | |
| | | import com.ruoyi.common.core.utils.StringUtils; |
| | | |
| | | /** |
| | | * 转义和反转义工具类 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class EscapeUtil |
| | | { |
| | | public static final String RE_HTML_MARK = "(<[^<]*?>)|(<[\\s]*?/[^<]*?>)|(<[^<]*?/[\\s]*?>)"; |
| | | |
| | | private static final char[][] TEXT = new char[64][]; |
| | | |
| | | static |
| | | { |
| | | for (int i = 0; i < 64; i++) |
| | | { |
| | | TEXT[i] = new char[] { (char) i }; |
| | | } |
| | | |
| | | // special HTML characters |
| | | TEXT['\''] = "'".toCharArray(); // 单引号 |
| | | TEXT['"'] = """.toCharArray(); // 双引号 |
| | | TEXT['&'] = "&".toCharArray(); // &符 |
| | | TEXT['<'] = "<".toCharArray(); // 小于号 |
| | | TEXT['>'] = ">".toCharArray(); // 大于号 |
| | | } |
| | | |
| | | /** |
| | | * 转义文本中的HTML字符为安全的字符 |
| | | * |
| | | * @param text 被转义的文本 |
| | | * @return 转义后的文本 |
| | | */ |
| | | public static String escape(String text) |
| | | { |
| | | return encode(text); |
| | | } |
| | | |
| | | /** |
| | | * 还原被转义的HTML特殊字符 |
| | | * |
| | | * @param content 包含转义符的HTML内容 |
| | | * @return 转换后的字符串 |
| | | */ |
| | | public static String unescape(String content) |
| | | { |
| | | return decode(content); |
| | | } |
| | | |
| | | /** |
| | | * 清除所有HTML标签,但是不删除标签内的内容 |
| | | * |
| | | * @param content 文本 |
| | | * @return 清除标签后的文本 |
| | | */ |
| | | public static String clean(String content) |
| | | { |
| | | return new HTMLFilter().filter(content); |
| | | } |
| | | |
| | | /** |
| | | * Escape编码 |
| | | * |
| | | * @param text 被编码的文本 |
| | | * @return 编码后的字符 |
| | | */ |
| | | private static String encode(String text) |
| | | { |
| | | if (StringUtils.isEmpty(text)) |
| | | { |
| | | return StringUtils.EMPTY; |
| | | } |
| | | |
| | | final StringBuilder tmp = new StringBuilder(text.length() * 6); |
| | | char c; |
| | | for (int i = 0; i < text.length(); i++) |
| | | { |
| | | c = text.charAt(i); |
| | | if (c < 256) |
| | | { |
| | | tmp.append("%"); |
| | | if (c < 16) |
| | | { |
| | | tmp.append("0"); |
| | | } |
| | | tmp.append(Integer.toString(c, 16)); |
| | | } |
| | | else |
| | | { |
| | | tmp.append("%u"); |
| | | if (c <= 0xfff) |
| | | { |
| | | // issue#I49JU8@Gitee |
| | | tmp.append("0"); |
| | | } |
| | | tmp.append(Integer.toString(c, 16)); |
| | | } |
| | | } |
| | | return tmp.toString(); |
| | | } |
| | | |
| | | /** |
| | | * Escape解码 |
| | | * |
| | | * @param content 被转义的内容 |
| | | * @return 解码后的字符串 |
| | | */ |
| | | public static String decode(String content) |
| | | { |
| | | if (StringUtils.isEmpty(content)) |
| | | { |
| | | return content; |
| | | } |
| | | |
| | | StringBuilder tmp = new StringBuilder(content.length()); |
| | | int lastPos = 0, pos = 0; |
| | | char ch; |
| | | while (lastPos < content.length()) |
| | | { |
| | | pos = content.indexOf("%", lastPos); |
| | | if (pos == lastPos) |
| | | { |
| | | if (content.charAt(pos + 1) == 'u') |
| | | { |
| | | ch = (char) Integer.parseInt(content.substring(pos + 2, pos + 6), 16); |
| | | tmp.append(ch); |
| | | lastPos = pos + 6; |
| | | } |
| | | else |
| | | { |
| | | ch = (char) Integer.parseInt(content.substring(pos + 1, pos + 3), 16); |
| | | tmp.append(ch); |
| | | lastPos = pos + 3; |
| | | } |
| | | } |
| | | else |
| | | { |
| | | if (pos == -1) |
| | | { |
| | | tmp.append(content.substring(lastPos)); |
| | | lastPos = content.length(); |
| | | } |
| | | else |
| | | { |
| | | tmp.append(content.substring(lastPos, pos)); |
| | | lastPos = pos; |
| | | } |
| | | } |
| | | } |
| | | return tmp.toString(); |
| | | } |
| | | |
| | | public static void main(String[] args) |
| | | { |
| | | String html = "<script>alert(1);</script>"; |
| | | String escape = EscapeUtil.escape(html); |
| | | // String html = "<scr<script>ipt>alert(\"XSS\")</scr<script>ipt>"; |
| | | // String html = "<123"; |
| | | // String html = "123>"; |
| | | System.out.println("clean: " + EscapeUtil.clean(html)); |
| | | System.out.println("escape: " + escape); |
| | | System.out.println("unescape: " + EscapeUtil.unescape(escape)); |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.utils.html; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.Collections; |
| | | import java.util.HashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.concurrent.ConcurrentHashMap; |
| | | import java.util.concurrent.ConcurrentMap; |
| | | import java.util.regex.Matcher; |
| | | import java.util.regex.Pattern; |
| | | |
| | | /** |
| | | * HTML过滤器,用于去除XSS漏洞隐患。 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public final class HTMLFilter |
| | | { |
| | | /** |
| | | * regex flag union representing /si modifiers in php |
| | | **/ |
| | | private static final int REGEX_FLAGS_SI = Pattern.CASE_INSENSITIVE | Pattern.DOTALL; |
| | | private static final Pattern P_COMMENTS = Pattern.compile("<!--(.*?)-->", Pattern.DOTALL); |
| | | private static final Pattern P_COMMENT = Pattern.compile("^!--(.*)--$", REGEX_FLAGS_SI); |
| | | private static final Pattern P_TAGS = Pattern.compile("<(.*?)>", Pattern.DOTALL); |
| | | private static final Pattern P_END_TAG = Pattern.compile("^/([a-z0-9]+)", REGEX_FLAGS_SI); |
| | | private static final Pattern P_START_TAG = Pattern.compile("^([a-z0-9]+)(.*?)(/?)$", REGEX_FLAGS_SI); |
| | | private static final Pattern P_QUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)=([\"'])(.*?)\\2", REGEX_FLAGS_SI); |
| | | private static final Pattern P_UNQUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)(=)([^\"\\s']+)", REGEX_FLAGS_SI); |
| | | private static final Pattern P_PROTOCOL = Pattern.compile("^([^:]+):", REGEX_FLAGS_SI); |
| | | private static final Pattern P_ENTITY = Pattern.compile("&#(\\d+);?"); |
| | | private static final Pattern P_ENTITY_UNICODE = Pattern.compile("&#x([0-9a-f]+);?"); |
| | | private static final Pattern P_ENCODE = Pattern.compile("%([0-9a-f]{2});?"); |
| | | private static final Pattern P_VALID_ENTITIES = Pattern.compile("&([^&;]*)(?=(;|&|$))"); |
| | | private static final Pattern P_VALID_QUOTES = Pattern.compile("(>|^)([^<]+?)(<|$)", Pattern.DOTALL); |
| | | private static final Pattern P_END_ARROW = Pattern.compile("^>"); |
| | | private static final Pattern P_BODY_TO_END = Pattern.compile("<([^>]*?)(?=<|$)"); |
| | | private static final Pattern P_XML_CONTENT = Pattern.compile("(^|>)([^<]*?)(?=>)"); |
| | | private static final Pattern P_STRAY_LEFT_ARROW = Pattern.compile("<([^>]*?)(?=<|$)"); |
| | | private static final Pattern P_STRAY_RIGHT_ARROW = Pattern.compile("(^|>)([^<]*?)(?=>)"); |
| | | private static final Pattern P_AMP = Pattern.compile("&"); |
| | | private static final Pattern P_QUOTE = Pattern.compile("\""); |
| | | private static final Pattern P_LEFT_ARROW = Pattern.compile("<"); |
| | | private static final Pattern P_RIGHT_ARROW = Pattern.compile(">"); |
| | | private static final Pattern P_BOTH_ARROWS = Pattern.compile("<>"); |
| | | |
| | | // @xxx could grow large... maybe use sesat's ReferenceMap |
| | | private static final ConcurrentMap<String, Pattern> P_REMOVE_PAIR_BLANKS = new ConcurrentHashMap<>(); |
| | | private static final ConcurrentMap<String, Pattern> P_REMOVE_SELF_BLANKS = new ConcurrentHashMap<>(); |
| | | |
| | | /** |
| | | * set of allowed html elements, along with allowed attributes for each element |
| | | **/ |
| | | private final Map<String, List<String>> vAllowed; |
| | | /** |
| | | * counts of open tags for each (allowable) html element |
| | | **/ |
| | | private final Map<String, Integer> vTagCounts = new HashMap<>(); |
| | | |
| | | /** |
| | | * html elements which must always be self-closing (e.g. "<img />") |
| | | **/ |
| | | private final String[] vSelfClosingTags; |
| | | /** |
| | | * html elements which must always have separate opening and closing tags (e.g. "<b></b>") |
| | | **/ |
| | | private final String[] vNeedClosingTags; |
| | | /** |
| | | * set of disallowed html elements |
| | | **/ |
| | | private final String[] vDisallowed; |
| | | /** |
| | | * attributes which should be checked for valid protocols |
| | | **/ |
| | | private final String[] vProtocolAtts; |
| | | /** |
| | | * allowed protocols |
| | | **/ |
| | | private final String[] vAllowedProtocols; |
| | | /** |
| | | * tags which should be removed if they contain no content (e.g. "<b></b>" or "<b />") |
| | | **/ |
| | | private final String[] vRemoveBlanks; |
| | | /** |
| | | * entities allowed within html markup |
| | | **/ |
| | | private final String[] vAllowedEntities; |
| | | /** |
| | | * flag determining whether comments are allowed in input String. |
| | | */ |
| | | private final boolean stripComment; |
| | | private final boolean encodeQuotes; |
| | | /** |
| | | * flag determining whether to try to make tags when presented with "unbalanced" angle brackets (e.g. "<b text </b>" |
| | | * becomes "<b> text </b>"). If set to false, unbalanced angle brackets will be html escaped. |
| | | */ |
| | | private final boolean alwaysMakeTags; |
| | | |
| | | /** |
| | | * Default constructor. |
| | | */ |
| | | public HTMLFilter() |
| | | { |
| | | vAllowed = new HashMap<>(); |
| | | |
| | | final ArrayList<String> a_atts = new ArrayList<>(); |
| | | a_atts.add("href"); |
| | | a_atts.add("target"); |
| | | vAllowed.put("a", a_atts); |
| | | |
| | | final ArrayList<String> img_atts = new ArrayList<>(); |
| | | img_atts.add("src"); |
| | | img_atts.add("width"); |
| | | img_atts.add("height"); |
| | | img_atts.add("alt"); |
| | | vAllowed.put("img", img_atts); |
| | | |
| | | final ArrayList<String> no_atts = new ArrayList<>(); |
| | | vAllowed.put("b", no_atts); |
| | | vAllowed.put("strong", no_atts); |
| | | vAllowed.put("i", no_atts); |
| | | vAllowed.put("em", no_atts); |
| | | |
| | | vSelfClosingTags = new String[] { "img" }; |
| | | vNeedClosingTags = new String[] { "a", "b", "strong", "i", "em" }; |
| | | vDisallowed = new String[] {}; |
| | | vAllowedProtocols = new String[] { "http", "mailto", "https" }; // no ftp. |
| | | vProtocolAtts = new String[] { "src", "href" }; |
| | | vRemoveBlanks = new String[] { "a", "b", "strong", "i", "em" }; |
| | | vAllowedEntities = new String[] { "amp", "gt", "lt", "quot" }; |
| | | stripComment = true; |
| | | encodeQuotes = true; |
| | | alwaysMakeTags = false; |
| | | } |
| | | |
| | | /** |
| | | * Map-parameter configurable constructor. |
| | | * |
| | | * @param conf map containing configuration. keys match field names. |
| | | */ |
| | | @SuppressWarnings("unchecked") |
| | | public HTMLFilter(final Map<String, Object> conf) |
| | | { |
| | | |
| | | assert conf.containsKey("vAllowed") : "configuration requires vAllowed"; |
| | | assert conf.containsKey("vSelfClosingTags") : "configuration requires vSelfClosingTags"; |
| | | assert conf.containsKey("vNeedClosingTags") : "configuration requires vNeedClosingTags"; |
| | | assert conf.containsKey("vDisallowed") : "configuration requires vDisallowed"; |
| | | assert conf.containsKey("vAllowedProtocols") : "configuration requires vAllowedProtocols"; |
| | | assert conf.containsKey("vProtocolAtts") : "configuration requires vProtocolAtts"; |
| | | assert conf.containsKey("vRemoveBlanks") : "configuration requires vRemoveBlanks"; |
| | | assert conf.containsKey("vAllowedEntities") : "configuration requires vAllowedEntities"; |
| | | |
| | | vAllowed = Collections.unmodifiableMap((HashMap<String, List<String>>) conf.get("vAllowed")); |
| | | vSelfClosingTags = (String[]) conf.get("vSelfClosingTags"); |
| | | vNeedClosingTags = (String[]) conf.get("vNeedClosingTags"); |
| | | vDisallowed = (String[]) conf.get("vDisallowed"); |
| | | vAllowedProtocols = (String[]) conf.get("vAllowedProtocols"); |
| | | vProtocolAtts = (String[]) conf.get("vProtocolAtts"); |
| | | vRemoveBlanks = (String[]) conf.get("vRemoveBlanks"); |
| | | vAllowedEntities = (String[]) conf.get("vAllowedEntities"); |
| | | stripComment = conf.containsKey("stripComment") ? (Boolean) conf.get("stripComment") : true; |
| | | encodeQuotes = conf.containsKey("encodeQuotes") ? (Boolean) conf.get("encodeQuotes") : true; |
| | | alwaysMakeTags = conf.containsKey("alwaysMakeTags") ? (Boolean) conf.get("alwaysMakeTags") : true; |
| | | } |
| | | |
| | | private void reset() |
| | | { |
| | | vTagCounts.clear(); |
| | | } |
| | | |
| | | // --------------------------------------------------------------- |
| | | // my versions of some PHP library functions |
| | | public static String chr(final int decimal) |
| | | { |
| | | return String.valueOf((char) decimal); |
| | | } |
| | | |
| | | public static String htmlSpecialChars(final String s) |
| | | { |
| | | String result = s; |
| | | result = regexReplace(P_AMP, "&", result); |
| | | result = regexReplace(P_QUOTE, """, result); |
| | | result = regexReplace(P_LEFT_ARROW, "<", result); |
| | | result = regexReplace(P_RIGHT_ARROW, ">", result); |
| | | return result; |
| | | } |
| | | |
| | | // --------------------------------------------------------------- |
| | | |
| | | /** |
| | | * given a user submitted input String, filter out any invalid or restricted html. |
| | | * |
| | | * @param input text (i.e. submitted by a user) than may contain html |
| | | * @return "clean" version of input, with only valid, whitelisted html elements allowed |
| | | */ |
| | | public String filter(final String input) |
| | | { |
| | | reset(); |
| | | String s = input; |
| | | |
| | | s = escapeComments(s); |
| | | |
| | | s = balanceHTML(s); |
| | | |
| | | s = checkTags(s); |
| | | |
| | | s = processRemoveBlanks(s); |
| | | |
| | | // s = validateEntities(s); |
| | | |
| | | return s; |
| | | } |
| | | |
| | | public boolean isAlwaysMakeTags() |
| | | { |
| | | return alwaysMakeTags; |
| | | } |
| | | |
| | | public boolean isStripComments() |
| | | { |
| | | return stripComment; |
| | | } |
| | | |
| | | private String escapeComments(final String s) |
| | | { |
| | | final Matcher m = P_COMMENTS.matcher(s); |
| | | final StringBuffer buf = new StringBuffer(); |
| | | if (m.find()) |
| | | { |
| | | final String match = m.group(1); // (.*?) |
| | | m.appendReplacement(buf, Matcher.quoteReplacement("<!--" + htmlSpecialChars(match) + "-->")); |
| | | } |
| | | m.appendTail(buf); |
| | | |
| | | return buf.toString(); |
| | | } |
| | | |
| | | private String balanceHTML(String s) |
| | | { |
| | | if (alwaysMakeTags) |
| | | { |
| | | // |
| | | // try and form html |
| | | // |
| | | s = regexReplace(P_END_ARROW, "", s); |
| | | // 不追加结束标签 |
| | | s = regexReplace(P_BODY_TO_END, "<$1>", s); |
| | | s = regexReplace(P_XML_CONTENT, "$1<$2", s); |
| | | |
| | | } |
| | | else |
| | | { |
| | | // |
| | | // escape stray brackets |
| | | // |
| | | s = regexReplace(P_STRAY_LEFT_ARROW, "<$1", s); |
| | | s = regexReplace(P_STRAY_RIGHT_ARROW, "$1$2><", s); |
| | | |
| | | // |
| | | // the last regexp causes '<>' entities to appear |
| | | // (we need to do a lookahead assertion so that the last bracket can |
| | | // be used in the next pass of the regexp) |
| | | // |
| | | s = regexReplace(P_BOTH_ARROWS, "", s); |
| | | } |
| | | |
| | | return s; |
| | | } |
| | | |
| | | private String checkTags(String s) |
| | | { |
| | | Matcher m = P_TAGS.matcher(s); |
| | | |
| | | final StringBuffer buf = new StringBuffer(); |
| | | while (m.find()) |
| | | { |
| | | String replaceStr = m.group(1); |
| | | replaceStr = processTag(replaceStr); |
| | | m.appendReplacement(buf, Matcher.quoteReplacement(replaceStr)); |
| | | } |
| | | m.appendTail(buf); |
| | | |
| | | // these get tallied in processTag |
| | | // (remember to reset before subsequent calls to filter method) |
| | | final StringBuilder sBuilder = new StringBuilder(buf.toString()); |
| | | for (String key : vTagCounts.keySet()) |
| | | { |
| | | for (int ii = 0; ii < vTagCounts.get(key); ii++) |
| | | { |
| | | sBuilder.append("</").append(key).append(">"); |
| | | } |
| | | } |
| | | s = sBuilder.toString(); |
| | | |
| | | return s; |
| | | } |
| | | |
| | | private String processRemoveBlanks(final String s) |
| | | { |
| | | String result = s; |
| | | for (String tag : vRemoveBlanks) |
| | | { |
| | | if (!P_REMOVE_PAIR_BLANKS.containsKey(tag)) |
| | | { |
| | | P_REMOVE_PAIR_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?></" + tag + ">")); |
| | | } |
| | | result = regexReplace(P_REMOVE_PAIR_BLANKS.get(tag), "", result); |
| | | if (!P_REMOVE_SELF_BLANKS.containsKey(tag)) |
| | | { |
| | | P_REMOVE_SELF_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?/>")); |
| | | } |
| | | result = regexReplace(P_REMOVE_SELF_BLANKS.get(tag), "", result); |
| | | } |
| | | |
| | | return result; |
| | | } |
| | | |
| | | private static String regexReplace(final Pattern regex_pattern, final String replacement, final String s) |
| | | { |
| | | Matcher m = regex_pattern.matcher(s); |
| | | return m.replaceAll(replacement); |
| | | } |
| | | |
| | | private String processTag(final String s) |
| | | { |
| | | // ending tags |
| | | Matcher m = P_END_TAG.matcher(s); |
| | | if (m.find()) |
| | | { |
| | | final String name = m.group(1).toLowerCase(); |
| | | if (allowed(name)) |
| | | { |
| | | if (!inArray(name, vSelfClosingTags)) |
| | | { |
| | | if (vTagCounts.containsKey(name)) |
| | | { |
| | | vTagCounts.put(name, vTagCounts.get(name) - 1); |
| | | return "</" + name + ">"; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | // starting tags |
| | | m = P_START_TAG.matcher(s); |
| | | if (m.find()) |
| | | { |
| | | final String name = m.group(1).toLowerCase(); |
| | | final String body = m.group(2); |
| | | String ending = m.group(3); |
| | | |
| | | // debug( "in a starting tag, name='" + name + "'; body='" + body + "'; ending='" + ending + "'" ); |
| | | if (allowed(name)) |
| | | { |
| | | final StringBuilder params = new StringBuilder(); |
| | | |
| | | final Matcher m2 = P_QUOTED_ATTRIBUTES.matcher(body); |
| | | final Matcher m3 = P_UNQUOTED_ATTRIBUTES.matcher(body); |
| | | final List<String> paramNames = new ArrayList<>(); |
| | | final List<String> paramValues = new ArrayList<>(); |
| | | while (m2.find()) |
| | | { |
| | | paramNames.add(m2.group(1)); // ([a-z0-9]+) |
| | | paramValues.add(m2.group(3)); // (.*?) |
| | | } |
| | | while (m3.find()) |
| | | { |
| | | paramNames.add(m3.group(1)); // ([a-z0-9]+) |
| | | paramValues.add(m3.group(3)); // ([^\"\\s']+) |
| | | } |
| | | |
| | | String paramName, paramValue; |
| | | for (int ii = 0; ii < paramNames.size(); ii++) |
| | | { |
| | | paramName = paramNames.get(ii).toLowerCase(); |
| | | paramValue = paramValues.get(ii); |
| | | |
| | | // debug( "paramName='" + paramName + "'" ); |
| | | // debug( "paramValue='" + paramValue + "'" ); |
| | | // debug( "allowed? " + vAllowed.get( name ).contains( paramName ) ); |
| | | |
| | | if (allowedAttribute(name, paramName)) |
| | | { |
| | | if (inArray(paramName, vProtocolAtts)) |
| | | { |
| | | paramValue = processParamProtocol(paramValue); |
| | | } |
| | | params.append(' ').append(paramName).append("=\\\"").append(paramValue).append("\\\""); |
| | | } |
| | | } |
| | | |
| | | if (inArray(name, vSelfClosingTags)) |
| | | { |
| | | ending = " /"; |
| | | } |
| | | |
| | | if (inArray(name, vNeedClosingTags)) |
| | | { |
| | | ending = ""; |
| | | } |
| | | |
| | | if (ending == null || ending.length() < 1) |
| | | { |
| | | if (vTagCounts.containsKey(name)) |
| | | { |
| | | vTagCounts.put(name, vTagCounts.get(name) + 1); |
| | | } |
| | | else |
| | | { |
| | | vTagCounts.put(name, 1); |
| | | } |
| | | } |
| | | else |
| | | { |
| | | ending = " /"; |
| | | } |
| | | return "<" + name + params + ending + ">"; |
| | | } |
| | | else |
| | | { |
| | | return ""; |
| | | } |
| | | } |
| | | |
| | | // comments |
| | | m = P_COMMENT.matcher(s); |
| | | if (!stripComment && m.find()) |
| | | { |
| | | return "<" + m.group() + ">"; |
| | | } |
| | | |
| | | return ""; |
| | | } |
| | | |
| | | private String processParamProtocol(String s) |
| | | { |
| | | s = decodeEntities(s); |
| | | final Matcher m = P_PROTOCOL.matcher(s); |
| | | if (m.find()) |
| | | { |
| | | final String protocol = m.group(1); |
| | | if (!inArray(protocol, vAllowedProtocols)) |
| | | { |
| | | // bad protocol, turn into local anchor link instead |
| | | s = "#" + s.substring(protocol.length() + 1); |
| | | if (s.startsWith("#//")) |
| | | { |
| | | s = "#" + s.substring(3); |
| | | } |
| | | } |
| | | } |
| | | |
| | | return s; |
| | | } |
| | | |
| | | private String decodeEntities(String s) |
| | | { |
| | | StringBuffer buf = new StringBuffer(); |
| | | |
| | | Matcher m = P_ENTITY.matcher(s); |
| | | while (m.find()) |
| | | { |
| | | final String match = m.group(1); |
| | | final int decimal = Integer.decode(match).intValue(); |
| | | m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); |
| | | } |
| | | m.appendTail(buf); |
| | | s = buf.toString(); |
| | | |
| | | buf = new StringBuffer(); |
| | | m = P_ENTITY_UNICODE.matcher(s); |
| | | while (m.find()) |
| | | { |
| | | final String match = m.group(1); |
| | | final int decimal = Integer.valueOf(match, 16).intValue(); |
| | | m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); |
| | | } |
| | | m.appendTail(buf); |
| | | s = buf.toString(); |
| | | |
| | | buf = new StringBuffer(); |
| | | m = P_ENCODE.matcher(s); |
| | | while (m.find()) |
| | | { |
| | | final String match = m.group(1); |
| | | final int decimal = Integer.valueOf(match, 16).intValue(); |
| | | m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); |
| | | } |
| | | m.appendTail(buf); |
| | | s = buf.toString(); |
| | | |
| | | s = validateEntities(s); |
| | | return s; |
| | | } |
| | | |
| | | private String validateEntities(final String s) |
| | | { |
| | | StringBuffer buf = new StringBuffer(); |
| | | |
| | | // validate entities throughout the string |
| | | Matcher m = P_VALID_ENTITIES.matcher(s); |
| | | while (m.find()) |
| | | { |
| | | final String one = m.group(1); // ([^&;]*) |
| | | final String two = m.group(2); // (?=(;|&|$)) |
| | | m.appendReplacement(buf, Matcher.quoteReplacement(checkEntity(one, two))); |
| | | } |
| | | m.appendTail(buf); |
| | | |
| | | return encodeQuotes(buf.toString()); |
| | | } |
| | | |
| | | private String encodeQuotes(final String s) |
| | | { |
| | | if (encodeQuotes) |
| | | { |
| | | StringBuffer buf = new StringBuffer(); |
| | | Matcher m = P_VALID_QUOTES.matcher(s); |
| | | while (m.find()) |
| | | { |
| | | final String one = m.group(1); // (>|^) |
| | | final String two = m.group(2); // ([^<]+?) |
| | | final String three = m.group(3); // (<|$) |
| | | // 不替换双引号为",防止json格式无效 regexReplace(P_QUOTE, """, two) |
| | | m.appendReplacement(buf, Matcher.quoteReplacement(one + two + three)); |
| | | } |
| | | m.appendTail(buf); |
| | | return buf.toString(); |
| | | } |
| | | else |
| | | { |
| | | return s; |
| | | } |
| | | } |
| | | |
| | | private String checkEntity(final String preamble, final String term) |
| | | { |
| | | |
| | | return ";".equals(term) && isValidEntity(preamble) ? '&' + preamble : "&" + preamble; |
| | | } |
| | | |
| | | private boolean isValidEntity(final String entity) |
| | | { |
| | | return inArray(entity, vAllowedEntities); |
| | | } |
| | | |
| | | private static boolean inArray(final String s, final String[] array) |
| | | { |
| | | for (String item : array) |
| | | { |
| | | if (item != null && item.equals(s)) |
| | | { |
| | | return true; |
| | | } |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | private boolean allowed(final String name) |
| | | { |
| | | return (vAllowed.isEmpty() || vAllowed.containsKey(name)) && !inArray(name, vDisallowed); |
| | | } |
| | | |
| | | private boolean allowedAttribute(final String name, final String paramName) |
| | | { |
| | | return allowed(name) && (vAllowed.isEmpty() || vAllowed.get(name).contains(paramName)); |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.utils.ip; |
| | | |
| | | import java.net.InetAddress; |
| | | import java.net.UnknownHostException; |
| | | import javax.servlet.http.HttpServletRequest; |
| | | import com.ruoyi.common.core.utils.ServletUtils; |
| | | import com.ruoyi.common.core.utils.StringUtils; |
| | | |
| | | /** |
| | | * 获取IP方法 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class IpUtils |
| | | { |
| | | public final static String REGX_0_255 = "(25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]\\d|\\d)"; |
| | | // 匹配 ip |
| | | public final static String REGX_IP = "((" + REGX_0_255 + "\\.){3}" + REGX_0_255 + ")"; |
| | | public final static String REGX_IP_WILDCARD = "(((\\*\\.){3}\\*)|(" + REGX_0_255 + "(\\.\\*){3})|(" + REGX_0_255 + "\\." + REGX_0_255 + ")(\\.\\*){2}" + "|((" + REGX_0_255 + "\\.){3}\\*))"; |
| | | // 匹配网段 |
| | | public final static String REGX_IP_SEG = "(" + REGX_IP + "\\-" + REGX_IP + ")"; |
| | | |
| | | /** |
| | | * 获取客户端IP |
| | | * |
| | | * @return IP地址 |
| | | */ |
| | | public static String getIpAddr() |
| | | { |
| | | return getIpAddr(ServletUtils.getRequest()); |
| | | } |
| | | |
| | | /** |
| | | * 获取客户端IP |
| | | * |
| | | * @param request 请求对象 |
| | | * @return IP地址 |
| | | */ |
| | | public static String getIpAddr(HttpServletRequest request) |
| | | { |
| | | if (request == null) |
| | | { |
| | | return "unknown"; |
| | | } |
| | | String ip = request.getHeader("x-forwarded-for"); |
| | | if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) |
| | | { |
| | | ip = request.getHeader("Proxy-Client-IP"); |
| | | } |
| | | if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) |
| | | { |
| | | ip = request.getHeader("X-Forwarded-For"); |
| | | } |
| | | if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) |
| | | { |
| | | ip = request.getHeader("WL-Proxy-Client-IP"); |
| | | } |
| | | if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) |
| | | { |
| | | ip = request.getHeader("X-Real-IP"); |
| | | } |
| | | |
| | | if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) |
| | | { |
| | | ip = request.getRemoteAddr(); |
| | | } |
| | | |
| | | return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : getMultistageReverseProxyIp(ip); |
| | | } |
| | | |
| | | /** |
| | | * 检查是否为内部IP地址 |
| | | * |
| | | * @param ip IP地址 |
| | | * @return 结果 |
| | | */ |
| | | public static boolean internalIp(String ip) |
| | | { |
| | | byte[] addr = textToNumericFormatV4(ip); |
| | | return internalIp(addr) || "127.0.0.1".equals(ip); |
| | | } |
| | | |
| | | /** |
| | | * 检查是否为内部IP地址 |
| | | * |
| | | * @param addr byte地址 |
| | | * @return 结果 |
| | | */ |
| | | private static boolean internalIp(byte[] addr) |
| | | { |
| | | if (StringUtils.isNull(addr) || addr.length < 2) |
| | | { |
| | | return true; |
| | | } |
| | | final byte b0 = addr[0]; |
| | | final byte b1 = addr[1]; |
| | | // 10.x.x.x/8 |
| | | final byte SECTION_1 = 0x0A; |
| | | // 172.16.x.x/12 |
| | | final byte SECTION_2 = (byte) 0xAC; |
| | | final byte SECTION_3 = (byte) 0x10; |
| | | final byte SECTION_4 = (byte) 0x1F; |
| | | // 192.168.x.x/16 |
| | | final byte SECTION_5 = (byte) 0xC0; |
| | | final byte SECTION_6 = (byte) 0xA8; |
| | | switch (b0) |
| | | { |
| | | case SECTION_1: |
| | | return true; |
| | | case SECTION_2: |
| | | if (b1 >= SECTION_3 && b1 <= SECTION_4) |
| | | { |
| | | return true; |
| | | } |
| | | case SECTION_5: |
| | | switch (b1) |
| | | { |
| | | case SECTION_6: |
| | | return true; |
| | | } |
| | | default: |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 将IPv4地址转换成字节 |
| | | * |
| | | * @param text IPv4地址 |
| | | * @return byte 字节 |
| | | */ |
| | | public static byte[] textToNumericFormatV4(String text) |
| | | { |
| | | if (text.length() == 0) |
| | | { |
| | | return null; |
| | | } |
| | | |
| | | byte[] bytes = new byte[4]; |
| | | String[] elements = text.split("\\.", -1); |
| | | try |
| | | { |
| | | long l; |
| | | int i; |
| | | switch (elements.length) |
| | | { |
| | | case 1: |
| | | l = Long.parseLong(elements[0]); |
| | | if ((l < 0L) || (l > 4294967295L)) |
| | | { |
| | | return null; |
| | | } |
| | | bytes[0] = (byte) (int) (l >> 24 & 0xFF); |
| | | bytes[1] = (byte) (int) ((l & 0xFFFFFF) >> 16 & 0xFF); |
| | | bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF); |
| | | bytes[3] = (byte) (int) (l & 0xFF); |
| | | break; |
| | | case 2: |
| | | l = Integer.parseInt(elements[0]); |
| | | if ((l < 0L) || (l > 255L)) |
| | | { |
| | | return null; |
| | | } |
| | | bytes[0] = (byte) (int) (l & 0xFF); |
| | | l = Integer.parseInt(elements[1]); |
| | | if ((l < 0L) || (l > 16777215L)) |
| | | { |
| | | return null; |
| | | } |
| | | bytes[1] = (byte) (int) (l >> 16 & 0xFF); |
| | | bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF); |
| | | bytes[3] = (byte) (int) (l & 0xFF); |
| | | break; |
| | | case 3: |
| | | for (i = 0; i < 2; ++i) |
| | | { |
| | | l = Integer.parseInt(elements[i]); |
| | | if ((l < 0L) || (l > 255L)) |
| | | { |
| | | return null; |
| | | } |
| | | bytes[i] = (byte) (int) (l & 0xFF); |
| | | } |
| | | l = Integer.parseInt(elements[2]); |
| | | if ((l < 0L) || (l > 65535L)) |
| | | { |
| | | return null; |
| | | } |
| | | bytes[2] = (byte) (int) (l >> 8 & 0xFF); |
| | | bytes[3] = (byte) (int) (l & 0xFF); |
| | | break; |
| | | case 4: |
| | | for (i = 0; i < 4; ++i) |
| | | { |
| | | l = Integer.parseInt(elements[i]); |
| | | if ((l < 0L) || (l > 255L)) |
| | | { |
| | | return null; |
| | | } |
| | | bytes[i] = (byte) (int) (l & 0xFF); |
| | | } |
| | | break; |
| | | default: |
| | | return null; |
| | | } |
| | | } |
| | | catch (NumberFormatException e) |
| | | { |
| | | return null; |
| | | } |
| | | return bytes; |
| | | } |
| | | |
| | | /** |
| | | * 获取IP地址 |
| | | * |
| | | * @return 本地IP地址 |
| | | */ |
| | | public static String getHostIp() |
| | | { |
| | | try |
| | | { |
| | | return InetAddress.getLocalHost().getHostAddress(); |
| | | } |
| | | catch (UnknownHostException e) |
| | | { |
| | | } |
| | | return "127.0.0.1"; |
| | | } |
| | | |
| | | /** |
| | | * 获取主机名 |
| | | * |
| | | * @return 本地主机名 |
| | | */ |
| | | public static String getHostName() |
| | | { |
| | | try |
| | | { |
| | | return InetAddress.getLocalHost().getHostName(); |
| | | } |
| | | catch (UnknownHostException e) |
| | | { |
| | | } |
| | | return "未知"; |
| | | } |
| | | |
| | | /** |
| | | * 从多级反向代理中获得第一个非unknown IP地址 |
| | | * |
| | | * @param ip 获得的IP地址 |
| | | * @return 第一个非unknown IP地址 |
| | | */ |
| | | public static String getMultistageReverseProxyIp(String ip) |
| | | { |
| | | // 多级反向代理检测 |
| | | if (ip != null && ip.indexOf(",") > 0) |
| | | { |
| | | final String[] ips = ip.trim().split(","); |
| | | for (String subIp : ips) |
| | | { |
| | | if (false == isUnknown(subIp)) |
| | | { |
| | | ip = subIp; |
| | | break; |
| | | } |
| | | } |
| | | } |
| | | return StringUtils.substring(ip, 0, 255); |
| | | } |
| | | |
| | | /** |
| | | * 检测给定字符串是否为未知,多用于检测HTTP请求相关 |
| | | * |
| | | * @param checkString 被检测的字符串 |
| | | * @return 是否未知 |
| | | */ |
| | | public static boolean isUnknown(String checkString) |
| | | { |
| | | return StringUtils.isBlank(checkString) || "unknown".equalsIgnoreCase(checkString); |
| | | } |
| | | |
| | | /** |
| | | * 是否为IP |
| | | */ |
| | | public static boolean isIP(String ip) |
| | | { |
| | | return StringUtils.isNotBlank(ip) && ip.matches(REGX_IP); |
| | | } |
| | | |
| | | /** |
| | | * 是否为IP,或 *为间隔的通配符地址 |
| | | */ |
| | | public static boolean isIpWildCard(String ip) |
| | | { |
| | | return StringUtils.isNotBlank(ip) && ip.matches(REGX_IP_WILDCARD); |
| | | } |
| | | |
| | | /** |
| | | * 检测参数是否在ip通配符里 |
| | | */ |
| | | public static boolean ipIsInWildCardNoCheck(String ipWildCard, String ip) |
| | | { |
| | | String[] s1 = ipWildCard.split("\\."); |
| | | String[] s2 = ip.split("\\."); |
| | | boolean isMatchedSeg = true; |
| | | for (int i = 0; i < s1.length && !s1[i].equals("*"); i++) |
| | | { |
| | | if (!s1[i].equals(s2[i])) |
| | | { |
| | | isMatchedSeg = false; |
| | | break; |
| | | } |
| | | } |
| | | return isMatchedSeg; |
| | | } |
| | | |
| | | /** |
| | | * 是否为特定格式如:“10.10.10.1-10.10.10.99”的ip段字符串 |
| | | */ |
| | | public static boolean isIPSegment(String ipSeg) |
| | | { |
| | | return StringUtils.isNotBlank(ipSeg) && ipSeg.matches(REGX_IP_SEG); |
| | | } |
| | | |
| | | /** |
| | | * 判断ip是否在指定网段中 |
| | | */ |
| | | public static boolean ipIsInNetNoCheck(String iparea, String ip) |
| | | { |
| | | int idx = iparea.indexOf('-'); |
| | | String[] sips = iparea.substring(0, idx).split("\\."); |
| | | String[] sipe = iparea.substring(idx + 1).split("\\."); |
| | | String[] sipt = ip.split("\\."); |
| | | long ips = 0L, ipe = 0L, ipt = 0L; |
| | | for (int i = 0; i < 4; ++i) |
| | | { |
| | | ips = ips << 8 | Integer.parseInt(sips[i]); |
| | | ipe = ipe << 8 | Integer.parseInt(sipe[i]); |
| | | ipt = ipt << 8 | Integer.parseInt(sipt[i]); |
| | | } |
| | | if (ips > ipe) |
| | | { |
| | | long t = ips; |
| | | ips = ipe; |
| | | ipe = t; |
| | | } |
| | | return ips <= ipt && ipt <= ipe; |
| | | } |
| | | |
| | | /** |
| | | * 校验ip是否符合过滤串规则 |
| | | * |
| | | * @param filter 过滤IP列表,支持后缀'*'通配,支持网段如:`10.10.10.1-10.10.10.99` |
| | | * @param ip 校验IP地址 |
| | | * @return boolean 结果 |
| | | */ |
| | | public static boolean isMatchedIp(String filter, String ip) |
| | | { |
| | | if (StringUtils.isEmpty(filter) || StringUtils.isEmpty(ip)) |
| | | { |
| | | return false; |
| | | } |
| | | String[] ips = filter.split(";"); |
| | | for (String iStr : ips) |
| | | { |
| | | if (isIP(iStr) && iStr.equals(ip)) |
| | | { |
| | | return true; |
| | | } |
| | | else if (isIpWildCard(iStr) && ipIsInWildCardNoCheck(iStr, ip)) |
| | | { |
| | | return true; |
| | | } |
| | | else if (isIPSegment(iStr) && ipIsInNetNoCheck(iStr, ip)) |
| | | { |
| | | return true; |
| | | } |
| | | } |
| | | return false; |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.utils.poi; |
| | | |
| | | import org.apache.poi.ss.usermodel.Cell; |
| | | import org.apache.poi.ss.usermodel.Workbook; |
| | | |
| | | /** |
| | | * Excel数据格式处理适配器 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public interface ExcelHandlerAdapter |
| | | { |
| | | /** |
| | | * 格式化 |
| | | * |
| | | * @param value 单元格数据值 |
| | | * @param args excel注解args参数组 |
| | | * @param cell 单元格对象 |
| | | * @param wb 工作簿对象 |
| | | * |
| | | * @return 处理后的值 |
| | | */ |
| | | Object format(Object value, String[] args, Cell cell, Workbook wb); |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.utils.poi; |
| | | |
| | | import com.ruoyi.common.core.annotation.Excel; |
| | | import com.ruoyi.common.core.annotation.Excel.ColumnType; |
| | | import com.ruoyi.common.core.annotation.Excel.Type; |
| | | import com.ruoyi.common.core.annotation.Excels; |
| | | import com.ruoyi.common.core.exception.UtilException; |
| | | import com.ruoyi.common.core.text.Convert; |
| | | import com.ruoyi.common.core.utils.DateUtils; |
| | | import com.ruoyi.common.core.utils.StringUtils; |
| | | import com.ruoyi.common.core.utils.file.FileTypeUtils; |
| | | import com.ruoyi.common.core.utils.file.ImageUtils; |
| | | import com.ruoyi.common.core.utils.reflect.ReflectUtils; |
| | | import org.apache.commons.lang3.ArrayUtils; |
| | | import org.apache.commons.lang3.RegExUtils; |
| | | import org.apache.commons.lang3.reflect.FieldUtils; |
| | | import org.apache.poi.ss.usermodel.*; |
| | | import org.apache.poi.ss.util.CellRangeAddress; |
| | | import org.apache.poi.ss.util.CellRangeAddressList; |
| | | import org.apache.poi.util.IOUtils; |
| | | import org.apache.poi.xssf.streaming.SXSSFWorkbook; |
| | | import org.apache.poi.xssf.usermodel.XSSFClientAnchor; |
| | | import org.apache.poi.xssf.usermodel.XSSFDataValidation; |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | |
| | | import javax.servlet.http.HttpServletResponse; |
| | | import java.io.IOException; |
| | | import java.io.InputStream; |
| | | import java.lang.reflect.Field; |
| | | import java.lang.reflect.Method; |
| | | import java.lang.reflect.ParameterizedType; |
| | | import java.math.BigDecimal; |
| | | import java.text.DecimalFormat; |
| | | import java.time.LocalDate; |
| | | import java.time.LocalDateTime; |
| | | import java.util.*; |
| | | import java.util.stream.Collectors; |
| | | |
| | | /** |
| | | * Excel相关处理 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class ExcelUtil<T> { |
| | | private static final Logger log = LoggerFactory.getLogger(ExcelUtil.class); |
| | | |
| | | public static final String FORMULA_REGEX_STR = "=|-|\\+|@"; |
| | | |
| | | public static final String[] FORMULA_STR = {"=", "-", "+", "@"}; |
| | | |
| | | /** |
| | | * Excel sheet最大行数,默认65536 |
| | | */ |
| | | public static final int sheetSize = 65536; |
| | | |
| | | /** |
| | | * 工作表名称 |
| | | */ |
| | | private String sheetName; |
| | | |
| | | /** |
| | | * 导出类型(EXPORT:导出数据;IMPORT:导入模板) |
| | | */ |
| | | private Type type; |
| | | |
| | | /** |
| | | * 工作薄对象 |
| | | */ |
| | | private Workbook wb; |
| | | |
| | | /** |
| | | * 工作表对象 |
| | | */ |
| | | private Sheet sheet; |
| | | |
| | | /** |
| | | * 样式列表 |
| | | */ |
| | | private Map<String, CellStyle> styles; |
| | | |
| | | /** |
| | | * 导入导出数据列表 |
| | | */ |
| | | private List<T> list; |
| | | |
| | | /** |
| | | * 注解列表 |
| | | */ |
| | | private List<Object[]> fields; |
| | | |
| | | /** |
| | | * 当前行号 |
| | | */ |
| | | private int rownum; |
| | | |
| | | /** |
| | | * 标题 |
| | | */ |
| | | private String title; |
| | | |
| | | /** |
| | | * 最大高度 |
| | | */ |
| | | private short maxHeight; |
| | | |
| | | /** |
| | | * 合并后最后行数 |
| | | */ |
| | | private int subMergedLastRowNum = 0; |
| | | |
| | | /** |
| | | * 合并后开始行数 |
| | | */ |
| | | private int subMergedFirstRowNum = 1; |
| | | |
| | | /** |
| | | * 对象的子列表方法 |
| | | */ |
| | | private Method subMethod; |
| | | |
| | | /** |
| | | * 对象的子列表属性 |
| | | */ |
| | | private List<Field> subFields; |
| | | |
| | | /** |
| | | * 统计列表 |
| | | */ |
| | | private Map<Integer, Double> statistics = new HashMap<Integer, Double>(); |
| | | |
| | | /** |
| | | * 数字格式 |
| | | */ |
| | | private static final DecimalFormat DOUBLE_FORMAT = new DecimalFormat("######0.00"); |
| | | |
| | | /** |
| | | * 实体对象 |
| | | */ |
| | | public Class<T> clazz; |
| | | |
| | | /** |
| | | * 需要排除列属性 |
| | | */ |
| | | public String[] excludeFields; |
| | | |
| | | public ExcelUtil(Class<T> clazz) { |
| | | this.clazz = clazz; |
| | | } |
| | | |
| | | /** |
| | | * 隐藏Excel中列属性 |
| | | * |
| | | * @param fields 列属性名 示例[单个"name"/多个"id","name"] |
| | | * @throws Exception |
| | | */ |
| | | public void hideColumn(String... fields) { |
| | | this.excludeFields = fields; |
| | | } |
| | | |
| | | public void init(List<T> list, String sheetName, String title, Type type) { |
| | | if (list == null) { |
| | | list = new ArrayList<T>(); |
| | | } |
| | | this.list = list; |
| | | this.sheetName = sheetName; |
| | | this.type = type; |
| | | this.title = title; |
| | | createExcelField(); |
| | | createWorkbook(); |
| | | createTitle(); |
| | | createSubHead(); |
| | | } |
| | | |
| | | /** |
| | | * 创建excel第一行标题 |
| | | */ |
| | | public void createTitle() { |
| | | if (StringUtils.isNotEmpty(title)) { |
| | | subMergedFirstRowNum++; |
| | | subMergedLastRowNum++; |
| | | int titleLastCol = this.fields.size() - 1; |
| | | if (isSubList()) { |
| | | titleLastCol = titleLastCol + subFields.size() - 1; |
| | | } |
| | | Row titleRow = sheet.createRow(rownum == 0 ? rownum++ : 0); |
| | | titleRow.setHeightInPoints(30); |
| | | Cell titleCell = titleRow.createCell(0); |
| | | titleCell.setCellStyle(styles.get("title")); |
| | | titleCell.setCellValue(title); |
| | | sheet.addMergedRegion(new CellRangeAddress(titleRow.getRowNum(), titleRow.getRowNum(), titleRow.getRowNum(), titleLastCol)); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 创建对象的子列表名称 |
| | | */ |
| | | public void createSubHead() { |
| | | if (isSubList()) { |
| | | subMergedFirstRowNum++; |
| | | subMergedLastRowNum++; |
| | | Row subRow = sheet.createRow(rownum); |
| | | int excelNum = 0; |
| | | for (Object[] objects : fields) { |
| | | Excel attr = (Excel) objects[1]; |
| | | Cell headCell1 = subRow.createCell(excelNum); |
| | | headCell1.setCellValue(attr.name()); |
| | | headCell1.setCellStyle(styles.get(StringUtils.format("header_{}_{}", attr.headerColor(), attr.headerBackgroundColor()))); |
| | | excelNum++; |
| | | } |
| | | int headFirstRow = excelNum - 1; |
| | | int headLastRow = headFirstRow + subFields.size() - 1; |
| | | if (headLastRow > headFirstRow) { |
| | | sheet.addMergedRegion(new CellRangeAddress(rownum, rownum, headFirstRow, headLastRow)); |
| | | } |
| | | rownum++; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 对excel表单默认第一个索引名转换成list |
| | | * |
| | | * @param is 输入流 |
| | | * @return 转换后集合 |
| | | */ |
| | | public List<T> importExcel(InputStream is) { |
| | | List<T> list = null; |
| | | try { |
| | | list = importExcel(is, 0); |
| | | } catch (Exception e) { |
| | | log.error("导入Excel异常{}", e.getMessage()); |
| | | throw new UtilException(e.getMessage()); |
| | | } finally { |
| | | IOUtils.closeQuietly(is); |
| | | } |
| | | return list; |
| | | } |
| | | |
| | | /** |
| | | * 对excel表单默认第一个索引名转换成list |
| | | * |
| | | * @param is 输入流 |
| | | * @param titleNum 标题占用行数 |
| | | * @return 转换后集合 |
| | | */ |
| | | public List<T> importExcel(InputStream is, int titleNum) throws Exception { |
| | | return importExcel(StringUtils.EMPTY, is, titleNum); |
| | | } |
| | | |
| | | /** |
| | | * 对excel表单指定表格索引名转换成list |
| | | * |
| | | * @param sheetName 表格索引名 |
| | | * @param titleNum 标题占用行数 |
| | | * @param is 输入流 |
| | | * @return 转换后集合 |
| | | */ |
| | | public List<T> importExcel(String sheetName, InputStream is, int titleNum) throws Exception { |
| | | this.type = Type.IMPORT; |
| | | this.wb = WorkbookFactory.create(is); |
| | | List<T> list = new ArrayList<T>(); |
| | | // 如果指定sheet名,则取指定sheet中的内容 否则默认指向第1个sheet |
| | | Sheet sheet = StringUtils.isNotEmpty(sheetName) ? wb.getSheet(sheetName) : wb.getSheetAt(0); |
| | | if (sheet == null) { |
| | | throw new IOException("文件sheet不存在"); |
| | | } |
| | | |
| | | // 获取最后一个非空行的行下标,比如总行数为n,则返回的为n-1 |
| | | int rows = sheet.getLastRowNum(); |
| | | if (rows > 0) { |
| | | // 定义一个map用于存放excel列的序号和field. |
| | | Map<String, Integer> cellMap = new HashMap<String, Integer>(); |
| | | // 获取表头 |
| | | Row heard = sheet.getRow(titleNum); |
| | | for (int i = 0; i < heard.getPhysicalNumberOfCells(); i++) { |
| | | Cell cell = heard.getCell(i); |
| | | if (StringUtils.isNotNull(cell)) { |
| | | String value = this.getCellValue(heard, i).toString(); |
| | | cellMap.put(value, i); |
| | | } else { |
| | | cellMap.put(null, i); |
| | | } |
| | | } |
| | | // 有数据时才处理 得到类的所有field. |
| | | List<Object[]> fields = this.getFields(); |
| | | Map<Integer, Object[]> fieldsMap = new HashMap<Integer, Object[]>(); |
| | | for (Object[] objects : fields) { |
| | | Excel attr = (Excel) objects[1]; |
| | | Integer column = cellMap.get(attr.name()); |
| | | if (column != null) { |
| | | fieldsMap.put(column, objects); |
| | | } |
| | | } |
| | | for (int i = titleNum + 1; i <= rows; i++) { |
| | | // 从第2行开始取数据,默认第一行是表头. |
| | | Row row = sheet.getRow(i); |
| | | // 判断当前行是否是空行 |
| | | if (isRowEmpty(row)) { |
| | | continue; |
| | | } |
| | | T entity = null; |
| | | for (Map.Entry<Integer, Object[]> entry : fieldsMap.entrySet()) { |
| | | Object val = this.getCellValue(row, entry.getKey()); |
| | | |
| | | // 如果不存在实例则新建. |
| | | entity = (entity == null ? clazz.newInstance() : entity); |
| | | // 从map中得到对应列的field. |
| | | Field field = (Field) entry.getValue()[0]; |
| | | Excel attr = (Excel) entry.getValue()[1]; |
| | | // 取得类型,并根据对象类型设置值. |
| | | Class<?> fieldType = field.getType(); |
| | | if (String.class == fieldType) { |
| | | String s = Convert.toStr(val); |
| | | if (StringUtils.endsWith(s, ".0")) { |
| | | val = StringUtils.substringBefore(s, ".0"); |
| | | } else { |
| | | String dateFormat = field.getAnnotation(Excel.class).dateFormat(); |
| | | if (StringUtils.isNotEmpty(dateFormat)) { |
| | | val = parseDateToStr(dateFormat, val); |
| | | } else { |
| | | val = Convert.toStr(val); |
| | | } |
| | | } |
| | | } else if ((Integer.TYPE == fieldType || Integer.class == fieldType) && StringUtils.isNumeric(Convert.toStr(val))) { |
| | | val = Convert.toInt(val); |
| | | } else if ((Long.TYPE == fieldType || Long.class == fieldType) && StringUtils.isNumeric(Convert.toStr(val))) { |
| | | val = Convert.toLong(val); |
| | | } else if (Double.TYPE == fieldType || Double.class == fieldType) { |
| | | val = Convert.toDouble(val); |
| | | } else if (Float.TYPE == fieldType || Float.class == fieldType) { |
| | | val = Convert.toFloat(val); |
| | | } else if (BigDecimal.class == fieldType) { |
| | | val = Convert.toBigDecimal(val); |
| | | } else if (Date.class == fieldType) { |
| | | if (val instanceof String) { |
| | | val = DateUtils.parseDate(val); |
| | | } else if (val instanceof Double) { |
| | | val = DateUtil.getJavaDate((Double) val); |
| | | } |
| | | } else if (Boolean.TYPE == fieldType || Boolean.class == fieldType) { |
| | | val = Convert.toBool(val, false); |
| | | } |
| | | if (StringUtils.isNotNull(fieldType)) { |
| | | String propertyName = field.getName(); |
| | | if (StringUtils.isNotEmpty(attr.targetAttr())) { |
| | | propertyName = field.getName() + "." + attr.targetAttr(); |
| | | } |
| | | if (StringUtils.isNotEmpty(attr.readConverterExp())) { |
| | | val = reverseByExp(Convert.toStr(val), attr.readConverterExp(), attr.separator()); |
| | | } else if (!attr.handler().equals(ExcelHandlerAdapter.class)) { |
| | | val = dataFormatHandlerAdapter(val, attr, null); |
| | | } |
| | | ReflectUtils.invokeSetter(entity, propertyName, val); |
| | | } |
| | | } |
| | | list.add(entity); |
| | | } |
| | | } |
| | | return list; |
| | | } |
| | | |
| | | /** |
| | | * 对list数据源将其里面的数据导入到excel表单 |
| | | * |
| | | * @param response 返回数据 |
| | | * @param list 导出数据集合 |
| | | * @param sheetName 工作表的名称 |
| | | * @return 结果 |
| | | */ |
| | | public void exportExcel(HttpServletResponse response, List<T> list, String sheetName) { |
| | | exportExcel(response, list, sheetName, StringUtils.EMPTY); |
| | | } |
| | | |
| | | /** |
| | | * 对list数据源将其里面的数据导入到excel表单 |
| | | * |
| | | * @param response 返回数据 |
| | | * @param list 导出数据集合 |
| | | * @param sheetName 工作表的名称 |
| | | * @param title 标题 |
| | | * @return 结果 |
| | | */ |
| | | public void exportExcel(HttpServletResponse response, List<T> list, String sheetName, String title) { |
| | | response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); |
| | | response.setCharacterEncoding("utf-8"); |
| | | this.init(list, sheetName, title, Type.EXPORT); |
| | | exportExcel(response); |
| | | } |
| | | |
| | | /** |
| | | * 对list数据源将其里面的数据导入到excel表单 |
| | | * |
| | | * @param sheetName 工作表的名称 |
| | | * @return 结果 |
| | | */ |
| | | public void importTemplateExcel(HttpServletResponse response, String sheetName) { |
| | | importTemplateExcel(response, sheetName, StringUtils.EMPTY); |
| | | } |
| | | |
| | | /** |
| | | * 对list数据源将其里面的数据导入到excel表单 |
| | | * |
| | | * @param sheetName 工作表的名称 |
| | | * @param title 标题 |
| | | * @return 结果 |
| | | */ |
| | | public void importTemplateExcel(HttpServletResponse response, String sheetName, String title) { |
| | | response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); |
| | | response.setCharacterEncoding("utf-8"); |
| | | this.init(null, sheetName, title, Type.IMPORT); |
| | | exportExcel(response); |
| | | } |
| | | |
| | | /** |
| | | * 对list数据源将其里面的数据导入到excel表单 |
| | | * |
| | | * @return 结果 |
| | | */ |
| | | public void exportExcel(HttpServletResponse response) { |
| | | try { |
| | | writeSheet(); |
| | | wb.write(response.getOutputStream()); |
| | | } catch (Exception e) { |
| | | log.error("导出Excel异常{}", e.getMessage()); |
| | | } finally { |
| | | IOUtils.closeQuietly(wb); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 创建写入数据到Sheet |
| | | */ |
| | | public void writeSheet() { |
| | | // 取出一共有多少个sheet. |
| | | int sheetNo = Math.max(1, (int) Math.ceil(list.size() * 1.0 / sheetSize)); |
| | | for (int index = 0; index < sheetNo; index++) { |
| | | createSheet(sheetNo, index); |
| | | |
| | | // 产生一行 |
| | | Row row = sheet.createRow(rownum); |
| | | int column = 0; |
| | | // 写入各个字段的列头名称 |
| | | for (Object[] os : fields) { |
| | | Field field = (Field) os[0]; |
| | | Excel excel = (Excel) os[1]; |
| | | if (Collection.class.isAssignableFrom(field.getType())) { |
| | | for (Field subField : subFields) { |
| | | Excel subExcel = subField.getAnnotation(Excel.class); |
| | | this.createHeadCell(subExcel, row, column++); |
| | | } |
| | | } else { |
| | | this.createHeadCell(excel, row, column++); |
| | | } |
| | | } |
| | | if (Type.EXPORT.equals(type)) { |
| | | fillExcelData(index, row); |
| | | addStatisticsRow(); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 填充excel数据 |
| | | * |
| | | * @param index 序号 |
| | | * @param row 单元格行 |
| | | */ |
| | | @SuppressWarnings("unchecked") |
| | | public void fillExcelData(int index, Row row) { |
| | | int startNo = index * sheetSize; |
| | | int endNo = Math.min(startNo + sheetSize, list.size()); |
| | | int rowNo = (1 + rownum) - startNo; |
| | | for (int i = startNo; i < endNo; i++) { |
| | | rowNo = isSubList() ? (i > 1 ? rowNo + 1 : rowNo + i) : i + 1 + rownum - startNo; |
| | | row = sheet.createRow(rowNo); |
| | | // 得到导出对象. |
| | | T vo = (T) list.get(i); |
| | | Collection<?> subList = null; |
| | | if (isSubList()) { |
| | | if (isSubListValue(vo)) { |
| | | subList = getListCellValue(vo); |
| | | subMergedLastRowNum = subMergedLastRowNum + subList.size(); |
| | | } else { |
| | | subMergedFirstRowNum++; |
| | | subMergedLastRowNum++; |
| | | } |
| | | } |
| | | int column = 0; |
| | | for (Object[] os : fields) { |
| | | Field field = (Field) os[0]; |
| | | Excel excel = (Excel) os[1]; |
| | | if (Collection.class.isAssignableFrom(field.getType()) && StringUtils.isNotNull(subList)) { |
| | | boolean subFirst = false; |
| | | for (Object obj : subList) { |
| | | if (subFirst) { |
| | | rowNo++; |
| | | row = sheet.createRow(rowNo); |
| | | } |
| | | List<Field> subFields = FieldUtils.getFieldsListWithAnnotation(obj.getClass(), Excel.class); |
| | | int subIndex = 0; |
| | | for (Field subField : subFields) { |
| | | if (subField.isAnnotationPresent(Excel.class)) { |
| | | subField.setAccessible(true); |
| | | Excel attr = subField.getAnnotation(Excel.class); |
| | | this.addCell(attr, row, (T) obj, subField, column + subIndex); |
| | | } |
| | | subIndex++; |
| | | } |
| | | subFirst = true; |
| | | } |
| | | this.subMergedFirstRowNum = this.subMergedFirstRowNum + subList.size(); |
| | | } else { |
| | | this.addCell(excel, row, vo, field, column++); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 创建表格样式 |
| | | * |
| | | * @param wb 工作薄对象 |
| | | * @return 样式列表 |
| | | */ |
| | | private Map<String, CellStyle> createStyles(Workbook wb) { |
| | | // 写入各条记录,每条记录对应excel表中的一行 |
| | | Map<String, CellStyle> styles = new HashMap<String, CellStyle>(); |
| | | CellStyle style = wb.createCellStyle(); |
| | | style.setAlignment(HorizontalAlignment.CENTER); |
| | | style.setVerticalAlignment(VerticalAlignment.CENTER); |
| | | Font titleFont = wb.createFont(); |
| | | titleFont.setFontName("Arial"); |
| | | titleFont.setFontHeightInPoints((short) 16); |
| | | titleFont.setBold(true); |
| | | style.setFont(titleFont); |
| | | DataFormat dataFormat = wb.createDataFormat(); |
| | | style.setDataFormat(dataFormat.getFormat("@")); |
| | | styles.put("title", style); |
| | | |
| | | style = wb.createCellStyle(); |
| | | style.setAlignment(HorizontalAlignment.CENTER); |
| | | style.setVerticalAlignment(VerticalAlignment.CENTER); |
| | | style.setBorderRight(BorderStyle.THIN); |
| | | style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); |
| | | style.setBorderLeft(BorderStyle.THIN); |
| | | style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); |
| | | style.setBorderTop(BorderStyle.THIN); |
| | | style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); |
| | | style.setBorderBottom(BorderStyle.THIN); |
| | | style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); |
| | | Font dataFont = wb.createFont(); |
| | | dataFont.setFontName("Arial"); |
| | | dataFont.setFontHeightInPoints((short) 10); |
| | | style.setFont(dataFont); |
| | | styles.put("data", style); |
| | | |
| | | style = wb.createCellStyle(); |
| | | style.setAlignment(HorizontalAlignment.CENTER); |
| | | style.setVerticalAlignment(VerticalAlignment.CENTER); |
| | | Font totalFont = wb.createFont(); |
| | | totalFont.setFontName("Arial"); |
| | | totalFont.setFontHeightInPoints((short) 10); |
| | | style.setFont(totalFont); |
| | | styles.put("total", style); |
| | | |
| | | styles.putAll(annotationHeaderStyles(wb, styles)); |
| | | |
| | | styles.putAll(annotationDataStyles(wb)); |
| | | |
| | | return styles; |
| | | } |
| | | |
| | | /** |
| | | * 根据Excel注解创建表格头样式 |
| | | * |
| | | * @param wb 工作薄对象 |
| | | * @return 自定义样式列表 |
| | | */ |
| | | private Map<String, CellStyle> annotationHeaderStyles(Workbook wb, Map<String, CellStyle> styles) { |
| | | Map<String, CellStyle> headerStyles = new HashMap<String, CellStyle>(); |
| | | for (Object[] os : fields) { |
| | | Excel excel = (Excel) os[1]; |
| | | String key = StringUtils.format("header_{}_{}", excel.headerColor(), excel.headerBackgroundColor()); |
| | | if (!headerStyles.containsKey(key)) { |
| | | CellStyle style = wb.createCellStyle(); |
| | | style.cloneStyleFrom(styles.get("data")); |
| | | style.setAlignment(HorizontalAlignment.CENTER); |
| | | style.setVerticalAlignment(VerticalAlignment.CENTER); |
| | | style.setFillForegroundColor(excel.headerBackgroundColor().index); |
| | | style.setFillPattern(FillPatternType.SOLID_FOREGROUND); |
| | | Font headerFont = wb.createFont(); |
| | | headerFont.setFontName("Arial"); |
| | | headerFont.setFontHeightInPoints((short) 10); |
| | | headerFont.setBold(true); |
| | | headerFont.setColor(excel.headerColor().index); |
| | | style.setFont(headerFont); |
| | | // 设置表格头单元格文本形式 |
| | | DataFormat dataFormat = wb.createDataFormat(); |
| | | style.setDataFormat(dataFormat.getFormat("@")); |
| | | headerStyles.put(key, style); |
| | | } |
| | | } |
| | | return headerStyles; |
| | | } |
| | | |
| | | /** |
| | | * 根据Excel注解创建表格列样式 |
| | | * |
| | | * @param wb 工作薄对象 |
| | | * @return 自定义样式列表 |
| | | */ |
| | | private Map<String, CellStyle> annotationDataStyles(Workbook wb) { |
| | | Map<String, CellStyle> styles = new HashMap<String, CellStyle>(); |
| | | for (Object[] os : fields) { |
| | | Field field = (Field) os[0]; |
| | | Excel excel = (Excel) os[1]; |
| | | if (Collection.class.isAssignableFrom(field.getType())) { |
| | | ParameterizedType pt = (ParameterizedType) field.getGenericType(); |
| | | Class<?> subClass = (Class<?>) pt.getActualTypeArguments()[0]; |
| | | List<Field> subFields = FieldUtils.getFieldsListWithAnnotation(subClass, Excel.class); |
| | | for (Field subField : subFields) { |
| | | Excel subExcel = subField.getAnnotation(Excel.class); |
| | | annotationDataStyles(styles, subField, subExcel); |
| | | } |
| | | } else { |
| | | annotationDataStyles(styles, field, excel); |
| | | } |
| | | } |
| | | return styles; |
| | | } |
| | | |
| | | /** |
| | | * 根据Excel注解创建表格列样式 |
| | | * |
| | | * @param styles 自定义样式列表 |
| | | * @param field 属性列信息 |
| | | * @param excel 注解信息 |
| | | */ |
| | | public void annotationDataStyles(Map<String, CellStyle> styles, Field field, Excel excel) { |
| | | String key = StringUtils.format("data_{}_{}_{}_{}", excel.align(), excel.color(), excel.backgroundColor(), excel.cellType()); |
| | | if (!styles.containsKey(key)) { |
| | | CellStyle style = wb.createCellStyle(); |
| | | style.setAlignment(excel.align()); |
| | | style.setVerticalAlignment(VerticalAlignment.CENTER); |
| | | style.setBorderRight(BorderStyle.THIN); |
| | | style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); |
| | | style.setBorderLeft(BorderStyle.THIN); |
| | | style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); |
| | | style.setBorderTop(BorderStyle.THIN); |
| | | style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); |
| | | style.setBorderBottom(BorderStyle.THIN); |
| | | style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); |
| | | style.setFillPattern(FillPatternType.SOLID_FOREGROUND); |
| | | style.setFillForegroundColor(excel.backgroundColor().getIndex()); |
| | | Font dataFont = wb.createFont(); |
| | | dataFont.setFontName("Arial"); |
| | | dataFont.setFontHeightInPoints((short) 10); |
| | | dataFont.setColor(excel.color().index); |
| | | style.setFont(dataFont); |
| | | if (ColumnType.TEXT == excel.cellType()) { |
| | | DataFormat dataFormat = wb.createDataFormat(); |
| | | style.setDataFormat(dataFormat.getFormat("@")); |
| | | } |
| | | styles.put(key, style); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 创建单元格 |
| | | */ |
| | | public Cell createHeadCell(Excel attr, Row row, int column) { |
| | | // 创建列 |
| | | Cell cell = row.createCell(column); |
| | | // 写入列信息 |
| | | cell.setCellValue(attr.name()); |
| | | setDataValidation(attr, row, column); |
| | | cell.setCellStyle(styles.get(StringUtils.format("header_{}_{}", attr.headerColor(), attr.headerBackgroundColor()))); |
| | | if (isSubList()) { |
| | | // 填充默认样式,防止合并单元格样式失效 |
| | | sheet.setDefaultColumnStyle(column, styles.get(StringUtils.format("data_{}_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor(), attr.cellType()))); |
| | | if (attr.needMerge()) { |
| | | sheet.addMergedRegion(new CellRangeAddress(rownum - 1, rownum, column, column)); |
| | | } |
| | | } |
| | | return cell; |
| | | } |
| | | |
| | | /** |
| | | * 设置单元格信息 |
| | | * |
| | | * @param value 单元格值 |
| | | * @param attr 注解相关 |
| | | * @param cell 单元格信息 |
| | | */ |
| | | public void setCellVo(Object value, Excel attr, Cell cell) { |
| | | if (ColumnType.STRING == attr.cellType() || ColumnType.TEXT == attr.cellType()) { |
| | | String cellValue = Convert.toStr(value); |
| | | // 对于任何以表达式触发字符 =-+@开头的单元格,直接使用tab字符作为前缀,防止CSV注入。 |
| | | if (StringUtils.startsWithAny(cellValue, FORMULA_STR)) { |
| | | cellValue = RegExUtils.replaceFirst(cellValue, FORMULA_REGEX_STR, "\t$0"); |
| | | } |
| | | if (value instanceof Collection && StringUtils.equals("[]", cellValue)) { |
| | | cellValue = StringUtils.EMPTY; |
| | | } |
| | | cell.setCellValue(StringUtils.isNull(cellValue) ? attr.defaultValue() : cellValue + attr.suffix()); |
| | | } else if (ColumnType.NUMERIC == attr.cellType()) { |
| | | if (StringUtils.isNotNull(value)) { |
| | | cell.setCellValue(StringUtils.contains(Convert.toStr(value), ".") ? Convert.toDouble(value) : Convert.toInt(value)); |
| | | } |
| | | } else if (ColumnType.IMAGE == attr.cellType()) { |
| | | ClientAnchor anchor = new XSSFClientAnchor(0, 0, 0, 0, (short) cell.getColumnIndex(), cell.getRow().getRowNum(), (short) (cell.getColumnIndex() + 1), cell.getRow().getRowNum() + 1); |
| | | String imagePath = Convert.toStr(value); |
| | | if (StringUtils.isNotEmpty(imagePath)) { |
| | | byte[] data = ImageUtils.getImage(imagePath); |
| | | getDrawingPatriarch(cell.getSheet()).createPicture(anchor, |
| | | cell.getSheet().getWorkbook().addPicture(data, getImageType(data))); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 获取画布 |
| | | */ |
| | | public static Drawing<?> getDrawingPatriarch(Sheet sheet) { |
| | | if (sheet.getDrawingPatriarch() == null) { |
| | | sheet.createDrawingPatriarch(); |
| | | } |
| | | return sheet.getDrawingPatriarch(); |
| | | } |
| | | |
| | | /** |
| | | * 获取图片类型,设置图片插入类型 |
| | | */ |
| | | public int getImageType(byte[] value) { |
| | | String type = FileTypeUtils.getFileExtendName(value); |
| | | if ("JPG".equalsIgnoreCase(type)) { |
| | | return Workbook.PICTURE_TYPE_JPEG; |
| | | } else if ("PNG".equalsIgnoreCase(type)) { |
| | | return Workbook.PICTURE_TYPE_PNG; |
| | | } |
| | | return Workbook.PICTURE_TYPE_JPEG; |
| | | } |
| | | |
| | | /** |
| | | * 创建表格样式 |
| | | */ |
| | | public void setDataValidation(Excel attr, Row row, int column) { |
| | | if (attr.name().indexOf("注:") >= 0) { |
| | | sheet.setColumnWidth(column, 6000); |
| | | } else { |
| | | // 设置列宽 |
| | | sheet.setColumnWidth(column, (int) ((attr.width() + 0.72) * 256)); |
| | | } |
| | | if (StringUtils.isNotEmpty(attr.prompt()) || attr.combo().length > 0) { |
| | | if (attr.combo().length > 15 || StringUtils.join(attr.combo()).length() > 255) { |
| | | // 如果下拉数大于15或字符串长度大于255,则使用一个新sheet存储,避免生成的模板下拉值获取不到 |
| | | setXSSFValidationWithHidden(sheet, attr.combo(), attr.prompt(), 1, 100, column, column); |
| | | } else { |
| | | // 提示信息或只能选择不能输入的列内容. |
| | | setPromptOrValidation(sheet, attr.combo(), attr.prompt(), 1, 100, column, column); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 添加单元格 |
| | | */ |
| | | public Cell addCell(Excel attr, Row row, T vo, Field field, int column) { |
| | | Cell cell = null; |
| | | try { |
| | | // 设置行高 |
| | | row.setHeight(maxHeight); |
| | | // 根据Excel中设置情况决定是否导出,有些情况需要保持为空,希望用户填写这一列. |
| | | if (attr.isExport()) { |
| | | // 创建cell |
| | | cell = row.createCell(column); |
| | | if (isSubListValue(vo) && getListCellValue(vo).size() > 1 && attr.needMerge()) { |
| | | CellRangeAddress cellAddress = new CellRangeAddress(subMergedFirstRowNum, subMergedLastRowNum, column, column); |
| | | sheet.addMergedRegion(cellAddress); |
| | | } |
| | | cell.setCellStyle(styles.get(StringUtils.format("data_{}_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor(), attr.cellType()))); |
| | | |
| | | // 用于读取对象中的属性 |
| | | Object value = getTargetValue(vo, field, attr); |
| | | String dateFormat = attr.dateFormat(); |
| | | String readConverterExp = attr.readConverterExp(); |
| | | String separator = attr.separator(); |
| | | if (StringUtils.isNotEmpty(dateFormat) && StringUtils.isNotNull(value)) { |
| | | cell.setCellValue(parseDateToStr(dateFormat, value)); |
| | | } else if (StringUtils.isNotEmpty(readConverterExp) && StringUtils.isNotNull(value)) { |
| | | cell.setCellValue(convertByExp(Convert.toStr(value), readConverterExp, separator)); |
| | | } else if (value instanceof BigDecimal && -1 != attr.scale()) { |
| | | cell.setCellValue((((BigDecimal) value).setScale(attr.scale(), attr.roundingMode())).doubleValue()); |
| | | } else if (!attr.handler().equals(ExcelHandlerAdapter.class)) { |
| | | cell.setCellValue(dataFormatHandlerAdapter(value, attr, cell)); |
| | | } else { |
| | | // 设置列类型 |
| | | setCellVo(value, attr, cell); |
| | | } |
| | | addStatisticsData(column, Convert.toStr(value), attr); |
| | | } |
| | | } catch (Exception e) { |
| | | log.error("导出Excel失败{}", e); |
| | | } |
| | | return cell; |
| | | } |
| | | |
| | | /** |
| | | * 设置 POI XSSFSheet 单元格提示或选择框 |
| | | * |
| | | * @param sheet 表单 |
| | | * @param textlist 下拉框显示的内容 |
| | | * @param promptContent 提示内容 |
| | | * @param firstRow 开始行 |
| | | * @param endRow 结束行 |
| | | * @param firstCol 开始列 |
| | | * @param endCol 结束列 |
| | | */ |
| | | public void setPromptOrValidation(Sheet sheet, String[] textlist, String promptContent, int firstRow, int endRow, |
| | | int firstCol, int endCol) { |
| | | DataValidationHelper helper = sheet.getDataValidationHelper(); |
| | | DataValidationConstraint constraint = textlist.length > 0 ? helper.createExplicitListConstraint(textlist) : helper.createCustomConstraint("DD1"); |
| | | CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol); |
| | | DataValidation dataValidation = helper.createValidation(constraint, regions); |
| | | if (StringUtils.isNotEmpty(promptContent)) { |
| | | // 如果设置了提示信息则鼠标放上去提示 |
| | | dataValidation.createPromptBox("", promptContent); |
| | | dataValidation.setShowPromptBox(true); |
| | | } |
| | | // 处理Excel兼容性问题 |
| | | if (dataValidation instanceof XSSFDataValidation) { |
| | | dataValidation.setSuppressDropDownArrow(true); |
| | | dataValidation.setShowErrorBox(true); |
| | | } else { |
| | | dataValidation.setSuppressDropDownArrow(false); |
| | | } |
| | | sheet.addValidationData(dataValidation); |
| | | } |
| | | |
| | | /** |
| | | * 设置某些列的值只能输入预制的数据,显示下拉框(兼容超出一定数量的下拉框). |
| | | * |
| | | * @param sheet 要设置的sheet. |
| | | * @param textlist 下拉框显示的内容 |
| | | * @param promptContent 提示内容 |
| | | * @param firstRow 开始行 |
| | | * @param endRow 结束行 |
| | | * @param firstCol 开始列 |
| | | * @param endCol 结束列 |
| | | */ |
| | | public void setXSSFValidationWithHidden(Sheet sheet, String[] textlist, String promptContent, int firstRow, int endRow, int firstCol, int endCol) { |
| | | String hideSheetName = "combo_" + firstCol + "_" + endCol; |
| | | Sheet hideSheet = wb.createSheet(hideSheetName); // 用于存储 下拉菜单数据 |
| | | for (int i = 0; i < textlist.length; i++) { |
| | | hideSheet.createRow(i).createCell(0).setCellValue(textlist[i]); |
| | | } |
| | | // 创建名称,可被其他单元格引用 |
| | | Name name = wb.createName(); |
| | | name.setNameName(hideSheetName + "_data"); |
| | | name.setRefersToFormula(hideSheetName + "!$A$1:$A$" + textlist.length); |
| | | DataValidationHelper helper = sheet.getDataValidationHelper(); |
| | | // 加载下拉列表内容 |
| | | DataValidationConstraint constraint = helper.createFormulaListConstraint(hideSheetName + "_data"); |
| | | // 设置数据有效性加载在哪个单元格上,四个参数分别是:起始行、终止行、起始列、终止列 |
| | | CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol); |
| | | // 数据有效性对象 |
| | | DataValidation dataValidation = helper.createValidation(constraint, regions); |
| | | if (StringUtils.isNotEmpty(promptContent)) { |
| | | // 如果设置了提示信息则鼠标放上去提示 |
| | | dataValidation.createPromptBox("", promptContent); |
| | | dataValidation.setShowPromptBox(true); |
| | | } |
| | | // 处理Excel兼容性问题 |
| | | if (dataValidation instanceof XSSFDataValidation) { |
| | | dataValidation.setSuppressDropDownArrow(true); |
| | | dataValidation.setShowErrorBox(true); |
| | | } else { |
| | | dataValidation.setSuppressDropDownArrow(false); |
| | | } |
| | | |
| | | sheet.addValidationData(dataValidation); |
| | | // 设置hiddenSheet隐藏 |
| | | wb.setSheetHidden(wb.getSheetIndex(hideSheet), true); |
| | | } |
| | | |
| | | /** |
| | | * 解析导出值 0=男,1=女,2=未知 |
| | | * |
| | | * @param propertyValue 参数值 |
| | | * @param converterExp 翻译注解 |
| | | * @param separator 分隔符 |
| | | * @return 解析后值 |
| | | */ |
| | | public static String convertByExp(String propertyValue, String converterExp, String separator) { |
| | | StringBuilder propertyString = new StringBuilder(); |
| | | String[] convertSource = converterExp.split(","); |
| | | for (String item : convertSource) { |
| | | String[] itemArray = item.split("="); |
| | | if (StringUtils.containsAny(propertyValue, separator)) { |
| | | for (String value : propertyValue.split(separator)) { |
| | | if (itemArray[0].equals(value)) { |
| | | propertyString.append(itemArray[1] + separator); |
| | | break; |
| | | } |
| | | } |
| | | } else { |
| | | if (itemArray[0].equals(propertyValue)) { |
| | | return itemArray[1]; |
| | | } |
| | | } |
| | | } |
| | | return StringUtils.stripEnd(propertyString.toString(), separator); |
| | | } |
| | | |
| | | /** |
| | | * 反向解析值 男=0,女=1,未知=2 |
| | | * |
| | | * @param propertyValue 参数值 |
| | | * @param converterExp 翻译注解 |
| | | * @param separator 分隔符 |
| | | * @return 解析后值 |
| | | */ |
| | | public static String reverseByExp(String propertyValue, String converterExp, String separator) { |
| | | StringBuilder propertyString = new StringBuilder(); |
| | | String[] convertSource = converterExp.split(","); |
| | | for (String item : convertSource) { |
| | | String[] itemArray = item.split("="); |
| | | if (StringUtils.containsAny(propertyValue, separator)) { |
| | | for (String value : propertyValue.split(separator)) { |
| | | if (itemArray[1].equals(value)) { |
| | | propertyString.append(itemArray[0] + separator); |
| | | break; |
| | | } |
| | | } |
| | | } else { |
| | | if (itemArray[1].equals(propertyValue)) { |
| | | return itemArray[0]; |
| | | } |
| | | } |
| | | } |
| | | return StringUtils.stripEnd(propertyString.toString(), separator); |
| | | } |
| | | |
| | | /** |
| | | * 数据处理器 |
| | | * |
| | | * @param value 数据值 |
| | | * @param excel 数据注解 |
| | | * @return |
| | | */ |
| | | public String dataFormatHandlerAdapter(Object value, Excel excel, Cell cell) { |
| | | try { |
| | | Object instance = excel.handler().newInstance(); |
| | | Method formatMethod = excel.handler().getMethod("format", new Class[]{Object.class, String[].class, Cell.class, Workbook.class}); |
| | | value = formatMethod.invoke(instance, value, excel.args(), cell, this.wb); |
| | | } catch (Exception e) { |
| | | log.error("不能格式化数据 " + excel.handler(), e.getMessage()); |
| | | } |
| | | return Convert.toStr(value); |
| | | } |
| | | |
| | | /** |
| | | * 合计统计信息 |
| | | */ |
| | | private void addStatisticsData(Integer index, String text, Excel entity) { |
| | | if (entity != null && entity.isStatistics()) { |
| | | Double temp = 0D; |
| | | if (!statistics.containsKey(index)) { |
| | | statistics.put(index, temp); |
| | | } |
| | | try { |
| | | temp = Double.valueOf(text); |
| | | } catch (NumberFormatException e) { |
| | | } |
| | | statistics.put(index, statistics.get(index) + temp); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 创建统计行 |
| | | */ |
| | | public void addStatisticsRow() { |
| | | if (statistics.size() > 0) { |
| | | Row row = sheet.createRow(sheet.getLastRowNum() + 1); |
| | | Set<Integer> keys = statistics.keySet(); |
| | | Cell cell = row.createCell(0); |
| | | cell.setCellStyle(styles.get("total")); |
| | | cell.setCellValue("合计"); |
| | | |
| | | for (Integer key : keys) { |
| | | cell = row.createCell(key); |
| | | cell.setCellStyle(styles.get("total")); |
| | | cell.setCellValue(DOUBLE_FORMAT.format(statistics.get(key))); |
| | | } |
| | | statistics.clear(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 获取bean中的属性值 |
| | | * |
| | | * @param vo 实体对象 |
| | | * @param field 字段 |
| | | * @param excel 注解 |
| | | * @return 最终的属性值 |
| | | * @throws Exception |
| | | */ |
| | | private Object getTargetValue(T vo, Field field, Excel excel) throws Exception { |
| | | Object o = field.get(vo); |
| | | if (StringUtils.isNotEmpty(excel.targetAttr())) { |
| | | String target = excel.targetAttr(); |
| | | if (target.contains(".")) { |
| | | String[] targets = target.split("[.]"); |
| | | for (String name : targets) { |
| | | o = getValue(o, name); |
| | | } |
| | | } else { |
| | | o = getValue(o, target); |
| | | } |
| | | } |
| | | return o; |
| | | } |
| | | |
| | | /** |
| | | * 以类的属性的get方法方法形式获取值 |
| | | * |
| | | * @param o |
| | | * @param name |
| | | * @return value |
| | | * @throws Exception |
| | | */ |
| | | private Object getValue(Object o, String name) throws Exception { |
| | | if (StringUtils.isNotNull(o) && StringUtils.isNotEmpty(name)) { |
| | | Class<?> clazz = o.getClass(); |
| | | Field field = clazz.getDeclaredField(name); |
| | | field.setAccessible(true); |
| | | o = field.get(o); |
| | | } |
| | | return o; |
| | | } |
| | | |
| | | /** |
| | | * 得到所有定义字段 |
| | | */ |
| | | private void createExcelField() { |
| | | this.fields = getFields(); |
| | | this.fields = this.fields.stream().sorted(Comparator.comparing(objects -> ((Excel) objects[1]).sort())).collect(Collectors.toList()); |
| | | this.maxHeight = getRowHeight(); |
| | | } |
| | | |
| | | /** |
| | | * 获取字段注解信息 |
| | | */ |
| | | public List<Object[]> getFields() { |
| | | List<Object[]> fields = new ArrayList<Object[]>(); |
| | | List<Field> tempFields = new ArrayList<>(); |
| | | tempFields.addAll(Arrays.asList(clazz.getSuperclass().getDeclaredFields())); |
| | | tempFields.addAll(Arrays.asList(clazz.getDeclaredFields())); |
| | | for (Field field : tempFields) { |
| | | if (!ArrayUtils.contains(this.excludeFields, field.getName())) { |
| | | // 单注解 |
| | | if (field.isAnnotationPresent(Excel.class)) { |
| | | Excel attr = field.getAnnotation(Excel.class); |
| | | if (attr != null && (attr.type() == Type.ALL || attr.type() == type)) { |
| | | field.setAccessible(true); |
| | | fields.add(new Object[]{field, attr}); |
| | | } |
| | | if (Collection.class.isAssignableFrom(field.getType())) { |
| | | subMethod = getSubMethod(field.getName(), clazz); |
| | | ParameterizedType pt = (ParameterizedType) field.getGenericType(); |
| | | Class<?> subClass = (Class<?>) pt.getActualTypeArguments()[0]; |
| | | this.subFields = FieldUtils.getFieldsListWithAnnotation(subClass, Excel.class); |
| | | } |
| | | } |
| | | |
| | | // 多注解 |
| | | if (field.isAnnotationPresent(Excels.class)) { |
| | | Excels attrs = field.getAnnotation(Excels.class); |
| | | Excel[] excels = attrs.value(); |
| | | for (Excel attr : excels) { |
| | | if (!ArrayUtils.contains(this.excludeFields, field.getName() + "." + attr.targetAttr()) |
| | | && (attr != null && (attr.type() == Type.ALL || attr.type() == type))) { |
| | | field.setAccessible(true); |
| | | fields.add(new Object[]{field, attr}); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | return fields; |
| | | } |
| | | |
| | | /** |
| | | * 根据注解获取最大行高 |
| | | */ |
| | | public short getRowHeight() { |
| | | double maxHeight = 0; |
| | | for (Object[] os : this.fields) { |
| | | Excel excel = (Excel) os[1]; |
| | | maxHeight = Math.max(maxHeight, excel.height()); |
| | | } |
| | | return (short) (maxHeight * 20); |
| | | } |
| | | |
| | | /** |
| | | * 创建一个工作簿 |
| | | */ |
| | | public void createWorkbook() { |
| | | this.wb = new SXSSFWorkbook(500); |
| | | this.sheet = wb.createSheet(); |
| | | wb.setSheetName(0, sheetName); |
| | | this.styles = createStyles(wb); |
| | | } |
| | | |
| | | /** |
| | | * 创建工作表 |
| | | * |
| | | * @param sheetNo sheet数量 |
| | | * @param index 序号 |
| | | */ |
| | | public void createSheet(int sheetNo, int index) { |
| | | // 设置工作表的名称. |
| | | if (sheetNo > 1 && index > 0) { |
| | | this.sheet = wb.createSheet(); |
| | | this.createTitle(); |
| | | wb.setSheetName(index, sheetName + index); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 获取单元格值 |
| | | * |
| | | * @param row 获取的行 |
| | | * @param column 获取单元格列号 |
| | | * @return 单元格值 |
| | | */ |
| | | public Object getCellValue(Row row, int column) { |
| | | if (row == null) { |
| | | return row; |
| | | } |
| | | Object val = ""; |
| | | try { |
| | | Cell cell = row.getCell(column); |
| | | if (StringUtils.isNotNull(cell)) { |
| | | if (cell.getCellType() == CellType.NUMERIC || cell.getCellType() == CellType.FORMULA) { |
| | | val = cell.getNumericCellValue(); |
| | | if (DateUtil.isCellDateFormatted(cell)) { |
| | | val = DateUtil.getJavaDate((Double) val); // POI Excel 日期格式转换 |
| | | } else { |
| | | if ((Double) val % 1 != 0) { |
| | | val = new BigDecimal(val.toString()); |
| | | } else { |
| | | val = new DecimalFormat("0").format(val); |
| | | } |
| | | } |
| | | } else if (cell.getCellType() == CellType.STRING) { |
| | | val = cell.getStringCellValue(); |
| | | } else if (cell.getCellType() == CellType.BOOLEAN) { |
| | | val = cell.getBooleanCellValue(); |
| | | } else if (cell.getCellType() == CellType.ERROR) { |
| | | val = cell.getErrorCellValue(); |
| | | } |
| | | |
| | | } |
| | | } catch (Exception e) { |
| | | return val; |
| | | } |
| | | return val; |
| | | } |
| | | |
| | | /** |
| | | * 判断是否是空行 |
| | | * |
| | | * @param row 判断的行 |
| | | * @return |
| | | */ |
| | | private boolean isRowEmpty(Row row) { |
| | | if (row == null) { |
| | | return true; |
| | | } |
| | | for (int i = row.getFirstCellNum(); i < row.getLastCellNum(); i++) { |
| | | Cell cell = row.getCell(i); |
| | | if (cell != null && cell.getCellType() != CellType.BLANK) { |
| | | return false; |
| | | } |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | /** |
| | | * 格式化不同类型的日期对象 |
| | | * |
| | | * @param dateFormat 日期格式 |
| | | * @param val 被格式化的日期对象 |
| | | * @return 格式化后的日期字符 |
| | | */ |
| | | public String parseDateToStr(String dateFormat, Object val) { |
| | | if (val == null) { |
| | | return ""; |
| | | } |
| | | String str; |
| | | if (val instanceof Date) { |
| | | str = DateUtils.parseDateToStr(dateFormat, (Date) val); |
| | | } else if (val instanceof LocalDateTime) { |
| | | str = DateUtils.parseDateToStr(dateFormat, DateUtils.toDate((LocalDateTime) val)); |
| | | } else if (val instanceof LocalDate) { |
| | | str = DateUtils.parseDateToStr(dateFormat, DateUtils.toDate((LocalDate) val)); |
| | | } else { |
| | | str = val.toString(); |
| | | } |
| | | return str; |
| | | } |
| | | |
| | | /** |
| | | * 是否有对象的子列表 |
| | | */ |
| | | public boolean isSubList() { |
| | | return StringUtils.isNotNull(subFields) && subFields.size() > 0; |
| | | } |
| | | |
| | | /** |
| | | * 是否有对象的子列表,集合不为空 |
| | | */ |
| | | public boolean isSubListValue(T vo) { |
| | | return StringUtils.isNotNull(subFields) && subFields.size() > 0 && StringUtils.isNotNull(getListCellValue(vo)) && getListCellValue(vo).size() > 0; |
| | | } |
| | | |
| | | /** |
| | | * 获取集合的值 |
| | | */ |
| | | public Collection<?> getListCellValue(Object obj) { |
| | | Object value; |
| | | try { |
| | | value = subMethod.invoke(obj, new Object[]{}); |
| | | } catch (Exception e) { |
| | | return new ArrayList<Object>(); |
| | | } |
| | | return (Collection<?>) value; |
| | | } |
| | | |
| | | /** |
| | | * 获取对象的子列表方法 |
| | | * |
| | | * @param name 名称 |
| | | * @param pojoClass 类对象 |
| | | * @return 子列表方法 |
| | | */ |
| | | public Method getSubMethod(String name, Class<?> pojoClass) { |
| | | StringBuffer getMethodName = new StringBuffer("get"); |
| | | getMethodName.append(name.substring(0, 1).toUpperCase()); |
| | | getMethodName.append(name.substring(1)); |
| | | Method method = null; |
| | | try { |
| | | method = pojoClass.getMethod(getMethodName.toString(), new Class[]{}); |
| | | } catch (Exception e) { |
| | | log.error("获取对象异常{}", e.getMessage()); |
| | | } |
| | | return method; |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.utils.reflect; |
| | | |
| | | import java.lang.reflect.Field; |
| | | import java.lang.reflect.InvocationTargetException; |
| | | import java.lang.reflect.Method; |
| | | import java.lang.reflect.Modifier; |
| | | import java.lang.reflect.ParameterizedType; |
| | | import java.lang.reflect.Type; |
| | | import java.util.Date; |
| | | import org.apache.commons.lang3.StringUtils; |
| | | import org.apache.commons.lang3.Validate; |
| | | import org.apache.poi.ss.usermodel.DateUtil; |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | import com.ruoyi.common.core.text.Convert; |
| | | import com.ruoyi.common.core.utils.DateUtils; |
| | | |
| | | /** |
| | | * 反射工具类. 提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class, 被AOP过的真实类等工具函数. |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | @SuppressWarnings("rawtypes") |
| | | public class ReflectUtils |
| | | { |
| | | private static final String SETTER_PREFIX = "set"; |
| | | |
| | | private static final String GETTER_PREFIX = "get"; |
| | | |
| | | private static final String CGLIB_CLASS_SEPARATOR = "$$"; |
| | | |
| | | private static Logger logger = LoggerFactory.getLogger(ReflectUtils.class); |
| | | |
| | | /** |
| | | * 调用Getter方法. |
| | | * 支持多级,如:对象名.对象名.方法 |
| | | */ |
| | | @SuppressWarnings("unchecked") |
| | | public static <E> E invokeGetter(Object obj, String propertyName) |
| | | { |
| | | Object object = obj; |
| | | for (String name : StringUtils.split(propertyName, ".")) |
| | | { |
| | | String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(name); |
| | | object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {}); |
| | | } |
| | | return (E) object; |
| | | } |
| | | |
| | | /** |
| | | * 调用Setter方法, 仅匹配方法名。 |
| | | * 支持多级,如:对象名.对象名.方法 |
| | | */ |
| | | public static <E> void invokeSetter(Object obj, String propertyName, E value) |
| | | { |
| | | Object object = obj; |
| | | String[] names = StringUtils.split(propertyName, "."); |
| | | for (int i = 0; i < names.length; i++) |
| | | { |
| | | if (i < names.length - 1) |
| | | { |
| | | String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(names[i]); |
| | | object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {}); |
| | | } |
| | | else |
| | | { |
| | | String setterMethodName = SETTER_PREFIX + StringUtils.capitalize(names[i]); |
| | | invokeMethodByName(object, setterMethodName, new Object[] { value }); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 直接读取对象属性值, 无视private/protected修饰符, 不经过getter函数. |
| | | */ |
| | | @SuppressWarnings("unchecked") |
| | | public static <E> E getFieldValue(final Object obj, final String fieldName) |
| | | { |
| | | Field field = getAccessibleField(obj, fieldName); |
| | | if (field == null) |
| | | { |
| | | logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 "); |
| | | return null; |
| | | } |
| | | E result = null; |
| | | try |
| | | { |
| | | result = (E) field.get(obj); |
| | | } |
| | | catch (IllegalAccessException e) |
| | | { |
| | | logger.error("不可能抛出的异常{}", e.getMessage()); |
| | | } |
| | | return result; |
| | | } |
| | | |
| | | /** |
| | | * 直接设置对象属性值, 无视private/protected修饰符, 不经过setter函数. |
| | | */ |
| | | public static <E> void setFieldValue(final Object obj, final String fieldName, final E value) |
| | | { |
| | | Field field = getAccessibleField(obj, fieldName); |
| | | if (field == null) |
| | | { |
| | | // throw new IllegalArgumentException("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 "); |
| | | logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 "); |
| | | return; |
| | | } |
| | | try |
| | | { |
| | | field.set(obj, value); |
| | | } |
| | | catch (IllegalAccessException e) |
| | | { |
| | | logger.error("不可能抛出的异常: {}", e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 直接调用对象方法, 无视private/protected修饰符. |
| | | * 用于一次性调用的情况,否则应使用getAccessibleMethod()函数获得Method后反复调用. |
| | | * 同时匹配方法名+参数类型, |
| | | */ |
| | | @SuppressWarnings("unchecked") |
| | | public static <E> E invokeMethod(final Object obj, final String methodName, final Class<?>[] parameterTypes, |
| | | final Object[] args) |
| | | { |
| | | if (obj == null || methodName == null) |
| | | { |
| | | return null; |
| | | } |
| | | Method method = getAccessibleMethod(obj, methodName, parameterTypes); |
| | | if (method == null) |
| | | { |
| | | logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 "); |
| | | return null; |
| | | } |
| | | try |
| | | { |
| | | return (E) method.invoke(obj, args); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | String msg = "method: " + method + ", obj: " + obj + ", args: " + args + ""; |
| | | throw convertReflectionExceptionToUnchecked(msg, e); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 直接调用对象方法, 无视private/protected修饰符, |
| | | * 用于一次性调用的情况,否则应使用getAccessibleMethodByName()函数获得Method后反复调用. |
| | | * 只匹配函数名,如果有多个同名函数调用第一个。 |
| | | */ |
| | | @SuppressWarnings("unchecked") |
| | | public static <E> E invokeMethodByName(final Object obj, final String methodName, final Object[] args) |
| | | { |
| | | Method method = getAccessibleMethodByName(obj, methodName, args.length); |
| | | if (method == null) |
| | | { |
| | | // 如果为空不报错,直接返回空。 |
| | | logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 "); |
| | | return null; |
| | | } |
| | | try |
| | | { |
| | | // 类型转换(将参数数据类型转换为目标方法参数类型) |
| | | Class<?>[] cs = method.getParameterTypes(); |
| | | for (int i = 0; i < cs.length; i++) |
| | | { |
| | | if (args[i] != null && !args[i].getClass().equals(cs[i])) |
| | | { |
| | | if (cs[i] == String.class) |
| | | { |
| | | args[i] = Convert.toStr(args[i]); |
| | | if (StringUtils.endsWith((String) args[i], ".0")) |
| | | { |
| | | args[i] = StringUtils.substringBefore((String) args[i], ".0"); |
| | | } |
| | | } |
| | | else if (cs[i] == Integer.class) |
| | | { |
| | | args[i] = Convert.toInt(args[i]); |
| | | } |
| | | else if (cs[i] == Long.class) |
| | | { |
| | | args[i] = Convert.toLong(args[i]); |
| | | } |
| | | else if (cs[i] == Double.class) |
| | | { |
| | | args[i] = Convert.toDouble(args[i]); |
| | | } |
| | | else if (cs[i] == Float.class) |
| | | { |
| | | args[i] = Convert.toFloat(args[i]); |
| | | } |
| | | else if (cs[i] == Date.class) |
| | | { |
| | | if (args[i] instanceof String) |
| | | { |
| | | args[i] = DateUtils.parseDate(args[i]); |
| | | } |
| | | else |
| | | { |
| | | args[i] = DateUtil.getJavaDate((Double) args[i]); |
| | | } |
| | | } |
| | | else if (cs[i] == boolean.class || cs[i] == Boolean.class) |
| | | { |
| | | args[i] = Convert.toBool(args[i]); |
| | | } |
| | | } |
| | | } |
| | | return (E) method.invoke(obj, args); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | String msg = "method: " + method + ", obj: " + obj + ", args: " + args + ""; |
| | | throw convertReflectionExceptionToUnchecked(msg, e); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 循环向上转型, 获取对象的DeclaredField, 并强制设置为可访问. |
| | | * 如向上转型到Object仍无法找到, 返回null. |
| | | */ |
| | | public static Field getAccessibleField(final Object obj, final String fieldName) |
| | | { |
| | | // 为空不报错。直接返回 null |
| | | if (obj == null) |
| | | { |
| | | return null; |
| | | } |
| | | Validate.notBlank(fieldName, "fieldName can't be blank"); |
| | | for (Class<?> superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) |
| | | { |
| | | try |
| | | { |
| | | Field field = superClass.getDeclaredField(fieldName); |
| | | makeAccessible(field); |
| | | return field; |
| | | } |
| | | catch (NoSuchFieldException e) |
| | | { |
| | | continue; |
| | | } |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | /** |
| | | * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问. |
| | | * 如向上转型到Object仍无法找到, 返回null. |
| | | * 匹配函数名+参数类型。 |
| | | * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args) |
| | | */ |
| | | public static Method getAccessibleMethod(final Object obj, final String methodName, |
| | | final Class<?>... parameterTypes) |
| | | { |
| | | // 为空不报错。直接返回 null |
| | | if (obj == null) |
| | | { |
| | | return null; |
| | | } |
| | | Validate.notBlank(methodName, "methodName can't be blank"); |
| | | for (Class<?> searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) |
| | | { |
| | | try |
| | | { |
| | | Method method = searchType.getDeclaredMethod(methodName, parameterTypes); |
| | | makeAccessible(method); |
| | | return method; |
| | | } |
| | | catch (NoSuchMethodException e) |
| | | { |
| | | continue; |
| | | } |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | /** |
| | | * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问. |
| | | * 如向上转型到Object仍无法找到, 返回null. |
| | | * 只匹配函数名。 |
| | | * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args) |
| | | */ |
| | | public static Method getAccessibleMethodByName(final Object obj, final String methodName, int argsNum) |
| | | { |
| | | // 为空不报错。直接返回 null |
| | | if (obj == null) |
| | | { |
| | | return null; |
| | | } |
| | | Validate.notBlank(methodName, "methodName can't be blank"); |
| | | for (Class<?> searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) |
| | | { |
| | | Method[] methods = searchType.getDeclaredMethods(); |
| | | for (Method method : methods) |
| | | { |
| | | if (method.getName().equals(methodName) && method.getParameterTypes().length == argsNum) |
| | | { |
| | | makeAccessible(method); |
| | | return method; |
| | | } |
| | | } |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | /** |
| | | * 改变private/protected的方法为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。 |
| | | */ |
| | | public static void makeAccessible(Method method) |
| | | { |
| | | if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers())) |
| | | && !method.isAccessible()) |
| | | { |
| | | method.setAccessible(true); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 改变private/protected的成员变量为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。 |
| | | */ |
| | | public static void makeAccessible(Field field) |
| | | { |
| | | if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers()) |
| | | || Modifier.isFinal(field.getModifiers())) && !field.isAccessible()) |
| | | { |
| | | field.setAccessible(true); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 通过反射, 获得Class定义中声明的泛型参数的类型, 注意泛型必须定义在父类处 |
| | | * 如无法找到, 返回Object.class. |
| | | */ |
| | | @SuppressWarnings("unchecked") |
| | | public static <T> Class<T> getClassGenricType(final Class clazz) |
| | | { |
| | | return getClassGenricType(clazz, 0); |
| | | } |
| | | |
| | | /** |
| | | * 通过反射, 获得Class定义中声明的父类的泛型参数的类型. |
| | | * 如无法找到, 返回Object.class. |
| | | */ |
| | | public static Class getClassGenricType(final Class clazz, final int index) |
| | | { |
| | | Type genType = clazz.getGenericSuperclass(); |
| | | |
| | | if (!(genType instanceof ParameterizedType)) |
| | | { |
| | | logger.debug(clazz.getSimpleName() + "'s superclass not ParameterizedType"); |
| | | return Object.class; |
| | | } |
| | | |
| | | Type[] params = ((ParameterizedType) genType).getActualTypeArguments(); |
| | | |
| | | if (index >= params.length || index < 0) |
| | | { |
| | | logger.debug("Index: " + index + ", Size of " + clazz.getSimpleName() + "'s Parameterized Type: " |
| | | + params.length); |
| | | return Object.class; |
| | | } |
| | | if (!(params[index] instanceof Class)) |
| | | { |
| | | logger.debug(clazz.getSimpleName() + " not set the actual class on superclass generic parameter"); |
| | | return Object.class; |
| | | } |
| | | |
| | | return (Class) params[index]; |
| | | } |
| | | |
| | | public static Class<?> getUserClass(Object instance) |
| | | { |
| | | if (instance == null) |
| | | { |
| | | throw new RuntimeException("Instance must not be null"); |
| | | } |
| | | Class clazz = instance.getClass(); |
| | | if (clazz != null && clazz.getName().contains(CGLIB_CLASS_SEPARATOR)) |
| | | { |
| | | Class<?> superClass = clazz.getSuperclass(); |
| | | if (superClass != null && !Object.class.equals(superClass)) |
| | | { |
| | | return superClass; |
| | | } |
| | | } |
| | | return clazz; |
| | | |
| | | } |
| | | |
| | | /** |
| | | * 将反射时的checked exception转换为unchecked exception. |
| | | */ |
| | | public static RuntimeException convertReflectionExceptionToUnchecked(String msg, Exception e) |
| | | { |
| | | if (e instanceof IllegalAccessException || e instanceof IllegalArgumentException |
| | | || e instanceof NoSuchMethodException) |
| | | { |
| | | return new IllegalArgumentException(msg, e); |
| | | } |
| | | else if (e instanceof InvocationTargetException) |
| | | { |
| | | return new RuntimeException(msg, ((InvocationTargetException) e).getTargetException()); |
| | | } |
| | | return new RuntimeException(msg, e); |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.utils.sign; |
| | | |
| | | /** |
| | | * Base64工具类 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public final class Base64 |
| | | { |
| | | static private final int BASELENGTH = 128; |
| | | static private final int LOOKUPLENGTH = 64; |
| | | static private final int TWENTYFOURBITGROUP = 24; |
| | | static private final int EIGHTBIT = 8; |
| | | static private final int SIXTEENBIT = 16; |
| | | static private final int FOURBYTE = 4; |
| | | static private final int SIGN = -128; |
| | | static private final char PAD = '='; |
| | | static final private byte[] base64Alphabet = new byte[BASELENGTH]; |
| | | static final private char[] lookUpBase64Alphabet = new char[LOOKUPLENGTH]; |
| | | |
| | | static |
| | | { |
| | | for (int i = 0; i < BASELENGTH; ++i) |
| | | { |
| | | base64Alphabet[i] = -1; |
| | | } |
| | | for (int i = 'Z'; i >= 'A'; i--) |
| | | { |
| | | base64Alphabet[i] = (byte) (i - 'A'); |
| | | } |
| | | for (int i = 'z'; i >= 'a'; i--) |
| | | { |
| | | base64Alphabet[i] = (byte) (i - 'a' + 26); |
| | | } |
| | | |
| | | for (int i = '9'; i >= '0'; i--) |
| | | { |
| | | base64Alphabet[i] = (byte) (i - '0' + 52); |
| | | } |
| | | |
| | | base64Alphabet['+'] = 62; |
| | | base64Alphabet['/'] = 63; |
| | | |
| | | for (int i = 0; i <= 25; i++) |
| | | { |
| | | lookUpBase64Alphabet[i] = (char) ('A' + i); |
| | | } |
| | | |
| | | for (int i = 26, j = 0; i <= 51; i++, j++) |
| | | { |
| | | lookUpBase64Alphabet[i] = (char) ('a' + j); |
| | | } |
| | | |
| | | for (int i = 52, j = 0; i <= 61; i++, j++) |
| | | { |
| | | lookUpBase64Alphabet[i] = (char) ('0' + j); |
| | | } |
| | | lookUpBase64Alphabet[62] = (char) '+'; |
| | | lookUpBase64Alphabet[63] = (char) '/'; |
| | | } |
| | | |
| | | private static boolean isWhiteSpace(char octect) |
| | | { |
| | | return (octect == 0x20 || octect == 0xd || octect == 0xa || octect == 0x9); |
| | | } |
| | | |
| | | private static boolean isPad(char octect) |
| | | { |
| | | return (octect == PAD); |
| | | } |
| | | |
| | | private static boolean isData(char octect) |
| | | { |
| | | return (octect < BASELENGTH && base64Alphabet[octect] != -1); |
| | | } |
| | | |
| | | /** |
| | | * Encodes hex octects into Base64 |
| | | * |
| | | * @param binaryData Array containing binaryData |
| | | * @return Encoded Base64 array |
| | | */ |
| | | public static String encode(byte[] binaryData) |
| | | { |
| | | if (binaryData == null) |
| | | { |
| | | return null; |
| | | } |
| | | |
| | | int lengthDataBits = binaryData.length * EIGHTBIT; |
| | | if (lengthDataBits == 0) |
| | | { |
| | | return ""; |
| | | } |
| | | |
| | | int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP; |
| | | int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP; |
| | | int numberQuartet = fewerThan24bits != 0 ? numberTriplets + 1 : numberTriplets; |
| | | char encodedData[] = null; |
| | | |
| | | encodedData = new char[numberQuartet * 4]; |
| | | |
| | | byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0; |
| | | |
| | | int encodedIndex = 0; |
| | | int dataIndex = 0; |
| | | |
| | | for (int i = 0; i < numberTriplets; i++) |
| | | { |
| | | b1 = binaryData[dataIndex++]; |
| | | b2 = binaryData[dataIndex++]; |
| | | b3 = binaryData[dataIndex++]; |
| | | |
| | | l = (byte) (b2 & 0x0f); |
| | | k = (byte) (b1 & 0x03); |
| | | |
| | | byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0); |
| | | byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0); |
| | | byte val3 = ((b3 & SIGN) == 0) ? (byte) (b3 >> 6) : (byte) ((b3) >> 6 ^ 0xfc); |
| | | |
| | | encodedData[encodedIndex++] = lookUpBase64Alphabet[val1]; |
| | | encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)]; |
| | | encodedData[encodedIndex++] = lookUpBase64Alphabet[(l << 2) | val3]; |
| | | encodedData[encodedIndex++] = lookUpBase64Alphabet[b3 & 0x3f]; |
| | | } |
| | | |
| | | // form integral number of 6-bit groups |
| | | if (fewerThan24bits == EIGHTBIT) |
| | | { |
| | | b1 = binaryData[dataIndex]; |
| | | k = (byte) (b1 & 0x03); |
| | | byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0); |
| | | encodedData[encodedIndex++] = lookUpBase64Alphabet[val1]; |
| | | encodedData[encodedIndex++] = lookUpBase64Alphabet[k << 4]; |
| | | encodedData[encodedIndex++] = PAD; |
| | | encodedData[encodedIndex++] = PAD; |
| | | } |
| | | else if (fewerThan24bits == SIXTEENBIT) |
| | | { |
| | | b1 = binaryData[dataIndex]; |
| | | b2 = binaryData[dataIndex + 1]; |
| | | l = (byte) (b2 & 0x0f); |
| | | k = (byte) (b1 & 0x03); |
| | | |
| | | byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0); |
| | | byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0); |
| | | |
| | | encodedData[encodedIndex++] = lookUpBase64Alphabet[val1]; |
| | | encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)]; |
| | | encodedData[encodedIndex++] = lookUpBase64Alphabet[l << 2]; |
| | | encodedData[encodedIndex++] = PAD; |
| | | } |
| | | return new String(encodedData); |
| | | } |
| | | |
| | | /** |
| | | * Decodes Base64 data into octects |
| | | * |
| | | * @param encoded string containing Base64 data |
| | | * @return Array containind decoded data. |
| | | */ |
| | | public static byte[] decode(String encoded) |
| | | { |
| | | if (encoded == null) |
| | | { |
| | | return null; |
| | | } |
| | | |
| | | char[] base64Data = encoded.toCharArray(); |
| | | // remove white spaces |
| | | int len = removeWhiteSpace(base64Data); |
| | | |
| | | if (len % FOURBYTE != 0) |
| | | { |
| | | return null;// should be divisible by four |
| | | } |
| | | |
| | | int numberQuadruple = (len / FOURBYTE); |
| | | |
| | | if (numberQuadruple == 0) |
| | | { |
| | | return new byte[0]; |
| | | } |
| | | |
| | | byte decodedData[] = null; |
| | | byte b1 = 0, b2 = 0, b3 = 0, b4 = 0; |
| | | char d1 = 0, d2 = 0, d3 = 0, d4 = 0; |
| | | |
| | | int i = 0; |
| | | int encodedIndex = 0; |
| | | int dataIndex = 0; |
| | | decodedData = new byte[(numberQuadruple) * 3]; |
| | | |
| | | for (; i < numberQuadruple - 1; i++) |
| | | { |
| | | |
| | | if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++])) |
| | | || !isData((d3 = base64Data[dataIndex++])) || !isData((d4 = base64Data[dataIndex++]))) |
| | | { |
| | | return null; |
| | | } // if found "no data" just return null |
| | | |
| | | b1 = base64Alphabet[d1]; |
| | | b2 = base64Alphabet[d2]; |
| | | b3 = base64Alphabet[d3]; |
| | | b4 = base64Alphabet[d4]; |
| | | |
| | | decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4); |
| | | decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); |
| | | decodedData[encodedIndex++] = (byte) (b3 << 6 | b4); |
| | | } |
| | | |
| | | if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++]))) |
| | | { |
| | | return null;// if found "no data" just return null |
| | | } |
| | | |
| | | b1 = base64Alphabet[d1]; |
| | | b2 = base64Alphabet[d2]; |
| | | |
| | | d3 = base64Data[dataIndex++]; |
| | | d4 = base64Data[dataIndex++]; |
| | | if (!isData((d3)) || !isData((d4))) |
| | | {// Check if they are PAD characters |
| | | if (isPad(d3) && isPad(d4)) |
| | | { |
| | | if ((b2 & 0xf) != 0)// last 4 bits should be zero |
| | | { |
| | | return null; |
| | | } |
| | | byte[] tmp = new byte[i * 3 + 1]; |
| | | System.arraycopy(decodedData, 0, tmp, 0, i * 3); |
| | | tmp[encodedIndex] = (byte) (b1 << 2 | b2 >> 4); |
| | | return tmp; |
| | | } |
| | | else if (!isPad(d3) && isPad(d4)) |
| | | { |
| | | b3 = base64Alphabet[d3]; |
| | | if ((b3 & 0x3) != 0)// last 2 bits should be zero |
| | | { |
| | | return null; |
| | | } |
| | | byte[] tmp = new byte[i * 3 + 2]; |
| | | System.arraycopy(decodedData, 0, tmp, 0, i * 3); |
| | | tmp[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4); |
| | | tmp[encodedIndex] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); |
| | | return tmp; |
| | | } |
| | | else |
| | | { |
| | | return null; |
| | | } |
| | | } |
| | | else |
| | | { // No PAD e.g 3cQl |
| | | b3 = base64Alphabet[d3]; |
| | | b4 = base64Alphabet[d4]; |
| | | decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4); |
| | | decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); |
| | | decodedData[encodedIndex++] = (byte) (b3 << 6 | b4); |
| | | |
| | | } |
| | | return decodedData; |
| | | } |
| | | |
| | | /** |
| | | * remove WhiteSpace from MIME containing encoded Base64 data. |
| | | * |
| | | * @param data the byte array of base64 data (with WS) |
| | | * @return the new length |
| | | */ |
| | | private static int removeWhiteSpace(char[] data) |
| | | { |
| | | if (data == null) |
| | | { |
| | | return 0; |
| | | } |
| | | |
| | | // count characters that's not whitespace |
| | | int newSize = 0; |
| | | int len = data.length; |
| | | for (int i = 0; i < len; i++) |
| | | { |
| | | if (!isWhiteSpace(data[i])) |
| | | { |
| | | data[newSize++] = data[i]; |
| | | } |
| | | } |
| | | return newSize; |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.utils.sql; |
| | | |
| | | import com.ruoyi.common.core.exception.UtilException; |
| | | import com.ruoyi.common.core.utils.StringUtils; |
| | | |
| | | /** |
| | | * sql操作工具类 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class SqlUtil |
| | | { |
| | | /** |
| | | * 定义常用的 sql关键字 |
| | | */ |
| | | public static String SQL_REGEX = "and |extractvalue|updatexml|exec |insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |or |+|user()"; |
| | | |
| | | /** |
| | | * 仅支持字母、数字、下划线、空格、逗号、小数点(支持多个字段排序) |
| | | */ |
| | | public static String SQL_PATTERN = "[a-zA-Z0-9_\\ \\,\\.]+"; |
| | | |
| | | /** |
| | | * 限制orderBy最大长度 |
| | | */ |
| | | private static final int ORDER_BY_MAX_LENGTH = 500; |
| | | |
| | | /** |
| | | * 检查字符,防止注入绕过 |
| | | */ |
| | | public static String escapeOrderBySql(String value) |
| | | { |
| | | if (StringUtils.isNotEmpty(value) && !isValidOrderBySql(value)) |
| | | { |
| | | throw new UtilException("参数不符合规范,不能进行查询"); |
| | | } |
| | | if (StringUtils.length(value) > ORDER_BY_MAX_LENGTH) |
| | | { |
| | | throw new UtilException("参数已超过最大限制,不能进行查询"); |
| | | } |
| | | return value; |
| | | } |
| | | |
| | | /** |
| | | * 验证 order by 语法是否符合规范 |
| | | */ |
| | | public static boolean isValidOrderBySql(String value) |
| | | { |
| | | return value.matches(SQL_PATTERN); |
| | | } |
| | | |
| | | /** |
| | | * SQL关键字检查 |
| | | */ |
| | | public static void filterKeyword(String value) |
| | | { |
| | | if (StringUtils.isEmpty(value)) |
| | | { |
| | | return; |
| | | } |
| | | String[] sqlKeywords = StringUtils.split(SQL_REGEX, "\\|"); |
| | | for (String sqlKeyword : sqlKeywords) |
| | | { |
| | | if (StringUtils.indexOfIgnoreCase(value, sqlKeyword) > -1) |
| | | { |
| | | throw new UtilException("参数存在SQL注入风险"); |
| | | } |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.utils.uuid; |
| | | |
| | | /** |
| | | * ID生成器工具类 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class IdUtils { |
| | | /** |
| | | * 获取随机UUID |
| | | * |
| | | * @return 随机UUID |
| | | */ |
| | | public static String randomUUID() { |
| | | return UUID.randomUUID().toString(); |
| | | } |
| | | |
| | | /** |
| | | * 简化的UUID,去掉了横线 |
| | | * |
| | | * @return 简化的UUID,去掉了横线 |
| | | */ |
| | | public static String simpleUUID() { |
| | | return UUID.randomUUID().toString(true); |
| | | } |
| | | |
| | | /** |
| | | * 获取随机UUID,使用性能更好的ThreadLocalRandom生成UUID |
| | | * |
| | | * @return 随机UUID |
| | | */ |
| | | public static String fastUUID() { |
| | | return UUID.fastUUID().toString(); |
| | | } |
| | | |
| | | /** |
| | | * 简化的UUID,去掉了横线,使用性能更好的ThreadLocalRandom生成UUID |
| | | * |
| | | * @return 简化的UUID,去掉了横线 |
| | | */ |
| | | public static String fastSimpleUUID() { |
| | | return UUID.fastUUID().toString(true); |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.utils.uuid; |
| | | |
| | | import java.util.concurrent.atomic.AtomicInteger; |
| | | import com.ruoyi.common.core.utils.DateUtils; |
| | | import com.ruoyi.common.core.utils.StringUtils; |
| | | |
| | | /** |
| | | * @author ruoyi 序列生成类 |
| | | */ |
| | | public class Seq |
| | | { |
| | | // 通用序列类型 |
| | | public static final String commSeqType = "COMMON"; |
| | | |
| | | // 上传序列类型 |
| | | public static final String uploadSeqType = "UPLOAD"; |
| | | |
| | | // 通用接口序列数 |
| | | private static AtomicInteger commSeq = new AtomicInteger(1); |
| | | |
| | | // 上传接口序列数 |
| | | private static AtomicInteger uploadSeq = new AtomicInteger(1); |
| | | |
| | | // 机器标识 |
| | | private static final String machineCode = "A"; |
| | | |
| | | /** |
| | | * 获取通用序列号 |
| | | * |
| | | * @return 序列值 |
| | | */ |
| | | public static String getId() |
| | | { |
| | | return getId(commSeqType); |
| | | } |
| | | |
| | | /** |
| | | * 默认16位序列号 yyMMddHHmmss + 一位机器标识 + 3长度循环递增字符串 |
| | | * |
| | | * @return 序列值 |
| | | */ |
| | | public static String getId(String type) |
| | | { |
| | | AtomicInteger atomicInt = commSeq; |
| | | if (uploadSeqType.equals(type)) |
| | | { |
| | | atomicInt = uploadSeq; |
| | | } |
| | | return getId(atomicInt, 3); |
| | | } |
| | | |
| | | /** |
| | | * 通用接口序列号 yyMMddHHmmss + 一位机器标识 + length长度循环递增字符串 |
| | | * |
| | | * @param atomicInt 序列数 |
| | | * @param length 数值长度 |
| | | * @return 序列值 |
| | | */ |
| | | public static String getId(AtomicInteger atomicInt, int length) |
| | | { |
| | | String result = DateUtils.dateTimeNow(); |
| | | result += machineCode; |
| | | result += getSeq(atomicInt, length); |
| | | return result; |
| | | } |
| | | |
| | | /** |
| | | * 序列循环递增字符串[1, 10 的 (length)幂次方), 用0左补齐length位数 |
| | | * |
| | | * @return 序列值 |
| | | */ |
| | | private synchronized static String getSeq(AtomicInteger atomicInt, int length) |
| | | { |
| | | // 先取值再+1 |
| | | int value = atomicInt.getAndIncrement(); |
| | | |
| | | // 如果更新后值>=10 的 (length)幂次方则重置为1 |
| | | int maxSeq = (int) Math.pow(10, length); |
| | | if (atomicInt.get() >= maxSeq) |
| | | { |
| | | atomicInt.set(1); |
| | | } |
| | | // 转字符串,用0左补齐 |
| | | return StringUtils.padl(value, length); |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.utils.uuid; |
| | | |
| | | import java.security.MessageDigest; |
| | | import java.security.NoSuchAlgorithmException; |
| | | import java.security.SecureRandom; |
| | | import java.util.Random; |
| | | import java.util.concurrent.ThreadLocalRandom; |
| | | import com.ruoyi.common.core.exception.UtilException; |
| | | |
| | | /** |
| | | * 提供通用唯一识别码(universally unique identifier)(UUID)实现 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public final class UUID implements java.io.Serializable, Comparable<UUID> |
| | | { |
| | | private static final long serialVersionUID = -1185015143654744140L; |
| | | |
| | | /** |
| | | * SecureRandom 的单例 |
| | | * |
| | | */ |
| | | private static class Holder |
| | | { |
| | | static final SecureRandom numberGenerator = getSecureRandom(); |
| | | } |
| | | |
| | | /** 此UUID的最高64有效位 */ |
| | | private final long mostSigBits; |
| | | |
| | | /** 此UUID的最低64有效位 */ |
| | | private final long leastSigBits; |
| | | |
| | | /** |
| | | * 私有构造 |
| | | * |
| | | * @param data 数据 |
| | | */ |
| | | private UUID(byte[] data) |
| | | { |
| | | long msb = 0; |
| | | long lsb = 0; |
| | | assert data.length == 16 : "data must be 16 bytes in length"; |
| | | for (int i = 0; i < 8; i++) |
| | | { |
| | | msb = (msb << 8) | (data[i] & 0xff); |
| | | } |
| | | for (int i = 8; i < 16; i++) |
| | | { |
| | | lsb = (lsb << 8) | (data[i] & 0xff); |
| | | } |
| | | this.mostSigBits = msb; |
| | | this.leastSigBits = lsb; |
| | | } |
| | | |
| | | /** |
| | | * 使用指定的数据构造新的 UUID。 |
| | | * |
| | | * @param mostSigBits 用于 {@code UUID} 的最高有效 64 位 |
| | | * @param leastSigBits 用于 {@code UUID} 的最低有效 64 位 |
| | | */ |
| | | public UUID(long mostSigBits, long leastSigBits) |
| | | { |
| | | this.mostSigBits = mostSigBits; |
| | | this.leastSigBits = leastSigBits; |
| | | } |
| | | |
| | | /** |
| | | * 获取类型 4(伪随机生成的)UUID 的静态工厂。 |
| | | * |
| | | * @return 随机生成的 {@code UUID} |
| | | */ |
| | | public static UUID fastUUID() |
| | | { |
| | | return randomUUID(false); |
| | | } |
| | | |
| | | /** |
| | | * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。 |
| | | * |
| | | * @return 随机生成的 {@code UUID} |
| | | */ |
| | | public static UUID randomUUID() |
| | | { |
| | | return randomUUID(true); |
| | | } |
| | | |
| | | /** |
| | | * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。 |
| | | * |
| | | * @param isSecure 是否使用{@link SecureRandom}如果是可以获得更安全的随机码,否则可以得到更好的性能 |
| | | * @return 随机生成的 {@code UUID} |
| | | */ |
| | | public static UUID randomUUID(boolean isSecure) |
| | | { |
| | | final Random ng = isSecure ? Holder.numberGenerator : getRandom(); |
| | | |
| | | byte[] randomBytes = new byte[16]; |
| | | ng.nextBytes(randomBytes); |
| | | randomBytes[6] &= 0x0f; /* clear version */ |
| | | randomBytes[6] |= 0x40; /* set to version 4 */ |
| | | randomBytes[8] &= 0x3f; /* clear variant */ |
| | | randomBytes[8] |= 0x80; /* set to IETF variant */ |
| | | return new UUID(randomBytes); |
| | | } |
| | | |
| | | /** |
| | | * 根据指定的字节数组获取类型 3(基于名称的)UUID 的静态工厂。 |
| | | * |
| | | * @param name 用于构造 UUID 的字节数组。 |
| | | * |
| | | * @return 根据指定数组生成的 {@code UUID} |
| | | */ |
| | | public static UUID nameUUIDFromBytes(byte[] name) |
| | | { |
| | | MessageDigest md; |
| | | try |
| | | { |
| | | md = MessageDigest.getInstance("MD5"); |
| | | } |
| | | catch (NoSuchAlgorithmException nsae) |
| | | { |
| | | throw new InternalError("MD5 not supported"); |
| | | } |
| | | byte[] md5Bytes = md.digest(name); |
| | | md5Bytes[6] &= 0x0f; /* clear version */ |
| | | md5Bytes[6] |= 0x30; /* set to version 3 */ |
| | | md5Bytes[8] &= 0x3f; /* clear variant */ |
| | | md5Bytes[8] |= 0x80; /* set to IETF variant */ |
| | | return new UUID(md5Bytes); |
| | | } |
| | | |
| | | /** |
| | | * 根据 {@link #toString()} 方法中描述的字符串标准表示形式创建{@code UUID}。 |
| | | * |
| | | * @param name 指定 {@code UUID} 字符串 |
| | | * @return 具有指定值的 {@code UUID} |
| | | * @throws IllegalArgumentException 如果 name 与 {@link #toString} 中描述的字符串表示形式不符抛出此异常 |
| | | * |
| | | */ |
| | | public static UUID fromString(String name) |
| | | { |
| | | String[] components = name.split("-"); |
| | | if (components.length != 5) |
| | | { |
| | | throw new IllegalArgumentException("Invalid UUID string: " + name); |
| | | } |
| | | for (int i = 0; i < 5; i++) |
| | | { |
| | | components[i] = "0x" + components[i]; |
| | | } |
| | | |
| | | long mostSigBits = Long.decode(components[0]).longValue(); |
| | | mostSigBits <<= 16; |
| | | mostSigBits |= Long.decode(components[1]).longValue(); |
| | | mostSigBits <<= 16; |
| | | mostSigBits |= Long.decode(components[2]).longValue(); |
| | | |
| | | long leastSigBits = Long.decode(components[3]).longValue(); |
| | | leastSigBits <<= 48; |
| | | leastSigBits |= Long.decode(components[4]).longValue(); |
| | | |
| | | return new UUID(mostSigBits, leastSigBits); |
| | | } |
| | | |
| | | /** |
| | | * 返回此 UUID 的 128 位值中的最低有效 64 位。 |
| | | * |
| | | * @return 此 UUID 的 128 位值中的最低有效 64 位。 |
| | | */ |
| | | public long getLeastSignificantBits() |
| | | { |
| | | return leastSigBits; |
| | | } |
| | | |
| | | /** |
| | | * 返回此 UUID 的 128 位值中的最高有效 64 位。 |
| | | * |
| | | * @return 此 UUID 的 128 位值中最高有效 64 位。 |
| | | */ |
| | | public long getMostSignificantBits() |
| | | { |
| | | return mostSigBits; |
| | | } |
| | | |
| | | /** |
| | | * 与此 {@code UUID} 相关联的版本号. 版本号描述此 {@code UUID} 是如何生成的。 |
| | | * <p> |
| | | * 版本号具有以下含意: |
| | | * <ul> |
| | | * <li>1 基于时间的 UUID |
| | | * <li>2 DCE 安全 UUID |
| | | * <li>3 基于名称的 UUID |
| | | * <li>4 随机生成的 UUID |
| | | * </ul> |
| | | * |
| | | * @return 此 {@code UUID} 的版本号 |
| | | */ |
| | | public int version() |
| | | { |
| | | // Version is bits masked by 0x000000000000F000 in MS long |
| | | return (int) ((mostSigBits >> 12) & 0x0f); |
| | | } |
| | | |
| | | /** |
| | | * 与此 {@code UUID} 相关联的变体号。变体号描述 {@code UUID} 的布局。 |
| | | * <p> |
| | | * 变体号具有以下含意: |
| | | * <ul> |
| | | * <li>0 为 NCS 向后兼容保留 |
| | | * <li>2 <a href="http://www.ietf.org/rfc/rfc4122.txt">IETF RFC 4122</a>(Leach-Salz), 用于此类 |
| | | * <li>6 保留,微软向后兼容 |
| | | * <li>7 保留供以后定义使用 |
| | | * </ul> |
| | | * |
| | | * @return 此 {@code UUID} 相关联的变体号 |
| | | */ |
| | | public int variant() |
| | | { |
| | | // This field is composed of a varying number of bits. |
| | | // 0 - - Reserved for NCS backward compatibility |
| | | // 1 0 - The IETF aka Leach-Salz variant (used by this class) |
| | | // 1 1 0 Reserved, Microsoft backward compatibility |
| | | // 1 1 1 Reserved for future definition. |
| | | return (int) ((leastSigBits >>> (64 - (leastSigBits >>> 62))) & (leastSigBits >> 63)); |
| | | } |
| | | |
| | | /** |
| | | * 与此 UUID 相关联的时间戳值。 |
| | | * |
| | | * <p> |
| | | * 60 位的时间戳值根据此 {@code UUID} 的 time_low、time_mid 和 time_hi 字段构造。<br> |
| | | * 所得到的时间戳以 100 毫微秒为单位,从 UTC(通用协调时间) 1582 年 10 月 15 日零时开始。 |
| | | * |
| | | * <p> |
| | | * 时间戳值仅在在基于时间的 UUID(其 version 类型为 1)中才有意义。<br> |
| | | * 如果此 {@code UUID} 不是基于时间的 UUID,则此方法抛出 UnsupportedOperationException。 |
| | | * |
| | | * @throws UnsupportedOperationException 如果此 {@code UUID} 不是 version 为 1 的 UUID。 |
| | | */ |
| | | public long timestamp() throws UnsupportedOperationException |
| | | { |
| | | checkTimeBase(); |
| | | return (mostSigBits & 0x0FFFL) << 48// |
| | | | ((mostSigBits >> 16) & 0x0FFFFL) << 32// |
| | | | mostSigBits >>> 32; |
| | | } |
| | | |
| | | /** |
| | | * 与此 UUID 相关联的时钟序列值。 |
| | | * |
| | | * <p> |
| | | * 14 位的时钟序列值根据此 UUID 的 clock_seq 字段构造。clock_seq 字段用于保证在基于时间的 UUID 中的时间唯一性。 |
| | | * <p> |
| | | * {@code clockSequence} 值仅在基于时间的 UUID(其 version 类型为 1)中才有意义。 如果此 UUID 不是基于时间的 UUID,则此方法抛出 |
| | | * UnsupportedOperationException。 |
| | | * |
| | | * @return 此 {@code UUID} 的时钟序列 |
| | | * |
| | | * @throws UnsupportedOperationException 如果此 UUID 的 version 不为 1 |
| | | */ |
| | | public int clockSequence() throws UnsupportedOperationException |
| | | { |
| | | checkTimeBase(); |
| | | return (int) ((leastSigBits & 0x3FFF000000000000L) >>> 48); |
| | | } |
| | | |
| | | /** |
| | | * 与此 UUID 相关的节点值。 |
| | | * |
| | | * <p> |
| | | * 48 位的节点值根据此 UUID 的 node 字段构造。此字段旨在用于保存机器的 IEEE 802 地址,该地址用于生成此 UUID 以保证空间唯一性。 |
| | | * <p> |
| | | * 节点值仅在基于时间的 UUID(其 version 类型为 1)中才有意义。<br> |
| | | * 如果此 UUID 不是基于时间的 UUID,则此方法抛出 UnsupportedOperationException。 |
| | | * |
| | | * @return 此 {@code UUID} 的节点值 |
| | | * |
| | | * @throws UnsupportedOperationException 如果此 UUID 的 version 不为 1 |
| | | */ |
| | | public long node() throws UnsupportedOperationException |
| | | { |
| | | checkTimeBase(); |
| | | return leastSigBits & 0x0000FFFFFFFFFFFFL; |
| | | } |
| | | |
| | | /** |
| | | * 返回此{@code UUID} 的字符串表现形式。 |
| | | * |
| | | * <p> |
| | | * UUID 的字符串表示形式由此 BNF 描述: |
| | | * |
| | | * <pre> |
| | | * {@code |
| | | * UUID = <time_low>-<time_mid>-<time_high_and_version>-<variant_and_sequence>-<node> |
| | | * time_low = 4*<hexOctet> |
| | | * time_mid = 2*<hexOctet> |
| | | * time_high_and_version = 2*<hexOctet> |
| | | * variant_and_sequence = 2*<hexOctet> |
| | | * node = 6*<hexOctet> |
| | | * hexOctet = <hexDigit><hexDigit> |
| | | * hexDigit = [0-9a-fA-F] |
| | | * } |
| | | * </pre> |
| | | * |
| | | * </blockquote> |
| | | * |
| | | * @return 此{@code UUID} 的字符串表现形式 |
| | | * @see #toString(boolean) |
| | | */ |
| | | @Override |
| | | public String toString() |
| | | { |
| | | return toString(false); |
| | | } |
| | | |
| | | /** |
| | | * 返回此{@code UUID} 的字符串表现形式。 |
| | | * |
| | | * <p> |
| | | * UUID 的字符串表示形式由此 BNF 描述: |
| | | * |
| | | * <pre> |
| | | * {@code |
| | | * UUID = <time_low>-<time_mid>-<time_high_and_version>-<variant_and_sequence>-<node> |
| | | * time_low = 4*<hexOctet> |
| | | * time_mid = 2*<hexOctet> |
| | | * time_high_and_version = 2*<hexOctet> |
| | | * variant_and_sequence = 2*<hexOctet> |
| | | * node = 6*<hexOctet> |
| | | * hexOctet = <hexDigit><hexDigit> |
| | | * hexDigit = [0-9a-fA-F] |
| | | * } |
| | | * </pre> |
| | | * |
| | | * </blockquote> |
| | | * |
| | | * @param isSimple 是否简单模式,简单模式为不带'-'的UUID字符串 |
| | | * @return 此{@code UUID} 的字符串表现形式 |
| | | */ |
| | | public String toString(boolean isSimple) |
| | | { |
| | | final StringBuilder builder = new StringBuilder(isSimple ? 32 : 36); |
| | | // time_low |
| | | builder.append(digits(mostSigBits >> 32, 8)); |
| | | if (false == isSimple) |
| | | { |
| | | builder.append('-'); |
| | | } |
| | | // time_mid |
| | | builder.append(digits(mostSigBits >> 16, 4)); |
| | | if (false == isSimple) |
| | | { |
| | | builder.append('-'); |
| | | } |
| | | // time_high_and_version |
| | | builder.append(digits(mostSigBits, 4)); |
| | | if (false == isSimple) |
| | | { |
| | | builder.append('-'); |
| | | } |
| | | // variant_and_sequence |
| | | builder.append(digits(leastSigBits >> 48, 4)); |
| | | if (false == isSimple) |
| | | { |
| | | builder.append('-'); |
| | | } |
| | | // node |
| | | builder.append(digits(leastSigBits, 12)); |
| | | |
| | | return builder.toString(); |
| | | } |
| | | |
| | | /** |
| | | * 返回此 UUID 的哈希码。 |
| | | * |
| | | * @return UUID 的哈希码值。 |
| | | */ |
| | | @Override |
| | | public int hashCode() |
| | | { |
| | | long hilo = mostSigBits ^ leastSigBits; |
| | | return ((int) (hilo >> 32)) ^ (int) hilo; |
| | | } |
| | | |
| | | /** |
| | | * 将此对象与指定对象比较。 |
| | | * <p> |
| | | * 当且仅当参数不为 {@code null}、而是一个 UUID 对象、具有与此 UUID 相同的 varriant、包含相同的值(每一位均相同)时,结果才为 {@code true}。 |
| | | * |
| | | * @param obj 要与之比较的对象 |
| | | * |
| | | * @return 如果对象相同,则返回 {@code true};否则返回 {@code false} |
| | | */ |
| | | @Override |
| | | public boolean equals(Object obj) |
| | | { |
| | | if ((null == obj) || (obj.getClass() != UUID.class)) |
| | | { |
| | | return false; |
| | | } |
| | | UUID id = (UUID) obj; |
| | | return (mostSigBits == id.mostSigBits && leastSigBits == id.leastSigBits); |
| | | } |
| | | |
| | | // Comparison Operations |
| | | |
| | | /** |
| | | * 将此 UUID 与指定的 UUID 比较。 |
| | | * |
| | | * <p> |
| | | * 如果两个 UUID 不同,且第一个 UUID 的最高有效字段大于第二个 UUID 的对应字段,则第一个 UUID 大于第二个 UUID。 |
| | | * |
| | | * @param val 与此 UUID 比较的 UUID |
| | | * |
| | | * @return 在此 UUID 小于、等于或大于 val 时,分别返回 -1、0 或 1。 |
| | | * |
| | | */ |
| | | @Override |
| | | public int compareTo(UUID val) |
| | | { |
| | | // The ordering is intentionally set up so that the UUIDs |
| | | // can simply be numerically compared as two numbers |
| | | return (this.mostSigBits < val.mostSigBits ? -1 : // |
| | | (this.mostSigBits > val.mostSigBits ? 1 : // |
| | | (this.leastSigBits < val.leastSigBits ? -1 : // |
| | | (this.leastSigBits > val.leastSigBits ? 1 : // |
| | | 0)))); |
| | | } |
| | | |
| | | // ------------------------------------------------------------------------------------------------------------------- |
| | | // Private method start |
| | | /** |
| | | * 返回指定数字对应的hex值 |
| | | * |
| | | * @param val 值 |
| | | * @param digits 位 |
| | | * @return 值 |
| | | */ |
| | | private static String digits(long val, int digits) |
| | | { |
| | | long hi = 1L << (digits * 4); |
| | | return Long.toHexString(hi | (val & (hi - 1))).substring(1); |
| | | } |
| | | |
| | | /** |
| | | * 检查是否为time-based版本UUID |
| | | */ |
| | | private void checkTimeBase() |
| | | { |
| | | if (version() != 1) |
| | | { |
| | | throw new UnsupportedOperationException("Not a time-based UUID"); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 获取{@link SecureRandom},类提供加密的强随机数生成器 (RNG) |
| | | * |
| | | * @return {@link SecureRandom} |
| | | */ |
| | | public static SecureRandom getSecureRandom() |
| | | { |
| | | try |
| | | { |
| | | return SecureRandom.getInstance("SHA1PRNG"); |
| | | } |
| | | catch (NoSuchAlgorithmException e) |
| | | { |
| | | throw new UtilException(e); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 获取随机数生成器对象<br> |
| | | * ThreadLocalRandom是JDK 7之后提供并发产生随机数,能够解决多个线程发生的竞争争夺。 |
| | | * |
| | | * @return {@link ThreadLocalRandom} |
| | | */ |
| | | public static ThreadLocalRandom getRandom() |
| | | { |
| | | return ThreadLocalRandom.current(); |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.web.controller; |
| | | |
| | | import com.github.pagehelper.PageInfo; |
| | | import com.ruoyi.common.core.constant.HttpStatus; |
| | | import com.ruoyi.common.core.utils.DateUtils; |
| | | import com.ruoyi.common.core.utils.PageUtils; |
| | | import com.ruoyi.common.core.web.domain.AjaxResult; |
| | | import com.ruoyi.common.core.web.page.TableDataInfo; |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | import org.springframework.web.bind.WebDataBinder; |
| | | import org.springframework.web.bind.annotation.InitBinder; |
| | | |
| | | import java.beans.PropertyEditorSupport; |
| | | import java.util.Date; |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * 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 clearPage() { |
| | | PageUtils.clearPage(); |
| | | } |
| | | |
| | | /** |
| | | * 响应请求分页数据 |
| | | */ |
| | | @SuppressWarnings({"rawtypes", "unchecked"}) |
| | | protected TableDataInfo getDataTable(List<?> list) { |
| | | TableDataInfo rspData = new TableDataInfo(); |
| | | rspData.setCode(HttpStatus.SUCCESS); |
| | | rspData.setRows(list); |
| | | rspData.setMsg("查询成功"); |
| | | rspData.setTotal(new PageInfo(list).getTotal()); |
| | | return rspData; |
| | | } |
| | | |
| | | /** |
| | | * 返回成功 |
| | | */ |
| | | public AjaxResult success() { |
| | | return AjaxResult.success(); |
| | | } |
| | | |
| | | /** |
| | | * 返回成功消息 |
| | | */ |
| | | public AjaxResult success(String message) { |
| | | return AjaxResult.success(message); |
| | | } |
| | | |
| | | /** |
| | | * 返回成功消息 |
| | | */ |
| | | public AjaxResult success(Object data) { |
| | | return AjaxResult.success(data); |
| | | } |
| | | |
| | | /** |
| | | * 返回失败消息 |
| | | */ |
| | | public AjaxResult error() { |
| | | return AjaxResult.error(); |
| | | } |
| | | |
| | | /** |
| | | * 返回失败消息 |
| | | */ |
| | | 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(); |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.web.domain; |
| | | |
| | | import java.util.HashMap; |
| | | import java.util.Objects; |
| | | import com.ruoyi.common.core.constant.HttpStatus; |
| | | import com.ruoyi.common.core.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); |
| | | } |
| | | |
| | | /** |
| | | * 是否为成功消息 |
| | | * |
| | | * @return 结果 |
| | | */ |
| | | public boolean isSuccess() |
| | | { |
| | | return Objects.equals(HttpStatus.SUCCESS, this.get(CODE_TAG)); |
| | | } |
| | | |
| | | /** |
| | | * 是否为警告消息 |
| | | * |
| | | * @return 结果 |
| | | */ |
| | | public boolean isWarn() |
| | | { |
| | | return Objects.equals(HttpStatus.WARN, this.get(CODE_TAG)); |
| | | } |
| | | |
| | | /** |
| | | * 是否为错误消息 |
| | | * |
| | | * @return 结果 |
| | | */ |
| | | public boolean isError() |
| | | { |
| | | return Objects.equals(HttpStatus.ERROR, this.get(CODE_TAG)); |
| | | } |
| | | |
| | | /** |
| | | * 方便链式调用 |
| | | * |
| | | * @param key |
| | | * @param value |
| | | * @return |
| | | */ |
| | | @Override |
| | | public AjaxResult put(String key, Object value) |
| | | { |
| | | super.put(key, value); |
| | | return this; |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.web.domain; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonFormat; |
| | | import com.fasterxml.jackson.annotation.JsonIgnore; |
| | | import com.fasterxml.jackson.annotation.JsonInclude; |
| | | |
| | | import java.io.Serializable; |
| | | import java.util.Date; |
| | | import java.util.HashMap; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * Entity基类 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class BaseEntity implements Serializable { |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | /** |
| | | * 搜索值 |
| | | */ |
| | | @JsonIgnore |
| | | private String searchValue; |
| | | |
| | | /** |
| | | * 创建者 |
| | | */ |
| | | private String createBy; |
| | | |
| | | /** |
| | | * 创建时间 |
| | | */ |
| | | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | private Date createTime; |
| | | |
| | | /** |
| | | * 更新者 |
| | | */ |
| | | private String updateBy; |
| | | |
| | | /** |
| | | * 更新时间 |
| | | */ |
| | | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
| | | private Date updateTime; |
| | | |
| | | /** |
| | | * 备注 |
| | | */ |
| | | private String remark; |
| | | |
| | | /** |
| | | * 请求参数 |
| | | */ |
| | | @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; |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.web.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; |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.web.page; |
| | | |
| | | import com.ruoyi.common.core.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; |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.web.page; |
| | | |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
| | | |
| | | import java.io.Serializable; |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * @Author liheng |
| | | * @Date 2019/08/26 10:28 AM |
| | | * @Description |
| | | */ |
| | | @JsonIgnoreProperties({"orders", "optimizeCountSql", "hitCount", "countId", "maxLimit", "searchCount"}) |
| | | public class PageInfo<T> extends Page<T> implements Serializable { |
| | | |
| | | private static final long serialVersionUID = 1L; |
| | | private boolean hasNextPage; |
| | | private boolean hasPrevPage; |
| | | private long startIndex; |
| | | |
| | | public PageInfo(long currentPage, long pageShowCount) { |
| | | super(currentPage, pageShowCount); |
| | | this.startIndex = super.offset(); |
| | | } |
| | | |
| | | public PageInfo() { |
| | | super(); |
| | | } |
| | | |
| | | private static boolean hasPrevPage(long currentPage) { |
| | | return currentPage != 1; |
| | | } |
| | | |
| | | @Override |
| | | public PageInfo<T> setRecords(List<T> records) { |
| | | super.setRecords(records); |
| | | this.hasNextPage = super.hasNext(); |
| | | this.hasPrevPage = super.hasPrevious(); |
| | | return this; |
| | | } |
| | | |
| | | public boolean getHasNextPage() { |
| | | return hasNextPage; |
| | | } |
| | | |
| | | public void setHasNextPage(boolean hasNextPage) { |
| | | this.hasNextPage = hasNextPage; |
| | | } |
| | | |
| | | public boolean getHasPrevPage() { |
| | | return hasPrevPage; |
| | | } |
| | | |
| | | public void setHasPrevPage(boolean hasPrevPage) { |
| | | this.hasPrevPage = hasPrevPage; |
| | | } |
| | | |
| | | private boolean hasNextPage(long currentPage, long totalPage) { |
| | | return currentPage < totalPage && totalPage != 0; |
| | | } |
| | | |
| | | public long getStartIndex() { |
| | | return startIndex; |
| | | } |
| | | |
| | | public void setStartIndex(long startIndex) { |
| | | this.startIndex = startIndex; |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.web.page; |
| | | |
| | | import java.io.Serializable; |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * 表格分页数据对象 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class TableDataInfo implements Serializable |
| | | { |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | /** 总记录数 */ |
| | | private long total; |
| | | |
| | | /** 列表数据 */ |
| | | private List<?> rows; |
| | | |
| | | /** 消息状态码 */ |
| | | private int code; |
| | | |
| | | /** 消息内容 */ |
| | | 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; |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.web.page; |
| | | |
| | | import com.ruoyi.common.core.text.Convert; |
| | | import com.ruoyi.common.core.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(); |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.xss; |
| | | |
| | | import javax.validation.Constraint; |
| | | import javax.validation.Payload; |
| | | import java.lang.annotation.ElementType; |
| | | import java.lang.annotation.Retention; |
| | | import java.lang.annotation.RetentionPolicy; |
| | | import java.lang.annotation.Target; |
| | | |
| | | /** |
| | | * 自定义xss校验注解 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | @Retention(RetentionPolicy.RUNTIME) |
| | | @Target(value = { ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.PARAMETER }) |
| | | @Constraint(validatedBy = { XssValidator.class }) |
| | | public @interface Xss |
| | | { |
| | | String message() |
| | | |
| | | default "不允许任何脚本运行"; |
| | | |
| | | Class<?>[] groups() default {}; |
| | | |
| | | Class<? extends Payload>[] payload() default {}; |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.core.xss; |
| | | |
| | | import java.util.regex.Matcher; |
| | | import java.util.regex.Pattern; |
| | | import javax.validation.ConstraintValidator; |
| | | import javax.validation.ConstraintValidatorContext; |
| | | import com.ruoyi.common.core.utils.StringUtils; |
| | | |
| | | /** |
| | | * 自定义xss校验注解实现 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class XssValidator implements ConstraintValidator<Xss, String> |
| | | { |
| | | private static final String HTML_PATTERN = "<(\\S*?)[^>]*>.*?|<.*? />"; |
| | | |
| | | @Override |
| | | public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) |
| | | { |
| | | if (StringUtils.isBlank(value)) |
| | | { |
| | | return true; |
| | | } |
| | | return !containsHtml(value); |
| | | } |
| | | |
| | | public static boolean containsHtml(String value) |
| | | { |
| | | StringBuilder sHtml = new StringBuilder(); |
| | | Pattern pattern = Pattern.compile(HTML_PATTERN); |
| | | Matcher matcher = pattern.matcher(value); |
| | | while (matcher.find()) |
| | | { |
| | | sHtml.append(matcher.group()); |
| | | } |
| | | return pattern.matcher(sHtml).matches(); |
| | | } |
| | | } |
New file |
| | |
| | | com.ruoyi.common.core.utils.SpringUtils |
New file |
| | |
| | | <?xml version="1.0" encoding="UTF-8"?> |
| | | <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
| | | xmlns="http://maven.apache.org/POM/4.0.0" |
| | | xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
| | | <parent> |
| | | <groupId>com.ruoyi</groupId> |
| | | <artifactId>ruoyi-common</artifactId> |
| | | <version>3.6.4</version> |
| | | </parent> |
| | | <modelVersion>4.0.0</modelVersion> |
| | | |
| | | <artifactId>ruoyi-common-datascope</artifactId> |
| | | |
| | | <description> |
| | | ruoyi-common-datascope权限范围 |
| | | </description> |
| | | |
| | | <dependencies> |
| | | |
| | | <!-- RuoYi Common Security --> |
| | | <dependency> |
| | | <groupId>com.ruoyi</groupId> |
| | | <artifactId>ruoyi-common-security</artifactId> |
| | | </dependency> |
| | | |
| | | </dependencies> |
| | | </project> |
New file |
| | |
| | | package com.ruoyi.common.datascope.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 ""; |
| | | |
| | | /** |
| | | * 权限字符(用于多个角色匹配符合要求的权限)默认根据权限注解@RequiresPermissions获取,多个权限用逗号分隔开来 |
| | | */ |
| | | public String permission() default ""; |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.datascope.aspect; |
| | | |
| | | import com.ruoyi.common.core.utils.StringUtils; |
| | | import com.ruoyi.common.core.web.domain.BaseEntity; |
| | | import com.ruoyi.common.datascope.annotation.DataScope; |
| | | import com.ruoyi.common.security.utils.SecurityUtils; |
| | | import com.ruoyi.system.api.domain.SysUser; |
| | | import com.ruoyi.system.api.model.LoginUser; |
| | | import org.aspectj.lang.JoinPoint; |
| | | import org.aspectj.lang.annotation.Aspect; |
| | | import org.aspectj.lang.annotation.Before; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * 数据过滤处理 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | @Aspect |
| | | @Component |
| | | public class DataScopeAspect { |
| | | |
| | | /** |
| | | * 数据权限过滤关键字 |
| | | */ |
| | | public static final String DATA_SCOPE = "dataScope"; |
| | | |
| | | @Before("@annotation(controllerDataScope)") |
| | | public void doBefore(JoinPoint point, DataScope controllerDataScope) { |
| | | clearDataScope(point); |
| | | handleDataScope(point, controllerDataScope); |
| | | } |
| | | |
| | | protected void handleDataScope(final JoinPoint joinPoint, DataScope controllerDataScope) { |
| | | // 获取当前的用户 |
| | | LoginUser loginUser = SecurityUtils.getLoginUser(); |
| | | if (StringUtils.isNotNull(loginUser)) { |
| | | SysUser currentUser = loginUser.getSysUser(); |
| | | // 如果是超级管理员,则不过滤数据 |
| | | if (StringUtils.isNotNull(currentUser) && !currentUser.isAdmin()) { |
| | | dataScopeFilter(joinPoint, controllerDataScope.deptAlias()); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 数据范围过滤 |
| | | * |
| | | * @param joinPoint 切点 |
| | | * @param deptAlias 部门别名 |
| | | */ |
| | | public static void dataScopeFilter(JoinPoint joinPoint, String deptAlias) { |
| | | StringBuilder sqlString = new StringBuilder(); |
| | | List<String> conditions = new ArrayList<String>(); |
| | | |
| | | // 多角色情况下,所有角色都不包含传递过来的权限字符,这个时候sqlString也会为空,所以要限制一下,不查询任何数据 |
| | | if (StringUtils.isEmpty(conditions)) { |
| | | sqlString.append(StringUtils.format(" OR {}.dept_id = 0 ", deptAlias)); |
| | | } |
| | | if (StringUtils.isNotBlank(sqlString.toString())) { |
| | | Object params = joinPoint.getArgs()[0]; |
| | | if (StringUtils.isNotNull(params) && params instanceof BaseEntity) { |
| | | BaseEntity baseEntity = (BaseEntity) params; |
| | | baseEntity.getParams().put(DATA_SCOPE, " AND (" + sqlString.substring(4) + ")"); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 拼接权限sql前先清空params.dataScope参数防止注入 |
| | | */ |
| | | private void clearDataScope(final JoinPoint joinPoint) { |
| | | Object params = joinPoint.getArgs()[0]; |
| | | if (StringUtils.isNotNull(params) && params instanceof BaseEntity) { |
| | | BaseEntity baseEntity = (BaseEntity) params; |
| | | baseEntity.getParams().put(DATA_SCOPE, ""); |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | com.ruoyi.common.datascope.aspect.DataScopeAspect |
New file |
| | |
| | | <?xml version="1.0" encoding="UTF-8"?> |
| | | <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
| | | xmlns="http://maven.apache.org/POM/4.0.0" |
| | | xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
| | | <parent> |
| | | <groupId>com.ruoyi</groupId> |
| | | <artifactId>ruoyi-common</artifactId> |
| | | <version>3.6.4</version> |
| | | </parent> |
| | | <modelVersion>4.0.0</modelVersion> |
| | | |
| | | <artifactId>ruoyi-common-datasource</artifactId> |
| | | |
| | | <description> |
| | | ruoyi-common-datasource多数据源 |
| | | </description> |
| | | |
| | | <dependencies> |
| | | |
| | | <!-- Druid --> |
| | | <dependency> |
| | | <groupId>com.alibaba</groupId> |
| | | <artifactId>druid-spring-boot-starter</artifactId> |
| | | <version>${druid.version}</version> |
| | | </dependency> |
| | | |
| | | <!-- Dynamic DataSource --> |
| | | <dependency> |
| | | <groupId>com.baomidou</groupId> |
| | | <artifactId>dynamic-datasource-spring-boot-starter</artifactId> |
| | | <version>${dynamic-ds.version}</version> |
| | | </dependency> |
| | | |
| | | </dependencies> |
| | | </project> |
New file |
| | |
| | | package com.ruoyi.common.datasource.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.baomidou.dynamic.datasource.annotation.DS; |
| | | |
| | | /** |
| | | * 主库数据源 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | @Target({ ElementType.TYPE, ElementType.METHOD }) |
| | | @Retention(RetentionPolicy.RUNTIME) |
| | | @Documented |
| | | @DS("master") |
| | | public @interface Master |
| | | { |
| | | |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.datasource.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.baomidou.dynamic.datasource.annotation.DS; |
| | | |
| | | /** |
| | | * 从库数据源 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | @Target({ ElementType.TYPE, ElementType.METHOD }) |
| | | @Retention(RetentionPolicy.RUNTIME) |
| | | @Documented |
| | | @DS("slave") |
| | | public @interface Slave |
| | | { |
| | | |
| | | } |
New file |
| | |
| | | <?xml version="1.0" encoding="UTF-8"?> |
| | | <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
| | | xmlns="http://maven.apache.org/POM/4.0.0" |
| | | xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
| | | <parent> |
| | | <groupId>com.ruoyi</groupId> |
| | | <artifactId>ruoyi-common</artifactId> |
| | | <version>3.6.4</version> |
| | | </parent> |
| | | <modelVersion>4.0.0</modelVersion> |
| | | |
| | | <artifactId>ruoyi-common-log</artifactId> |
| | | |
| | | <description> |
| | | ruoyi-common-log日志记录 |
| | | </description> |
| | | |
| | | <dependencies> |
| | | |
| | | <!-- RuoYi Common Security --> |
| | | <dependency> |
| | | <groupId>com.ruoyi</groupId> |
| | | <artifactId>ruoyi-common-security</artifactId> |
| | | </dependency> |
| | | |
| | | </dependencies> |
| | | </project> |
New file |
| | |
| | | package com.ruoyi.common.log.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.log.enums.BusinessType; |
| | | import com.ruoyi.common.log.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 {}; |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.log.aspect; |
| | | |
| | | import java.util.Collection; |
| | | import java.util.Map; |
| | | import javax.servlet.http.HttpServletRequest; |
| | | import javax.servlet.http.HttpServletResponse; |
| | | import org.apache.commons.lang3.ArrayUtils; |
| | | import org.aspectj.lang.JoinPoint; |
| | | import org.aspectj.lang.annotation.AfterReturning; |
| | | import org.aspectj.lang.annotation.AfterThrowing; |
| | | import org.aspectj.lang.annotation.Aspect; |
| | | import org.aspectj.lang.annotation.Before; |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.core.NamedThreadLocal; |
| | | import org.springframework.http.HttpMethod; |
| | | import org.springframework.stereotype.Component; |
| | | import org.springframework.validation.BindingResult; |
| | | import org.springframework.web.multipart.MultipartFile; |
| | | import com.alibaba.fastjson2.JSON; |
| | | import com.ruoyi.common.core.utils.ServletUtils; |
| | | import com.ruoyi.common.core.utils.StringUtils; |
| | | import com.ruoyi.common.core.utils.ip.IpUtils; |
| | | import com.ruoyi.common.log.annotation.Log; |
| | | import com.ruoyi.common.log.enums.BusinessStatus; |
| | | import com.ruoyi.common.log.filter.PropertyPreExcludeFilter; |
| | | import com.ruoyi.common.log.service.AsyncLogService; |
| | | import com.ruoyi.common.security.utils.SecurityUtils; |
| | | import com.ruoyi.system.api.domain.SysOperLog; |
| | | |
| | | /** |
| | | * 操作日志记录处理 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | @Aspect |
| | | @Component |
| | | public class LogAspect |
| | | { |
| | | private static final Logger log = LoggerFactory.getLogger(LogAspect.class); |
| | | |
| | | /** 排除敏感属性字段 */ |
| | | public static final String[] EXCLUDE_PROPERTIES = { "password", "oldPassword", "newPassword", "confirmPassword" }; |
| | | |
| | | /** 计算操作消耗时间 */ |
| | | private static final ThreadLocal<Long> TIME_THREADLOCAL = new NamedThreadLocal<Long>("Cost Time"); |
| | | |
| | | @Autowired |
| | | private AsyncLogService asyncLogService; |
| | | |
| | | /** |
| | | * 处理请求前执行 |
| | | */ |
| | | @Before(value = "@annotation(controllerLog)") |
| | | public void boBefore(JoinPoint joinPoint, Log controllerLog) |
| | | { |
| | | TIME_THREADLOCAL.set(System.currentTimeMillis()); |
| | | } |
| | | |
| | | /** |
| | | * 处理完请求后执行 |
| | | * |
| | | * @param joinPoint 切点 |
| | | */ |
| | | @AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult") |
| | | public void doAfterReturning(JoinPoint joinPoint, Log controllerLog, Object jsonResult) |
| | | { |
| | | handleLog(joinPoint, controllerLog, null, jsonResult); |
| | | } |
| | | |
| | | /** |
| | | * 拦截异常操作 |
| | | * |
| | | * @param joinPoint 切点 |
| | | * @param e 异常 |
| | | */ |
| | | @AfterThrowing(value = "@annotation(controllerLog)", throwing = "e") |
| | | public void doAfterThrowing(JoinPoint joinPoint, Log controllerLog, Exception e) |
| | | { |
| | | handleLog(joinPoint, controllerLog, e, null); |
| | | } |
| | | |
| | | protected void handleLog(final JoinPoint joinPoint, Log controllerLog, final Exception e, Object jsonResult) |
| | | { |
| | | try |
| | | { |
| | | // *========数据库日志=========*// |
| | | SysOperLog operLog = new SysOperLog(); |
| | | operLog.setStatus(BusinessStatus.SUCCESS.ordinal()); |
| | | // 请求的地址 |
| | | String ip = IpUtils.getIpAddr(); |
| | | operLog.setOperIp(ip); |
| | | operLog.setOperUrl(StringUtils.substring(ServletUtils.getRequest().getRequestURI(), 0, 255)); |
| | | String username = SecurityUtils.getUsername(); |
| | | if (StringUtils.isNotBlank(username)) |
| | | { |
| | | operLog.setOperName(username); |
| | | } |
| | | |
| | | if (e != null) |
| | | { |
| | | operLog.setStatus(BusinessStatus.FAIL.ordinal()); |
| | | operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000)); |
| | | } |
| | | // 设置方法名称 |
| | | String className = joinPoint.getTarget().getClass().getName(); |
| | | String methodName = joinPoint.getSignature().getName(); |
| | | operLog.setMethod(className + "." + methodName + "()"); |
| | | // 设置请求方式 |
| | | operLog.setRequestMethod(ServletUtils.getRequest().getMethod()); |
| | | // 处理设置注解上的参数 |
| | | getControllerMethodDescription(joinPoint, controllerLog, operLog, jsonResult); |
| | | // 设置消耗时间 |
| | | operLog.setCostTime(System.currentTimeMillis() - TIME_THREADLOCAL.get()); |
| | | // 保存数据库 |
| | | asyncLogService.saveSysLog(operLog); |
| | | } |
| | | catch (Exception exp) |
| | | { |
| | | // 记录本地异常日志 |
| | | log.error("异常信息:{}", exp.getMessage()); |
| | | exp.printStackTrace(); |
| | | } |
| | | finally |
| | | { |
| | | TIME_THREADLOCAL.remove(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 获取注解中对方法的描述信息 用于Controller层注解 |
| | | * |
| | | * @param log 日志 |
| | | * @param operLog 操作日志 |
| | | * @throws Exception |
| | | */ |
| | | public void getControllerMethodDescription(JoinPoint joinPoint, Log log, SysOperLog operLog, Object jsonResult) throws Exception |
| | | { |
| | | // 设置action动作 |
| | | operLog.setBusinessType(log.businessType().ordinal()); |
| | | // 设置标题 |
| | | operLog.setTitle(log.title()); |
| | | // 设置操作人类别 |
| | | operLog.setOperatorType(log.operatorType().ordinal()); |
| | | // 是否需要保存request,参数和值 |
| | | if (log.isSaveRequestData()) |
| | | { |
| | | // 获取参数的信息,传入到数据库中。 |
| | | setRequestValue(joinPoint, operLog, log.excludeParamNames()); |
| | | } |
| | | // 是否需要保存response,参数和值 |
| | | if (log.isSaveResponseData() && StringUtils.isNotNull(jsonResult)) |
| | | { |
| | | operLog.setJsonResult(StringUtils.substring(JSON.toJSONString(jsonResult), 0, 2000)); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 获取请求的参数,放到log中 |
| | | * |
| | | * @param operLog 操作日志 |
| | | * @throws Exception 异常 |
| | | */ |
| | | private void setRequestValue(JoinPoint joinPoint, SysOperLog operLog, String[] excludeParamNames) throws Exception |
| | | { |
| | | String requestMethod = operLog.getRequestMethod(); |
| | | Map<?, ?> paramsMap = ServletUtils.getParamMap(ServletUtils.getRequest()); |
| | | if (StringUtils.isEmpty(paramsMap) |
| | | && (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod))) |
| | | { |
| | | String params = argsArrayToString(joinPoint.getArgs(), excludeParamNames); |
| | | operLog.setOperParam(StringUtils.substring(params, 0, 2000)); |
| | | } |
| | | else |
| | | { |
| | | operLog.setOperParam(StringUtils.substring(JSON.toJSONString(paramsMap, excludePropertyPreFilter(excludeParamNames)), 0, 2000)); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 参数拼装 |
| | | */ |
| | | private String argsArrayToString(Object[] paramsArray, String[] excludeParamNames) |
| | | { |
| | | String params = ""; |
| | | if (paramsArray != null && paramsArray.length > 0) |
| | | { |
| | | for (Object o : paramsArray) |
| | | { |
| | | if (StringUtils.isNotNull(o) && !isFilterObject(o)) |
| | | { |
| | | try |
| | | { |
| | | String jsonObj = JSON.toJSONString(o, excludePropertyPreFilter(excludeParamNames)); |
| | | params += jsonObj.toString() + " "; |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | } |
| | | } |
| | | } |
| | | } |
| | | return params.trim(); |
| | | } |
| | | |
| | | /** |
| | | * 忽略敏感属性 |
| | | */ |
| | | public PropertyPreExcludeFilter excludePropertyPreFilter(String[] excludeParamNames) |
| | | { |
| | | return new PropertyPreExcludeFilter().addExcludes(ArrayUtils.addAll(EXCLUDE_PROPERTIES, excludeParamNames)); |
| | | } |
| | | |
| | | /** |
| | | * 判断是否需要过滤的对象。 |
| | | * |
| | | * @param o 对象信息。 |
| | | * @return 如果是需要过滤的对象,则返回true;否则返回false。 |
| | | */ |
| | | @SuppressWarnings("rawtypes") |
| | | public boolean isFilterObject(final Object o) |
| | | { |
| | | Class<?> clazz = o.getClass(); |
| | | if (clazz.isArray()) |
| | | { |
| | | return clazz.getComponentType().isAssignableFrom(MultipartFile.class); |
| | | } |
| | | else if (Collection.class.isAssignableFrom(clazz)) |
| | | { |
| | | Collection collection = (Collection) o; |
| | | for (Object value : collection) |
| | | { |
| | | return value instanceof MultipartFile; |
| | | } |
| | | } |
| | | else if (Map.class.isAssignableFrom(clazz)) |
| | | { |
| | | Map map = (Map) o; |
| | | for (Object value : map.entrySet()) |
| | | { |
| | | Map.Entry entry = (Map.Entry) value; |
| | | return entry.getValue() instanceof MultipartFile; |
| | | } |
| | | } |
| | | return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse |
| | | || o instanceof BindingResult; |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.log.enums; |
| | | |
| | | /** |
| | | * 操作状态 |
| | | * |
| | | * @author ruoyi |
| | | * |
| | | */ |
| | | public enum BusinessStatus |
| | | { |
| | | /** |
| | | * 成功 |
| | | */ |
| | | SUCCESS, |
| | | |
| | | /** |
| | | * 失败 |
| | | */ |
| | | FAIL, |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.log.enums; |
| | | |
| | | /** |
| | | * 业务操作类型 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public enum BusinessType |
| | | { |
| | | /** |
| | | * 其它 |
| | | */ |
| | | OTHER, |
| | | |
| | | /** |
| | | * 新增 |
| | | */ |
| | | INSERT, |
| | | |
| | | /** |
| | | * 修改 |
| | | */ |
| | | UPDATE, |
| | | |
| | | /** |
| | | * 删除 |
| | | */ |
| | | DELETE, |
| | | |
| | | /** |
| | | * 授权 |
| | | */ |
| | | GRANT, |
| | | |
| | | /** |
| | | * 导出 |
| | | */ |
| | | EXPORT, |
| | | |
| | | /** |
| | | * 导入 |
| | | */ |
| | | IMPORT, |
| | | |
| | | /** |
| | | * 强退 |
| | | */ |
| | | FORCE, |
| | | |
| | | /** |
| | | * 生成代码 |
| | | */ |
| | | GENCODE, |
| | | |
| | | /** |
| | | * 清空数据 |
| | | */ |
| | | CLEAN, |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.log.enums; |
| | | |
| | | /** |
| | | * 操作人类别 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public enum OperatorType |
| | | { |
| | | /** |
| | | * 其它 |
| | | */ |
| | | OTHER, |
| | | |
| | | /** |
| | | * 后台用户 |
| | | */ |
| | | MANAGE, |
| | | |
| | | /** |
| | | * 手机端用户 |
| | | */ |
| | | MOBILE |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.log.filter; |
| | | |
| | | import com.alibaba.fastjson2.filter.SimplePropertyPreFilter; |
| | | |
| | | /** |
| | | * 排除JSON敏感属性 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class PropertyPreExcludeFilter extends SimplePropertyPreFilter |
| | | { |
| | | public PropertyPreExcludeFilter() |
| | | { |
| | | } |
| | | |
| | | public PropertyPreExcludeFilter addExcludes(String... filters) |
| | | { |
| | | for (int i = 0; i < filters.length; i++) |
| | | { |
| | | this.getExcludes().add(filters[i]); |
| | | } |
| | | return this; |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.log.service; |
| | | |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.scheduling.annotation.Async; |
| | | import org.springframework.stereotype.Service; |
| | | import com.ruoyi.common.core.constant.SecurityConstants; |
| | | import com.ruoyi.system.api.RemoteLogService; |
| | | import com.ruoyi.system.api.domain.SysOperLog; |
| | | |
| | | /** |
| | | * 异步调用日志服务 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | @Service |
| | | public class AsyncLogService |
| | | { |
| | | @Autowired |
| | | private RemoteLogService remoteLogService; |
| | | |
| | | /** |
| | | * 保存系统日志记录 |
| | | */ |
| | | @Async |
| | | public void saveSysLog(SysOperLog sysOperLog) throws Exception |
| | | { |
| | | remoteLogService.saveLog(sysOperLog, SecurityConstants.INNER); |
| | | } |
| | | } |
New file |
| | |
| | | com.ruoyi.common.log.service.AsyncLogService |
| | | com.ruoyi.common.log.aspect.LogAspect |
New file |
| | |
| | | <?xml version="1.0" encoding="UTF-8"?> |
| | | <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
| | | xmlns="http://maven.apache.org/POM/4.0.0" |
| | | xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
| | | <parent> |
| | | <groupId>com.ruoyi</groupId> |
| | | <artifactId>ruoyi-common</artifactId> |
| | | <version>3.6.4</version> |
| | | </parent> |
| | | <modelVersion>4.0.0</modelVersion> |
| | | |
| | | <artifactId>ruoyi-common-redis</artifactId> |
| | | |
| | | <description> |
| | | ruoyi-common-redis缓存服务 |
| | | </description> |
| | | |
| | | <dependencies> |
| | | |
| | | <!-- SpringBoot Boot Redis --> |
| | | <dependency> |
| | | <groupId>org.springframework.boot</groupId> |
| | | <artifactId>spring-boot-starter-data-redis</artifactId> |
| | | </dependency> |
| | | |
| | | <!-- RuoYi Common Core--> |
| | | <dependency> |
| | | <groupId>com.ruoyi</groupId> |
| | | <artifactId>ruoyi-common-core</artifactId> |
| | | </dependency> |
| | | |
| | | </dependencies> |
| | | </project> |
New file |
| | |
| | | package com.ruoyi.common.redis.configure; |
| | | |
| | | import com.alibaba.fastjson2.JSON; |
| | | import com.alibaba.fastjson2.JSONReader; |
| | | import com.alibaba.fastjson2.JSONWriter; |
| | | import com.alibaba.fastjson2.filter.Filter; |
| | | import com.ruoyi.common.core.constant.Constants; |
| | | import org.springframework.data.redis.serializer.RedisSerializer; |
| | | import org.springframework.data.redis.serializer.SerializationException; |
| | | |
| | | import java.nio.charset.Charset; |
| | | |
| | | /** |
| | | * Redis使用FastJson序列化 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class FastJson2JsonRedisSerializer<T> implements RedisSerializer<T> { |
| | | public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); |
| | | |
| | | static final Filter AUTO_TYPE_FILTER = JSONReader.autoTypeFilter(Constants.JSON_WHITELIST_STR); |
| | | |
| | | private Class<T> clazz; |
| | | |
| | | public FastJson2JsonRedisSerializer(Class<T> clazz) { |
| | | super(); |
| | | this.clazz = clazz; |
| | | } |
| | | |
| | | @Override |
| | | public byte[] serialize(T t) throws SerializationException { |
| | | if (t == null) { |
| | | return new byte[0]; |
| | | } |
| | | return JSON.toJSONString(t, JSONWriter.Feature.WriteClassName).getBytes(DEFAULT_CHARSET); |
| | | } |
| | | |
| | | @Override |
| | | public T deserialize(byte[] bytes) throws SerializationException { |
| | | if (bytes == null || bytes.length <= 0) { |
| | | return null; |
| | | } |
| | | String str = new String(bytes, DEFAULT_CHARSET); |
| | | |
| | | return JSON.parseObject(str, clazz, AUTO_TYPE_FILTER); |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.redis.configure; |
| | | |
| | | import org.springframework.boot.autoconfigure.AutoConfigureBefore; |
| | | import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; |
| | | import org.springframework.cache.annotation.CachingConfigurerSupport; |
| | | import org.springframework.cache.annotation.EnableCaching; |
| | | import org.springframework.context.annotation.Bean; |
| | | import org.springframework.context.annotation.Configuration; |
| | | import org.springframework.data.redis.connection.RedisConnectionFactory; |
| | | import org.springframework.data.redis.core.RedisTemplate; |
| | | import org.springframework.data.redis.serializer.StringRedisSerializer; |
| | | |
| | | /** |
| | | * redis配置 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | @Configuration |
| | | @EnableCaching |
| | | @AutoConfigureBefore(RedisAutoConfiguration.class) |
| | | public class RedisConfig extends CachingConfigurerSupport { |
| | | @Bean |
| | | @SuppressWarnings(value = {"unchecked", "rawtypes"}) |
| | | public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) { |
| | | RedisTemplate<Object, Object> template = new RedisTemplate<>(); |
| | | template.setConnectionFactory(connectionFactory); |
| | | |
| | | FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class); |
| | | |
| | | // 使用StringRedisSerializer来序列化和反序列化redis的key值 |
| | | template.setKeySerializer(new StringRedisSerializer()); |
| | | template.setValueSerializer(serializer); |
| | | |
| | | // Hash的key也采用StringRedisSerializer的序列化方式 |
| | | template.setHashKeySerializer(new StringRedisSerializer()); |
| | | template.setHashValueSerializer(serializer); |
| | | |
| | | template.afterPropertiesSet(); |
| | | return template; |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.redis.service; |
| | | |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.data.redis.core.BoundSetOperations; |
| | | import org.springframework.data.redis.core.HashOperations; |
| | | import org.springframework.data.redis.core.RedisTemplate; |
| | | import org.springframework.data.redis.core.ValueOperations; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | import java.util.*; |
| | | import java.util.concurrent.TimeUnit; |
| | | |
| | | /** |
| | | * spring redis 工具类 |
| | | * |
| | | * @author ruoyi |
| | | **/ |
| | | @SuppressWarnings(value = {"unchecked", "rawtypes"}) |
| | | @Component |
| | | public class RedisService { |
| | | @Autowired |
| | | public RedisTemplate redisTemplate; |
| | | |
| | | /** |
| | | * 缓存基本的对象,Integer、String、实体类等 |
| | | * |
| | | * @param key 缓存的键值 |
| | | * @param value 缓存的值 |
| | | */ |
| | | public <T> void setCacheObject(final String key, final T value) { |
| | | redisTemplate.opsForValue().set(key, value); |
| | | } |
| | | |
| | | /** |
| | | * 缓存基本的对象,Integer、String、实体类等 |
| | | * |
| | | * @param key 缓存的键值 |
| | | * @param value 缓存的值 |
| | | * @param timeout 时间 |
| | | * @param timeUnit 时间颗粒度 |
| | | */ |
| | | public <T> void setCacheObject(final String key, final T value, final Long timeout, final TimeUnit timeUnit) { |
| | | redisTemplate.opsForValue().set(key, value, timeout, timeUnit); |
| | | } |
| | | |
| | | /** |
| | | * 设置有效时间 |
| | | * |
| | | * @param key Redis键 |
| | | * @param timeout 超时时间 |
| | | * @return true=设置成功;false=设置失败 |
| | | */ |
| | | public boolean expire(final String key, final long timeout) { |
| | | return expire(key, timeout, TimeUnit.SECONDS); |
| | | } |
| | | |
| | | /** |
| | | * 设置有效时间 |
| | | * |
| | | * @param key Redis键 |
| | | * @param timeout 超时时间 |
| | | * @param unit 时间单位 |
| | | * @return true=设置成功;false=设置失败 |
| | | */ |
| | | public boolean expire(final String key, final long timeout, final TimeUnit unit) { |
| | | return redisTemplate.expire(key, timeout, unit); |
| | | } |
| | | |
| | | /** |
| | | * 获取有效时间 |
| | | * |
| | | * @param key Redis键 |
| | | * @return 有效时间 |
| | | */ |
| | | public long getExpire(final String key) { |
| | | return redisTemplate.getExpire(key); |
| | | } |
| | | |
| | | /** |
| | | * 判断 key是否存在 |
| | | * |
| | | * @param key 键 |
| | | * @return true 存在 false不存在 |
| | | */ |
| | | public Boolean hasKey(String key) { |
| | | return redisTemplate.hasKey(key); |
| | | } |
| | | |
| | | /** |
| | | * 获得缓存的基本对象。 |
| | | * |
| | | * @param key 缓存键值 |
| | | * @return 缓存键值对应的数据 |
| | | */ |
| | | public <T> T getCacheObject(final String key) { |
| | | ValueOperations<String, T> operation = redisTemplate.opsForValue(); |
| | | return operation.get(key); |
| | | } |
| | | |
| | | /** |
| | | * 删除单个对象 |
| | | * |
| | | * @param key |
| | | */ |
| | | public boolean deleteObject(final String key) { |
| | | return redisTemplate.delete(key); |
| | | } |
| | | |
| | | /** |
| | | * 删除集合对象 |
| | | * |
| | | * @param collection 多个对象 |
| | | * @return |
| | | */ |
| | | public boolean deleteObject(final Collection collection) { |
| | | return redisTemplate.delete(collection) > 0; |
| | | } |
| | | |
| | | /** |
| | | * 缓存List数据 |
| | | * |
| | | * @param key 缓存的键值 |
| | | * @param dataList 待缓存的List数据 |
| | | * @return 缓存的对象 |
| | | */ |
| | | public <T> long setCacheList(final String key, final List<T> dataList) { |
| | | Long count = redisTemplate.opsForList().rightPushAll(key, dataList); |
| | | return count == null ? 0 : count; |
| | | } |
| | | |
| | | /** |
| | | * 获得缓存的list对象 |
| | | * |
| | | * @param key 缓存的键值 |
| | | * @return 缓存键值对应的数据 |
| | | */ |
| | | public <T> List<T> getCacheList(final String key) { |
| | | return redisTemplate.opsForList().range(key, 0, -1); |
| | | } |
| | | |
| | | /** |
| | | * 缓存Set |
| | | * |
| | | * @param key 缓存键值 |
| | | * @param dataSet 缓存的数据 |
| | | * @return 缓存数据的对象 |
| | | */ |
| | | public <T> BoundSetOperations<String, T> setCacheSet(final String key, final Set<T> dataSet) { |
| | | BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key); |
| | | Iterator<T> it = dataSet.iterator(); |
| | | while (it.hasNext()) { |
| | | setOperation.add(it.next()); |
| | | } |
| | | return setOperation; |
| | | } |
| | | |
| | | /** |
| | | * 获得缓存的set |
| | | * |
| | | * @param key |
| | | * @return |
| | | */ |
| | | public <T> Set<T> getCacheSet(final String key) { |
| | | return redisTemplate.opsForSet().members(key); |
| | | } |
| | | |
| | | /** |
| | | * 缓存Map |
| | | * |
| | | * @param key |
| | | * @param dataMap |
| | | */ |
| | | public <T> void setCacheMap(final String key, final Map<String, T> dataMap) { |
| | | if (dataMap != null) { |
| | | redisTemplate.opsForHash().putAll(key, dataMap); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 获得缓存的Map |
| | | * |
| | | * @param key |
| | | * @return |
| | | */ |
| | | public <T> Map<String, T> getCacheMap(final String key) { |
| | | return redisTemplate.opsForHash().entries(key); |
| | | } |
| | | |
| | | /** |
| | | * 往Hash中存入数据 |
| | | * |
| | | * @param key Redis键 |
| | | * @param hKey Hash键 |
| | | * @param value 值 |
| | | */ |
| | | public <T> void setCacheMapValue(final String key, final String hKey, final T value) { |
| | | redisTemplate.opsForHash().put(key, hKey, value); |
| | | } |
| | | |
| | | /** |
| | | * 获取Hash中的数据 |
| | | * |
| | | * @param key Redis键 |
| | | * @param hKey Hash键 |
| | | * @return Hash中的对象 |
| | | */ |
| | | public <T> T getCacheMapValue(final String key, final String hKey) { |
| | | HashOperations<String, String, T> opsForHash = redisTemplate.opsForHash(); |
| | | return opsForHash.get(key, hKey); |
| | | } |
| | | |
| | | /** |
| | | * 获取多个Hash中的数据 |
| | | * |
| | | * @param key Redis键 |
| | | * @param hKeys Hash键集合 |
| | | * @return Hash对象集合 |
| | | */ |
| | | public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys) { |
| | | return redisTemplate.opsForHash().multiGet(key, hKeys); |
| | | } |
| | | |
| | | /** |
| | | * 删除Hash中的某条数据 |
| | | * |
| | | * @param key Redis键 |
| | | * @param hKey Hash键 |
| | | * @return 是否成功 |
| | | */ |
| | | public boolean deleteCacheMapValue(final String key, final String hKey) { |
| | | return redisTemplate.opsForHash().delete(key, hKey) > 0; |
| | | } |
| | | |
| | | /** |
| | | * 获得缓存的基本对象列表 |
| | | * |
| | | * @param pattern 字符串前缀 |
| | | * @return 对象列表 |
| | | */ |
| | | public Collection<String> keys(final String pattern) { |
| | | return redisTemplate.keys(pattern); |
| | | } |
| | | } |
New file |
| | |
| | | com.ruoyi.common.redis.configure.RedisConfig |
| | | com.ruoyi.common.redis.service.RedisService |
New file |
| | |
| | | <?xml version="1.0" encoding="UTF-8"?> |
| | | <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
| | | xmlns="http://maven.apache.org/POM/4.0.0" |
| | | xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
| | | <parent> |
| | | <groupId>com.ruoyi</groupId> |
| | | <artifactId>ruoyi-common</artifactId> |
| | | <version>3.6.4</version> |
| | | </parent> |
| | | <modelVersion>4.0.0</modelVersion> |
| | | |
| | | <artifactId>ruoyi-common-seata</artifactId> |
| | | |
| | | <description> |
| | | ruoyi-common-seata分布式事务 |
| | | </description> |
| | | |
| | | <dependencies> |
| | | |
| | | <!-- SpringBoot Seata --> |
| | | <dependency> |
| | | <groupId>com.alibaba.cloud</groupId> |
| | | <artifactId>spring-cloud-starter-alibaba-seata</artifactId> |
| | | </dependency> |
| | | |
| | | </dependencies> |
| | | </project> |
New file |
| | |
| | | <?xml version="1.0" encoding="UTF-8"?> |
| | | <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" |
| | | xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> |
| | | <parent> |
| | | <groupId>com.ruoyi</groupId> |
| | | <artifactId>ruoyi-common</artifactId> |
| | | <version>3.6.4</version> |
| | | </parent> |
| | | <modelVersion>4.0.0</modelVersion> |
| | | |
| | | <artifactId>ruoyi-common-security</artifactId> |
| | | |
| | | <description> |
| | | ruoyi-common-security安全模块 |
| | | </description> |
| | | |
| | | <dependencies> |
| | | |
| | | <!-- Spring Web --> |
| | | <dependency> |
| | | <groupId>org.springframework</groupId> |
| | | <artifactId>spring-webmvc</artifactId> |
| | | </dependency> |
| | | |
| | | <!-- RuoYi Api System --> |
| | | <dependency> |
| | | <groupId>com.ruoyi</groupId> |
| | | <artifactId>ruoyi-api-system</artifactId> |
| | | </dependency> |
| | | |
| | | <!-- RuoYi Common Redis--> |
| | | <dependency> |
| | | <groupId>com.ruoyi</groupId> |
| | | <artifactId>ruoyi-common-redis</artifactId> |
| | | </dependency> |
| | | |
| | | </dependencies> |
| | | |
| | | </project> |
New file |
| | |
| | | package com.ruoyi.common.security.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 org.mybatis.spring.annotation.MapperScan; |
| | | import org.springframework.context.annotation.EnableAspectJAutoProxy; |
| | | import org.springframework.context.annotation.Import; |
| | | import org.springframework.scheduling.annotation.EnableAsync; |
| | | import com.ruoyi.common.security.config.ApplicationConfig; |
| | | import com.ruoyi.common.security.feign.FeignAutoConfiguration; |
| | | |
| | | @Target(ElementType.TYPE) |
| | | @Retention(RetentionPolicy.RUNTIME) |
| | | @Documented |
| | | @Inherited |
| | | // 表示通过aop框架暴露该代理对象,AopContext能够访问 |
| | | @EnableAspectJAutoProxy(exposeProxy = true) |
| | | // 指定要扫描的Mapper类的包的路径 |
| | | @MapperScan("com.ruoyi.**.mapper") |
| | | // 开启线程异步执行 |
| | | @EnableAsync |
| | | // 自动加载类 |
| | | @Import({ ApplicationConfig.class, FeignAutoConfiguration.class }) |
| | | public @interface EnableCustomConfig |
| | | { |
| | | |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.security.annotation; |
| | | |
| | | import org.springframework.cloud.openfeign.EnableFeignClients; |
| | | import java.lang.annotation.*; |
| | | |
| | | /** |
| | | * 自定义feign注解 |
| | | * 添加basePackages路径 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | @Target(ElementType.TYPE) |
| | | @Retention(RetentionPolicy.RUNTIME) |
| | | @Documented |
| | | @EnableFeignClients |
| | | public @interface EnableRyFeignClients |
| | | { |
| | | String[] value() default {}; |
| | | |
| | | String[] basePackages() default { "com.ruoyi" }; |
| | | |
| | | Class<?>[] basePackageClasses() default {}; |
| | | |
| | | Class<?>[] defaultConfiguration() default {}; |
| | | |
| | | Class<?>[] clients() default {}; |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.security.annotation; |
| | | |
| | | import java.lang.annotation.*; |
| | | |
| | | /** |
| | | * 内部认证注解 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | @Target(ElementType.METHOD) |
| | | @Retention(RetentionPolicy.RUNTIME) |
| | | @Documented |
| | | public @interface InnerAuth |
| | | { |
| | | /** |
| | | * 是否校验用户信息 |
| | | */ |
| | | boolean isUser() default false; |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.security.annotation; |
| | | |
| | | /** |
| | | * 权限注解的验证模式 |
| | | * |
| | | * @author ruoyi |
| | | * |
| | | */ |
| | | public enum Logical |
| | | { |
| | | /** |
| | | * 必须具有所有的元素 |
| | | */ |
| | | AND, |
| | | |
| | | /** |
| | | * 只需具有其中一个元素 |
| | | */ |
| | | OR |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.security.annotation; |
| | | |
| | | import java.lang.annotation.ElementType; |
| | | import java.lang.annotation.Retention; |
| | | import java.lang.annotation.RetentionPolicy; |
| | | import java.lang.annotation.Target; |
| | | |
| | | /** |
| | | * 登录认证:只有登录之后才能进入该方法 |
| | | * |
| | | * @author ruoyi |
| | | * |
| | | */ |
| | | @Retention(RetentionPolicy.RUNTIME) |
| | | @Target({ ElementType.METHOD, ElementType.TYPE }) |
| | | public @interface RequiresLogin |
| | | { |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.security.annotation; |
| | | |
| | | import java.lang.annotation.ElementType; |
| | | import java.lang.annotation.Retention; |
| | | import java.lang.annotation.RetentionPolicy; |
| | | import java.lang.annotation.Target; |
| | | |
| | | /** |
| | | * 权限认证:必须具有指定权限才能进入该方法 |
| | | * |
| | | * @author ruoyi |
| | | * |
| | | */ |
| | | @Retention(RetentionPolicy.RUNTIME) |
| | | @Target({ ElementType.METHOD, ElementType.TYPE }) |
| | | public @interface RequiresPermissions |
| | | { |
| | | /** |
| | | * 需要校验的权限码 |
| | | */ |
| | | String[] value() default {}; |
| | | |
| | | /** |
| | | * 验证模式:AND | OR,默认AND |
| | | */ |
| | | Logical logical() default Logical.AND; |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.security.annotation; |
| | | |
| | | import java.lang.annotation.ElementType; |
| | | import java.lang.annotation.Retention; |
| | | import java.lang.annotation.RetentionPolicy; |
| | | import java.lang.annotation.Target; |
| | | |
| | | /** |
| | | * 角色认证:必须具有指定角色标识才能进入该方法 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | @Retention(RetentionPolicy.RUNTIME) |
| | | @Target({ ElementType.METHOD, ElementType.TYPE }) |
| | | public @interface RequiresRoles |
| | | { |
| | | /** |
| | | * 需要校验的角色标识 |
| | | */ |
| | | String[] value() default {}; |
| | | |
| | | /** |
| | | * 验证逻辑:AND | OR,默认AND |
| | | */ |
| | | Logical logical() default Logical.AND; |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.security.aspect; |
| | | |
| | | import org.aspectj.lang.ProceedingJoinPoint; |
| | | import org.aspectj.lang.annotation.Around; |
| | | import org.aspectj.lang.annotation.Aspect; |
| | | import org.springframework.core.Ordered; |
| | | import org.springframework.stereotype.Component; |
| | | import com.ruoyi.common.core.constant.SecurityConstants; |
| | | import com.ruoyi.common.core.exception.InnerAuthException; |
| | | import com.ruoyi.common.core.utils.ServletUtils; |
| | | import com.ruoyi.common.core.utils.StringUtils; |
| | | import com.ruoyi.common.security.annotation.InnerAuth; |
| | | |
| | | /** |
| | | * 内部服务调用验证处理 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | @Aspect |
| | | @Component |
| | | public class InnerAuthAspect implements Ordered |
| | | { |
| | | @Around("@annotation(innerAuth)") |
| | | public Object innerAround(ProceedingJoinPoint point, InnerAuth innerAuth) throws Throwable |
| | | { |
| | | String source = ServletUtils.getRequest().getHeader(SecurityConstants.FROM_SOURCE); |
| | | // 内部请求验证 |
| | | if (!StringUtils.equals(SecurityConstants.INNER, source)) |
| | | { |
| | | throw new InnerAuthException("没有内部访问权限,不允许访问"); |
| | | } |
| | | |
| | | String userid = ServletUtils.getRequest().getHeader(SecurityConstants.DETAILS_USER_ID); |
| | | String username = ServletUtils.getRequest().getHeader(SecurityConstants.DETAILS_USERNAME); |
| | | // 用户信息验证 |
| | | if (innerAuth.isUser() && (StringUtils.isEmpty(userid) || StringUtils.isEmpty(username))) |
| | | { |
| | | throw new InnerAuthException("没有设置用户信息,不允许访问 "); |
| | | } |
| | | return point.proceed(); |
| | | } |
| | | |
| | | /** |
| | | * 确保在权限认证aop执行前执行 |
| | | */ |
| | | @Override |
| | | public int getOrder() |
| | | { |
| | | return Ordered.HIGHEST_PRECEDENCE + 1; |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.security.aspect; |
| | | |
| | | import java.lang.reflect.Method; |
| | | import org.aspectj.lang.ProceedingJoinPoint; |
| | | import org.aspectj.lang.annotation.Around; |
| | | import org.aspectj.lang.annotation.Aspect; |
| | | import org.aspectj.lang.annotation.Pointcut; |
| | | import org.aspectj.lang.reflect.MethodSignature; |
| | | import org.springframework.stereotype.Component; |
| | | import com.ruoyi.common.security.annotation.RequiresLogin; |
| | | import com.ruoyi.common.security.annotation.RequiresPermissions; |
| | | import com.ruoyi.common.security.annotation.RequiresRoles; |
| | | import com.ruoyi.common.security.auth.AuthUtil; |
| | | |
| | | /** |
| | | * 基于 Spring Aop 的注解鉴权 |
| | | * |
| | | * @author kong |
| | | */ |
| | | @Aspect |
| | | @Component |
| | | public class PreAuthorizeAspect |
| | | { |
| | | /** |
| | | * 构建 |
| | | */ |
| | | public PreAuthorizeAspect() |
| | | { |
| | | } |
| | | |
| | | /** |
| | | * 定义AOP签名 (切入所有使用鉴权注解的方法) |
| | | */ |
| | | public static final String POINTCUT_SIGN = " @annotation(com.ruoyi.common.security.annotation.RequiresLogin) || " |
| | | + "@annotation(com.ruoyi.common.security.annotation.RequiresPermissions) || " |
| | | + "@annotation(com.ruoyi.common.security.annotation.RequiresRoles)"; |
| | | |
| | | /** |
| | | * 声明AOP签名 |
| | | */ |
| | | @Pointcut(POINTCUT_SIGN) |
| | | public void pointcut() |
| | | { |
| | | } |
| | | |
| | | /** |
| | | * 环绕切入 |
| | | * |
| | | * @param joinPoint 切面对象 |
| | | * @return 底层方法执行后的返回值 |
| | | * @throws Throwable 底层方法抛出的异常 |
| | | */ |
| | | @Around("pointcut()") |
| | | public Object around(ProceedingJoinPoint joinPoint) throws Throwable |
| | | { |
| | | // 注解鉴权 |
| | | MethodSignature signature = (MethodSignature) joinPoint.getSignature(); |
| | | checkMethodAnnotation(signature.getMethod()); |
| | | try |
| | | { |
| | | // 执行原有逻辑 |
| | | Object obj = joinPoint.proceed(); |
| | | return obj; |
| | | } |
| | | catch (Throwable e) |
| | | { |
| | | throw e; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 对一个Method对象进行注解检查 |
| | | */ |
| | | public void checkMethodAnnotation(Method method) |
| | | { |
| | | // 校验 @RequiresLogin 注解 |
| | | RequiresLogin requiresLogin = method.getAnnotation(RequiresLogin.class); |
| | | if (requiresLogin != null) |
| | | { |
| | | AuthUtil.checkLogin(); |
| | | } |
| | | |
| | | // 校验 @RequiresRoles 注解 |
| | | RequiresRoles requiresRoles = method.getAnnotation(RequiresRoles.class); |
| | | if (requiresRoles != null) |
| | | { |
| | | AuthUtil.checkRole(requiresRoles); |
| | | } |
| | | |
| | | // 校验 @RequiresPermissions 注解 |
| | | RequiresPermissions requiresPermissions = method.getAnnotation(RequiresPermissions.class); |
| | | if (requiresPermissions != null) |
| | | { |
| | | AuthUtil.checkPermi(requiresPermissions); |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.security.auth; |
| | | |
| | | import com.ruoyi.common.core.context.SecurityContextHolder; |
| | | import com.ruoyi.common.core.exception.auth.NotLoginException; |
| | | import com.ruoyi.common.core.exception.auth.NotPermissionException; |
| | | import com.ruoyi.common.core.exception.auth.NotRoleException; |
| | | import com.ruoyi.common.core.utils.SpringUtils; |
| | | import com.ruoyi.common.core.utils.StringUtils; |
| | | import com.ruoyi.common.security.annotation.Logical; |
| | | import com.ruoyi.common.security.annotation.RequiresLogin; |
| | | import com.ruoyi.common.security.annotation.RequiresPermissions; |
| | | import com.ruoyi.common.security.annotation.RequiresRoles; |
| | | import com.ruoyi.common.security.service.TokenService; |
| | | import com.ruoyi.common.security.utils.SecurityUtils; |
| | | import com.ruoyi.system.api.model.LoginUser; |
| | | import org.springframework.util.PatternMatchUtils; |
| | | |
| | | import java.util.Collection; |
| | | import java.util.HashSet; |
| | | import java.util.Set; |
| | | |
| | | /** |
| | | * Token 权限验证,逻辑实现类 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class AuthLogic { |
| | | /** |
| | | * 所有权限标识 |
| | | */ |
| | | private static final String ALL_PERMISSION = "*:*:*"; |
| | | |
| | | /** |
| | | * 管理员角色权限标识 |
| | | */ |
| | | private static final String SUPER_ADMIN = "admin"; |
| | | |
| | | public TokenService tokenService = SpringUtils.getBean(TokenService.class); |
| | | |
| | | /** |
| | | * 会话注销 |
| | | */ |
| | | public void logout() { |
| | | String token = SecurityUtils.getToken(); |
| | | if (token == null) { |
| | | return; |
| | | } |
| | | logoutByToken(token); |
| | | } |
| | | |
| | | /** |
| | | * 会话注销,根据指定Token |
| | | */ |
| | | public void logoutByToken(String token) { |
| | | tokenService.delLoginUser(token); |
| | | } |
| | | |
| | | /** |
| | | * 学习端会话注销,根据指定Token |
| | | */ |
| | | public void logoutByTokenWorker(String token) { |
| | | tokenService.delLoginWorker(token); |
| | | } |
| | | |
| | | /** |
| | | * 检验用户是否已经登录,如未登录,则抛出异常 |
| | | */ |
| | | public void checkLogin() { |
| | | getLoginUser(); |
| | | } |
| | | |
| | | /** |
| | | * 获取当前用户缓存信息, 如果未登录,则抛出异常 |
| | | * |
| | | * @return 用户缓存信息 |
| | | */ |
| | | public LoginUser getLoginUser() { |
| | | String token = SecurityUtils.getToken(); |
| | | if (token == null) { |
| | | throw new NotLoginException("未提供token"); |
| | | } |
| | | LoginUser loginUser = SecurityUtils.getLoginUser(); |
| | | if (loginUser == null) { |
| | | throw new NotLoginException("无效的token"); |
| | | } |
| | | return loginUser; |
| | | } |
| | | |
| | | /** |
| | | * 获取当前用户缓存信息, 如果未登录,则抛出异常 |
| | | * |
| | | * @param token 前端传递的认证信息 |
| | | * @return 用户缓存信息 |
| | | */ |
| | | public LoginUser getLoginUser(String token) { |
| | | return tokenService.getLoginUser(token); |
| | | } |
| | | |
| | | /** |
| | | * 验证当前用户有效期, 如果相差不足120分钟,自动刷新缓存 |
| | | * |
| | | * @param loginUser 当前用户信息 |
| | | */ |
| | | public void verifyLoginUserExpire(LoginUser loginUser) { |
| | | tokenService.verifyToken(loginUser); |
| | | } |
| | | |
| | | /** |
| | | * 验证用户是否具备某权限 |
| | | * |
| | | * @param permission 权限字符串 |
| | | * @return 用户是否具备某权限 |
| | | */ |
| | | public boolean hasPermi(String permission) { |
| | | return hasPermi(getPermiList(), permission); |
| | | } |
| | | |
| | | /** |
| | | * 验证用户是否具备某权限, 如果验证未通过,则抛出异常: NotPermissionException |
| | | * |
| | | * @param permission 权限字符串 |
| | | * @return 用户是否具备某权限 |
| | | */ |
| | | public void checkPermi(String permission) { |
| | | if (!hasPermi(getPermiList(), permission)) { |
| | | throw new NotPermissionException(permission); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 根据注解(@RequiresPermissions)鉴权, 如果验证未通过,则抛出异常: NotPermissionException |
| | | * |
| | | * @param requiresPermissions 注解对象 |
| | | */ |
| | | public void checkPermi(RequiresPermissions requiresPermissions) { |
| | | SecurityContextHolder.setPermission(StringUtils.join(requiresPermissions.value(), ",")); |
| | | if (requiresPermissions.logical() == Logical.AND) { |
| | | checkPermiAnd(requiresPermissions.value()); |
| | | } else { |
| | | checkPermiOr(requiresPermissions.value()); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 验证用户是否含有指定权限,必须全部拥有 |
| | | * |
| | | * @param permissions 权限列表 |
| | | */ |
| | | public void checkPermiAnd(String... permissions) { |
| | | Set<String> permissionList = getPermiList(); |
| | | for (String permission : permissions) { |
| | | if (!hasPermi(permissionList, permission)) { |
| | | throw new NotPermissionException(permission); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 验证用户是否含有指定权限,只需包含其中一个 |
| | | * |
| | | * @param permissions 权限码数组 |
| | | */ |
| | | public void checkPermiOr(String... permissions) { |
| | | Set<String> permissionList = getPermiList(); |
| | | for (String permission : permissions) { |
| | | if (hasPermi(permissionList, permission)) { |
| | | return; |
| | | } |
| | | } |
| | | if (permissions.length > 0) { |
| | | throw new NotPermissionException(permissions); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 判断用户是否拥有某个角色 |
| | | * |
| | | * @param role 角色标识 |
| | | * @return 用户是否具备某角色 |
| | | */ |
| | | public boolean hasRole(String role) { |
| | | return hasRole(getRoleList(), role); |
| | | } |
| | | |
| | | /** |
| | | * 判断用户是否拥有某个角色, 如果验证未通过,则抛出异常: NotRoleException |
| | | * |
| | | * @param role 角色标识 |
| | | */ |
| | | public void checkRole(String role) { |
| | | if (!hasRole(role)) { |
| | | throw new NotRoleException(role); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 根据注解(@RequiresRoles)鉴权 |
| | | * |
| | | * @param requiresRoles 注解对象 |
| | | */ |
| | | public void checkRole(RequiresRoles requiresRoles) { |
| | | if (requiresRoles.logical() == Logical.AND) { |
| | | checkRoleAnd(requiresRoles.value()); |
| | | } else { |
| | | checkRoleOr(requiresRoles.value()); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 验证用户是否含有指定角色,必须全部拥有 |
| | | * |
| | | * @param roles 角色标识数组 |
| | | */ |
| | | public void checkRoleAnd(String... roles) { |
| | | String roleStr = getRoleList(); |
| | | for (String role : roles) { |
| | | if (!hasRole(roleStr, role)) { |
| | | throw new NotRoleException(role); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 验证用户是否含有指定角色,只需包含其中一个 |
| | | * |
| | | * @param roles 角色标识数组 |
| | | */ |
| | | public void checkRoleOr(String... roles) { |
| | | String roleStr = getRoleList(); |
| | | for (String role : roles) { |
| | | if (hasRole(roleStr, role)) { |
| | | return; |
| | | } |
| | | } |
| | | if (roles.length > 0) { |
| | | throw new NotRoleException(roles); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 根据注解(@RequiresLogin)鉴权 |
| | | * |
| | | * @param at 注解对象 |
| | | */ |
| | | public void checkByAnnotation(RequiresLogin at) { |
| | | this.checkLogin(); |
| | | } |
| | | |
| | | /** |
| | | * 根据注解(@RequiresRoles)鉴权 |
| | | * |
| | | * @param at 注解对象 |
| | | */ |
| | | public void checkByAnnotation(RequiresRoles at) { |
| | | String[] roleArray = at.value(); |
| | | if (at.logical() == Logical.AND) { |
| | | this.checkRoleAnd(roleArray); |
| | | } else { |
| | | this.checkRoleOr(roleArray); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 根据注解(@RequiresPermissions)鉴权 |
| | | * |
| | | * @param at 注解对象 |
| | | */ |
| | | public void checkByAnnotation(RequiresPermissions at) { |
| | | String[] permissionArray = at.value(); |
| | | if (at.logical() == Logical.AND) { |
| | | this.checkPermiAnd(permissionArray); |
| | | } else { |
| | | this.checkPermiOr(permissionArray); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 获取当前账号的角色列表 |
| | | * |
| | | * @return 角色列表 |
| | | */ |
| | | public String getRoleList() { |
| | | try { |
| | | LoginUser loginUser = getLoginUser(); |
| | | return loginUser.getRole(); |
| | | } catch (Exception e) { |
| | | return ""; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 获取当前账号的权限列表 |
| | | * |
| | | * @return 权限列表 |
| | | */ |
| | | public Set<String> getPermiList() { |
| | | try { |
| | | LoginUser loginUser = getLoginUser(); |
| | | return loginUser.getPermissions(); |
| | | } catch (Exception e) { |
| | | return new HashSet<>(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 判断是否包含权限 |
| | | * |
| | | * @param authorities 权限列表 |
| | | * @param permission 权限字符串 |
| | | * @return 用户是否具备某权限 |
| | | */ |
| | | public boolean hasPermi(Collection<String> authorities, String permission) { |
| | | return authorities.stream().filter(StringUtils::hasText) |
| | | .anyMatch(x -> ALL_PERMISSION.equals(x) || PatternMatchUtils.simpleMatch(x, permission)); |
| | | } |
| | | |
| | | /** |
| | | * 判断是否包含角色 |
| | | * |
| | | * @param roleStr 角色列表 |
| | | * @param role 角色 |
| | | * @return 用户是否具备某角色权限 |
| | | */ |
| | | public boolean hasRole(String roleStr, String role) { |
| | | return roleStr.equals(role); |
| | | } |
| | | |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.security.auth; |
| | | |
| | | import com.ruoyi.common.security.annotation.RequiresPermissions; |
| | | import com.ruoyi.common.security.annotation.RequiresRoles; |
| | | import com.ruoyi.system.api.model.LoginUser; |
| | | |
| | | /** |
| | | * Token 权限验证工具类 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class AuthUtil { |
| | | /** |
| | | * 底层的 AuthLogic 对象 |
| | | */ |
| | | public static AuthLogic authLogic = new AuthLogic(); |
| | | |
| | | /** |
| | | * 会话注销 |
| | | */ |
| | | public static void logout() { |
| | | authLogic.logout(); |
| | | } |
| | | |
| | | /** |
| | | * 会话注销,根据指定Token |
| | | * |
| | | * @param token 指定token |
| | | */ |
| | | public static void logoutByToken(String token) { |
| | | authLogic.logoutByToken(token); |
| | | } |
| | | |
| | | /** |
| | | * 师傅端会话注销,根据指定Token |
| | | * |
| | | * @param token 指定token |
| | | */ |
| | | public static void logoutByTokenWorker(String token) { |
| | | authLogic.logoutByTokenWorker(token); |
| | | } |
| | | |
| | | /** |
| | | * 检验当前会话是否已经登录,如未登录,则抛出异常 |
| | | */ |
| | | public static void checkLogin() { |
| | | authLogic.checkLogin(); |
| | | } |
| | | |
| | | /** |
| | | * 获取当前登录用户信息 |
| | | * |
| | | * @param token 指定token |
| | | * @return 用户信息 |
| | | */ |
| | | public static LoginUser getLoginUser(String token) { |
| | | return authLogic.getLoginUser(token); |
| | | } |
| | | |
| | | /** |
| | | * 验证当前用户有效期 |
| | | * |
| | | * @param loginUser 用户信息 |
| | | */ |
| | | public static void verifyLoginUserExpire(LoginUser loginUser) { |
| | | authLogic.verifyLoginUserExpire(loginUser); |
| | | } |
| | | |
| | | /** |
| | | * 当前账号是否含有指定角色标识, 返回true或false |
| | | * |
| | | * @param role 角色标识 |
| | | * @return 是否含有指定角色标识 |
| | | */ |
| | | public static boolean hasRole(String role) { |
| | | return authLogic.hasRole(role); |
| | | } |
| | | |
| | | /** |
| | | * 当前账号是否含有指定角色标识, 如果验证未通过,则抛出异常: NotRoleException |
| | | * |
| | | * @param role 角色标识 |
| | | */ |
| | | public static void checkRole(String role) { |
| | | authLogic.checkRole(role); |
| | | } |
| | | |
| | | /** |
| | | * 根据注解传入参数鉴权, 如果验证未通过,则抛出异常: NotRoleException |
| | | * |
| | | * @param requiresRoles 角色权限注解 |
| | | */ |
| | | public static void checkRole(RequiresRoles requiresRoles) { |
| | | authLogic.checkRole(requiresRoles); |
| | | } |
| | | |
| | | /** |
| | | * 当前账号是否含有指定角色标识 [指定多个,必须全部验证通过] |
| | | * |
| | | * @param roles 角色标识数组 |
| | | */ |
| | | public static void checkRoleAnd(String... roles) { |
| | | authLogic.checkRoleAnd(roles); |
| | | } |
| | | |
| | | /** |
| | | * 当前账号是否含有指定角色标识 [指定多个,只要其一验证通过即可] |
| | | * |
| | | * @param roles 角色标识数组 |
| | | */ |
| | | public static void checkRoleOr(String... roles) { |
| | | authLogic.checkRoleOr(roles); |
| | | } |
| | | |
| | | /** |
| | | * 当前账号是否含有指定权限, 返回true或false |
| | | * |
| | | * @param permission 权限码 |
| | | * @return 是否含有指定权限 |
| | | */ |
| | | public static boolean hasPermi(String permission) { |
| | | return authLogic.hasPermi(permission); |
| | | } |
| | | |
| | | /** |
| | | * 当前账号是否含有指定权限, 如果验证未通过,则抛出异常: NotPermissionException |
| | | * |
| | | * @param permission 权限码 |
| | | */ |
| | | public static void checkPermi(String permission) { |
| | | authLogic.checkPermi(permission); |
| | | } |
| | | |
| | | /** |
| | | * 根据注解传入参数鉴权, 如果验证未通过,则抛出异常: NotPermissionException |
| | | * |
| | | * @param requiresPermissions 权限注解 |
| | | */ |
| | | public static void checkPermi(RequiresPermissions requiresPermissions) { |
| | | authLogic.checkPermi(requiresPermissions); |
| | | } |
| | | |
| | | /** |
| | | * 当前账号是否含有指定权限 [指定多个,必须全部验证通过] |
| | | * |
| | | * @param permissions 权限码数组 |
| | | */ |
| | | public static void checkPermiAnd(String... permissions) { |
| | | authLogic.checkPermiAnd(permissions); |
| | | } |
| | | |
| | | /** |
| | | * 当前账号是否含有指定权限 [指定多个,只要其一验证通过即可] |
| | | * |
| | | * @param permissions 权限码数组 |
| | | */ |
| | | public static void checkPermiOr(String... permissions) { |
| | | authLogic.checkPermiOr(permissions); |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.security.config; |
| | | |
| | | import java.util.TimeZone; |
| | | import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; |
| | | import org.springframework.context.annotation.Bean; |
| | | |
| | | /** |
| | | * 系统配置 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class ApplicationConfig |
| | | { |
| | | /** |
| | | * 时区配置 |
| | | */ |
| | | @Bean |
| | | public Jackson2ObjectMapperBuilderCustomizer jacksonObjectMapperCustomization() |
| | | { |
| | | return jacksonObjectMapperBuilder -> jacksonObjectMapperBuilder.timeZone(TimeZone.getDefault()); |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.security.config; |
| | | |
| | | import org.springframework.web.servlet.config.annotation.InterceptorRegistry; |
| | | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; |
| | | import com.ruoyi.common.security.interceptor.HeaderInterceptor; |
| | | |
| | | /** |
| | | * 拦截器配置 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class WebMvcConfig implements WebMvcConfigurer |
| | | { |
| | | /** 不需要拦截地址 */ |
| | | public static final String[] excludeUrls = { "/login", "/logout", "/refresh" }; |
| | | |
| | | @Override |
| | | public void addInterceptors(InterceptorRegistry registry) |
| | | { |
| | | registry.addInterceptor(getHeaderInterceptor()) |
| | | .addPathPatterns("/**") |
| | | .excludePathPatterns(excludeUrls) |
| | | .order(-10); |
| | | } |
| | | |
| | | /** |
| | | * 自定义请求头拦截器 |
| | | */ |
| | | public HeaderInterceptor getHeaderInterceptor() |
| | | { |
| | | return new HeaderInterceptor(); |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.security.feign; |
| | | |
| | | import org.springframework.context.annotation.Bean; |
| | | import org.springframework.context.annotation.Configuration; |
| | | import feign.RequestInterceptor; |
| | | |
| | | /** |
| | | * Feign 配置注册 |
| | | * |
| | | * @author ruoyi |
| | | **/ |
| | | @Configuration |
| | | public class FeignAutoConfiguration |
| | | { |
| | | @Bean |
| | | public RequestInterceptor requestInterceptor() |
| | | { |
| | | return new FeignRequestInterceptor(); |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.security.feign; |
| | | |
| | | import java.util.Map; |
| | | import javax.servlet.http.HttpServletRequest; |
| | | import org.springframework.stereotype.Component; |
| | | import com.ruoyi.common.core.constant.SecurityConstants; |
| | | import com.ruoyi.common.core.utils.ServletUtils; |
| | | import com.ruoyi.common.core.utils.StringUtils; |
| | | import com.ruoyi.common.core.utils.ip.IpUtils; |
| | | import feign.RequestInterceptor; |
| | | import feign.RequestTemplate; |
| | | |
| | | /** |
| | | * feign 请求拦截器 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | @Component |
| | | public class FeignRequestInterceptor implements RequestInterceptor |
| | | { |
| | | @Override |
| | | public void apply(RequestTemplate requestTemplate) |
| | | { |
| | | HttpServletRequest httpServletRequest = ServletUtils.getRequest(); |
| | | if (StringUtils.isNotNull(httpServletRequest)) |
| | | { |
| | | Map<String, String> headers = ServletUtils.getHeaders(httpServletRequest); |
| | | // 传递用户信息请求头,防止丢失 |
| | | String userId = headers.get(SecurityConstants.DETAILS_USER_ID); |
| | | if (StringUtils.isNotEmpty(userId)) |
| | | { |
| | | requestTemplate.header(SecurityConstants.DETAILS_USER_ID, userId); |
| | | } |
| | | String userKey = headers.get(SecurityConstants.USER_KEY); |
| | | if (StringUtils.isNotEmpty(userKey)) |
| | | { |
| | | requestTemplate.header(SecurityConstants.USER_KEY, userKey); |
| | | } |
| | | String userName = headers.get(SecurityConstants.DETAILS_USERNAME); |
| | | if (StringUtils.isNotEmpty(userName)) |
| | | { |
| | | requestTemplate.header(SecurityConstants.DETAILS_USERNAME, userName); |
| | | } |
| | | String authentication = headers.get(SecurityConstants.AUTHORIZATION_HEADER); |
| | | if (StringUtils.isNotEmpty(authentication)) |
| | | { |
| | | requestTemplate.header(SecurityConstants.AUTHORIZATION_HEADER, authentication); |
| | | } |
| | | |
| | | // 配置客户端IP |
| | | requestTemplate.header("X-Forwarded-For", IpUtils.getIpAddr()); |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.security.handler; |
| | | |
| | | import com.ruoyi.common.core.constant.HttpStatus; |
| | | import com.ruoyi.common.core.domain.R; |
| | | import com.ruoyi.common.core.exception.DemoModeException; |
| | | import com.ruoyi.common.core.exception.GlobalException; |
| | | import com.ruoyi.common.core.exception.InnerAuthException; |
| | | import com.ruoyi.common.core.exception.ServiceException; |
| | | import com.ruoyi.common.core.exception.auth.NotPermissionException; |
| | | import com.ruoyi.common.core.exception.auth.NotRoleException; |
| | | import com.ruoyi.common.core.utils.StringUtils; |
| | | import com.ruoyi.common.core.web.domain.AjaxResult; |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | import org.springframework.validation.BindException; |
| | | import org.springframework.web.HttpRequestMethodNotSupportedException; |
| | | import org.springframework.web.bind.MethodArgumentNotValidException; |
| | | import org.springframework.web.bind.MissingPathVariableException; |
| | | import org.springframework.web.bind.annotation.ExceptionHandler; |
| | | import org.springframework.web.bind.annotation.RestControllerAdvice; |
| | | import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; |
| | | |
| | | import javax.servlet.http.HttpServletRequest; |
| | | |
| | | /** |
| | | * 全局异常处理器 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | @RestControllerAdvice |
| | | public class GlobalExceptionHandler { |
| | | private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class); |
| | | |
| | | /** |
| | | * 权限码异常 |
| | | */ |
| | | @ExceptionHandler(NotPermissionException.class) |
| | | public AjaxResult handleNotPermissionException(NotPermissionException e, HttpServletRequest request) { |
| | | String requestURI = request.getRequestURI(); |
| | | log.error("请求地址'{}',权限码校验失败'{}'", requestURI, e.getMessage()); |
| | | return AjaxResult.error(HttpStatus.FORBIDDEN, "没有访问权限,请联系管理员授权"); |
| | | } |
| | | |
| | | /** |
| | | * 角色权限异常 |
| | | */ |
| | | @ExceptionHandler(NotRoleException.class) |
| | | public AjaxResult handleNotRoleException(NotRoleException e, HttpServletRequest request) { |
| | | String requestURI = request.getRequestURI(); |
| | | log.error("请求地址'{}',角色权限校验失败'{}'", requestURI, e.getMessage()); |
| | | return AjaxResult.error(HttpStatus.FORBIDDEN, "没有访问权限,请联系管理员授权"); |
| | | } |
| | | |
| | | /** |
| | | * 请求方式不支持 |
| | | */ |
| | | @ExceptionHandler(HttpRequestMethodNotSupportedException.class) |
| | | public AjaxResult handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e, HttpServletRequest request) { |
| | | String requestURI = request.getRequestURI(); |
| | | log.error("请求地址'{}',不支持'{}'请求", requestURI, e.getMethod()); |
| | | return AjaxResult.error(e.getMessage()); |
| | | } |
| | | |
| | | /** |
| | | * 业务异常 |
| | | */ |
| | | @ExceptionHandler(ServiceException.class) |
| | | public AjaxResult handleServiceException(ServiceException e, HttpServletRequest request) { |
| | | log.error(e.getMessage(), e); |
| | | Integer code = e.getCode(); |
| | | return StringUtils.isNotNull(code) ? AjaxResult.error(code, e.getMessage()) : AjaxResult.error(e.getMessage()); |
| | | } |
| | | |
| | | /** |
| | | * 请求路径中缺少必需的路径变量 |
| | | */ |
| | | @ExceptionHandler(MissingPathVariableException.class) |
| | | public AjaxResult handleMissingPathVariableException(MissingPathVariableException e, HttpServletRequest request) { |
| | | String requestURI = request.getRequestURI(); |
| | | log.error("请求路径中缺少必需的路径变量'{}',发生系统异常.", requestURI, e); |
| | | return AjaxResult.error(String.format("请求路径中缺少必需的路径变量[%s]", e.getVariableName())); |
| | | } |
| | | |
| | | /** |
| | | * 请求参数类型不匹配 |
| | | */ |
| | | @ExceptionHandler(MethodArgumentTypeMismatchException.class) |
| | | public AjaxResult handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e, HttpServletRequest request) { |
| | | String requestURI = request.getRequestURI(); |
| | | log.error("请求参数类型不匹配'{}',发生系统异常.", requestURI, e); |
| | | return AjaxResult.error(String.format("请求参数类型不匹配,参数[%s]要求类型为:'%s',但输入值为:'%s'", e.getName(), e.getRequiredType().getName(), e.getValue())); |
| | | } |
| | | |
| | | /** |
| | | * 拦截未知的运行时异常 |
| | | */ |
| | | @ExceptionHandler(RuntimeException.class) |
| | | public AjaxResult handleRuntimeException(RuntimeException e, HttpServletRequest request) { |
| | | String requestURI = request.getRequestURI(); |
| | | log.error("请求地址'{}',发生未知异常.", requestURI, e); |
| | | return AjaxResult.error(e.getMessage()); |
| | | } |
| | | |
| | | /** |
| | | * 系统异常 |
| | | */ |
| | | @ExceptionHandler(Exception.class) |
| | | public AjaxResult handleException(Exception e, HttpServletRequest request) { |
| | | String requestURI = request.getRequestURI(); |
| | | log.error("请求地址'{}',发生系统异常.", requestURI, e); |
| | | return AjaxResult.error(e.getMessage()); |
| | | } |
| | | |
| | | /** |
| | | * 自定义验证异常 |
| | | */ |
| | | @ExceptionHandler(BindException.class) |
| | | public AjaxResult handleBindException(BindException e) { |
| | | log.error(e.getMessage(), e); |
| | | String message = e.getAllErrors().get(0).getDefaultMessage(); |
| | | return AjaxResult.error(message); |
| | | } |
| | | |
| | | /** |
| | | * 自定义验证异常 |
| | | */ |
| | | @ExceptionHandler(MethodArgumentNotValidException.class) |
| | | public Object handleMethodArgumentNotValidException(MethodArgumentNotValidException e) { |
| | | log.error(e.getMessage(), e); |
| | | String message = e.getBindingResult().getFieldError().getDefaultMessage(); |
| | | return AjaxResult.error(message); |
| | | } |
| | | |
| | | /** |
| | | * 内部认证异常 |
| | | */ |
| | | @ExceptionHandler(InnerAuthException.class) |
| | | public AjaxResult handleInnerAuthException(InnerAuthException e) { |
| | | return AjaxResult.error(e.getMessage()); |
| | | } |
| | | |
| | | /** |
| | | * 演示模式异常 |
| | | */ |
| | | @ExceptionHandler(DemoModeException.class) |
| | | public AjaxResult handleDemoModeException(DemoModeException e) { |
| | | return AjaxResult.error("演示模式,不允许操作"); |
| | | } |
| | | |
| | | /** |
| | | * 演示模式异常 |
| | | */ |
| | | @ExceptionHandler(GlobalException.class) |
| | | public R<String> handleDemoModeException(GlobalException g) { |
| | | return R.fail(g.getDetailMessage()); |
| | | } |
| | | |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.security.interceptor; |
| | | |
| | | import javax.servlet.http.HttpServletRequest; |
| | | import javax.servlet.http.HttpServletResponse; |
| | | import org.springframework.web.method.HandlerMethod; |
| | | import org.springframework.web.servlet.AsyncHandlerInterceptor; |
| | | import com.ruoyi.common.core.constant.SecurityConstants; |
| | | import com.ruoyi.common.core.context.SecurityContextHolder; |
| | | import com.ruoyi.common.core.utils.ServletUtils; |
| | | import com.ruoyi.common.core.utils.StringUtils; |
| | | import com.ruoyi.common.security.auth.AuthUtil; |
| | | import com.ruoyi.common.security.utils.SecurityUtils; |
| | | import com.ruoyi.system.api.model.LoginUser; |
| | | |
| | | /** |
| | | * 自定义请求头拦截器,将Header数据封装到线程变量中方便获取 |
| | | * 注意:此拦截器会同时验证当前用户有效期自动刷新有效期 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class HeaderInterceptor implements AsyncHandlerInterceptor |
| | | { |
| | | @Override |
| | | public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception |
| | | { |
| | | if (!(handler instanceof HandlerMethod)) |
| | | { |
| | | return true; |
| | | } |
| | | |
| | | SecurityContextHolder.setUserId(ServletUtils.getHeader(request, SecurityConstants.DETAILS_USER_ID)); |
| | | SecurityContextHolder.setUserName(ServletUtils.getHeader(request, SecurityConstants.DETAILS_USERNAME)); |
| | | SecurityContextHolder.setUserKey(ServletUtils.getHeader(request, SecurityConstants.USER_KEY)); |
| | | |
| | | String token = SecurityUtils.getToken(); |
| | | if (StringUtils.isNotEmpty(token)) |
| | | { |
| | | LoginUser loginUser = AuthUtil.getLoginUser(token); |
| | | if (StringUtils.isNotNull(loginUser)) |
| | | { |
| | | AuthUtil.verifyLoginUserExpire(loginUser); |
| | | SecurityContextHolder.set(SecurityConstants.LOGIN_USER, loginUser); |
| | | } |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | @Override |
| | | public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) |
| | | throws Exception |
| | | { |
| | | SecurityContextHolder.remove(); |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.security.service; |
| | | |
| | | import com.ruoyi.common.core.constant.CacheConstants; |
| | | import com.ruoyi.common.core.constant.SecurityConstants; |
| | | import com.ruoyi.common.core.utils.JwtUtils; |
| | | import com.ruoyi.common.core.utils.ServletUtils; |
| | | import com.ruoyi.common.core.utils.StringUtils; |
| | | import com.ruoyi.common.core.utils.ip.IpUtils; |
| | | import com.ruoyi.common.core.utils.uuid.IdUtils; |
| | | import com.ruoyi.common.redis.service.RedisService; |
| | | import com.ruoyi.common.security.utils.SecurityUtils; |
| | | import com.ruoyi.system.api.model.LoginUser; |
| | | import com.ruoyi.system.api.model.LoginUserInfo; |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | import javax.servlet.http.HttpServletRequest; |
| | | import java.util.HashMap; |
| | | import java.util.Map; |
| | | import java.util.concurrent.TimeUnit; |
| | | |
| | | /** |
| | | * token验证处理 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | @Component |
| | | public class TokenService { |
| | | private static final Logger log = LoggerFactory.getLogger(TokenService.class); |
| | | |
| | | @Autowired |
| | | private RedisService redisService; |
| | | |
| | | protected static final long MILLIS_SECOND = 1000; |
| | | |
| | | protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND; |
| | | |
| | | private final static long expireTime = CacheConstants.EXPIRATION; |
| | | |
| | | private final static String ACCESS_TOKEN = CacheConstants.LOGIN_TOKEN_KEY; |
| | | |
| | | private final static Long MILLIS_MINUTE_TEN = CacheConstants.REFRESH_TIME * MILLIS_MINUTE; |
| | | |
| | | /** |
| | | * 创建令牌 |
| | | */ |
| | | public Map<String, Object> createToken(LoginUser loginUser) { |
| | | String token = IdUtils.fastUUID(); |
| | | Long userId = loginUser.getSysUser().getUserId(); |
| | | String userName = loginUser.getSysUser().getNickName(); |
| | | loginUser.setToken(token); |
| | | loginUser.setUserid(userId); |
| | | loginUser.setUsername(userName); |
| | | loginUser.setIpaddr(IpUtils.getIpAddr()); |
| | | refreshToken(loginUser); |
| | | |
| | | // Jwt存储信息 |
| | | Map<String, Object> claimsMap = new HashMap<String, Object>(); |
| | | claimsMap.put(SecurityConstants.USER_KEY, token); |
| | | claimsMap.put(SecurityConstants.DETAILS_USER_ID, userId); |
| | | claimsMap.put(SecurityConstants.DETAILS_USERNAME, userName); |
| | | |
| | | // 接口返回信息 |
| | | Map<String, Object> rspMap = new HashMap<String, Object>(); |
| | | rspMap.put("access_token", JwtUtils.createToken(claimsMap)); |
| | | rspMap.put("expires_in", expireTime); |
| | | return rspMap; |
| | | } |
| | | |
| | | /** |
| | | * 用户端-创建令牌 |
| | | */ |
| | | public Map<String, Object> createTokenByUser(LoginUserInfo loginUserInfo) { |
| | | String token = IdUtils.fastUUID(); |
| | | Integer userId = loginUserInfo.getUserid(); |
| | | String userName = loginUserInfo.getName(); |
| | | loginUserInfo.setToken(token); |
| | | loginUserInfo.setIpaddr(IpUtils.getIpAddr()); |
| | | refreshTokenByUser(loginUserInfo); |
| | | |
| | | // Jwt存储信息 |
| | | Map<String, Object> claimsMap = new HashMap<String, Object>(); |
| | | claimsMap.put(SecurityConstants.USER_USER_KEY, token); |
| | | claimsMap.put(SecurityConstants.DETAILS_USER_ID, userId); |
| | | claimsMap.put(SecurityConstants.DETAILS_USERNAME, userName); |
| | | |
| | | // 接口返回信息 |
| | | Map<String, Object> rspMap = new HashMap<String, Object>(); |
| | | rspMap.put("access_token", JwtUtils.createToken(claimsMap)); |
| | | rspMap.put("expires_in", expireTime); |
| | | return rspMap; |
| | | } |
| | | |
| | | /** |
| | | * 师傅端-创建令牌 |
| | | */ |
| | | public Map<String, Object> createTokenByWorker(LoginUserInfo loginUserInfo) { |
| | | String token = IdUtils.fastUUID(); |
| | | Integer userId = loginUserInfo.getUserid(); |
| | | String userName = loginUserInfo.getName(); |
| | | loginUserInfo.setToken(token); |
| | | loginUserInfo.setIpaddr(IpUtils.getIpAddr()); |
| | | refreshTokenByWorker(loginUserInfo); |
| | | // Jwt存储信息 |
| | | Map<String, Object> claimsMap = new HashMap<String, Object>(); |
| | | claimsMap.put(SecurityConstants.USER_WORKER_KEY, token); |
| | | claimsMap.put(SecurityConstants.DETAILS_USER_ID, userId); |
| | | claimsMap.put(SecurityConstants.DETAILS_USERNAME, userName); |
| | | |
| | | // 接口返回信息 |
| | | Map<String, Object> rspMap = new HashMap<String, Object>(); |
| | | rspMap.put("access_token", JwtUtils.createToken(claimsMap)); |
| | | rspMap.put("expires_in", expireTime); |
| | | return rspMap; |
| | | } |
| | | |
| | | /** |
| | | * 获取用户身份信息 |
| | | * |
| | | * @return 用户信息 |
| | | */ |
| | | public LoginUser getLoginUser() { |
| | | return getLoginUser(ServletUtils.getRequest()); |
| | | } |
| | | |
| | | /** |
| | | * 获取用户端身份信息 |
| | | * |
| | | * @return 用户信息 |
| | | */ |
| | | public LoginUser getLoginUserByUser() { |
| | | return getLoginUserByUser(ServletUtils.getRequest()); |
| | | } |
| | | |
| | | /** |
| | | * 获取师傅端身份信息 |
| | | * |
| | | * @return 用户信息 |
| | | */ |
| | | public LoginUserInfo getLoginUserByWorker() { |
| | | return getLoginUserByWorker(ServletUtils.getRequest()); |
| | | } |
| | | |
| | | /** |
| | | * 获取用户身份信息 |
| | | * |
| | | * @return 用户信息 |
| | | */ |
| | | public LoginUser getLoginUser(HttpServletRequest request) { |
| | | // 获取请求携带的令牌 |
| | | String token = SecurityUtils.getToken(request); |
| | | return getLoginUser(token); |
| | | } |
| | | |
| | | /** |
| | | * 获取用户身份信息 |
| | | * |
| | | * @return 用户信息 |
| | | */ |
| | | public LoginUser getLoginUserByUser(HttpServletRequest request) { |
| | | // 获取请求携带的令牌 |
| | | String token = SecurityUtils.getToken(request); |
| | | return getLoginUserByUser(token); |
| | | } |
| | | |
| | | /** |
| | | * 获取师傅端身份信息 |
| | | * |
| | | * @return 用户信息 |
| | | */ |
| | | public LoginUserInfo getLoginUserByWorker(HttpServletRequest request) { |
| | | // 获取请求携带的令牌 |
| | | String token = SecurityUtils.getToken(request); |
| | | return getLoginUserByWorker(token); |
| | | } |
| | | |
| | | /** |
| | | * 获取用户身份信息 |
| | | * |
| | | * @return 用户信息 |
| | | */ |
| | | public LoginUser getLoginUser(String token) { |
| | | LoginUser user = null; |
| | | try { |
| | | if (StringUtils.isNotEmpty(token)) { |
| | | String userkey = JwtUtils.getUserKey(token); |
| | | user = redisService.getCacheObject(getTokenKey(userkey)); |
| | | return user; |
| | | } |
| | | } catch (Exception e) { |
| | | log.error("获取用户信息异常'{}'", e.getMessage()); |
| | | } |
| | | return user; |
| | | } |
| | | |
| | | /** |
| | | * 获取用户身份信息 |
| | | * |
| | | * @return 用户信息 |
| | | */ |
| | | public LoginUser getLoginUserByUser(String token) { |
| | | LoginUser user = null; |
| | | try { |
| | | if (StringUtils.isNotEmpty(token)) { |
| | | String userkey = JwtUtils.getUserKeyByUser(token); |
| | | user = redisService.getCacheObject(getTokenKeyByUser(userkey)); |
| | | return user; |
| | | } |
| | | } catch (Exception e) { |
| | | log.error("获取用户信息异常'{}'", e.getMessage()); |
| | | } |
| | | return user; |
| | | } |
| | | |
| | | /** |
| | | * 获取用户身份信息 |
| | | * |
| | | * @return 用户信息 |
| | | */ |
| | | public LoginUserInfo getLoginUserByWorker(String token) { |
| | | LoginUserInfo user = null; |
| | | try { |
| | | if (StringUtils.isNotEmpty(token)) { |
| | | String userkey = JwtUtils.getUserKeyByWorker(token); |
| | | user = redisService.getCacheObject(getTokenKeyByWorker(userkey)); |
| | | return user; |
| | | } |
| | | } catch (Exception e) { |
| | | log.error("获取用户信息异常'{}'", e.getMessage()); |
| | | } |
| | | return user; |
| | | } |
| | | |
| | | /** |
| | | * 设置用户身份信息 |
| | | */ |
| | | public void setLoginUser(LoginUser loginUser) { |
| | | if (StringUtils.isNotNull(loginUser) && StringUtils.isNotEmpty(loginUser.getToken())) { |
| | | refreshToken(loginUser); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 删除用户缓存信息 |
| | | */ |
| | | public void delLoginUser(String token) { |
| | | if (StringUtils.isNotEmpty(token)) { |
| | | String userkey = JwtUtils.getUserKey(token); |
| | | redisService.deleteObject(getTokenKey(userkey)); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 删除用户端缓存信息 |
| | | */ |
| | | public void delLoginUserByUser(String token) { |
| | | if (StringUtils.isNotEmpty(token)) { |
| | | String userkey = JwtUtils.getUserKey(token); |
| | | redisService.deleteObject(getTokenKeyByUser(userkey)); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 删除师傅端缓存信息 |
| | | */ |
| | | public void delLoginUserByWorker(String token) { |
| | | if (StringUtils.isNotEmpty(token)) { |
| | | String userkey = JwtUtils.getUserKey(token); |
| | | redisService.deleteObject(getTokenKeyByWorker(userkey)); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 验证令牌有效期,相差不足120分钟,自动刷新缓存 |
| | | * |
| | | * @param loginUser |
| | | */ |
| | | public void verifyToken(LoginUser loginUser) { |
| | | long expireTime = loginUser.getExpireTime(); |
| | | long currentTime = System.currentTimeMillis(); |
| | | if (expireTime - currentTime <= MILLIS_MINUTE_TEN) { |
| | | refreshToken(loginUser); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 刷新令牌有效期 |
| | | * |
| | | * @param loginUser 登录信息 |
| | | */ |
| | | public void refreshToken(LoginUser loginUser) { |
| | | loginUser.setLoginTime(System.currentTimeMillis()); |
| | | loginUser.setExpireTime(loginUser.getLoginTime() + expireTime * MILLIS_MINUTE); |
| | | // 根据uuid将loginUser缓存 |
| | | String userKey = getTokenKey(loginUser.getToken()); |
| | | redisService.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES); |
| | | } |
| | | |
| | | /** |
| | | * 用户端-刷新令牌有效期 |
| | | * |
| | | * @param loginUserInfo 登录信息 |
| | | */ |
| | | public void refreshTokenByUser(LoginUserInfo loginUserInfo) { |
| | | loginUserInfo.setLoginTime(System.currentTimeMillis()); |
| | | loginUserInfo.setExpireTime(loginUserInfo.getLoginTime() + expireTime * MILLIS_MINUTE); |
| | | // 根据uuid将loginUser缓存 |
| | | String userKey = getTokenKeyByUser(loginUserInfo.getToken()); |
| | | redisService.setCacheObject(userKey, loginUserInfo, expireTime, TimeUnit.MINUTES); |
| | | } |
| | | |
| | | /** |
| | | * 师傅端-刷新令牌有效期 |
| | | * |
| | | * @param loginUserInfo 登录信息 |
| | | */ |
| | | public void refreshTokenByWorker(LoginUserInfo loginUserInfo) { |
| | | loginUserInfo.setLoginTime(System.currentTimeMillis()); |
| | | loginUserInfo.setExpireTime(loginUserInfo.getLoginTime() + expireTime * MILLIS_MINUTE); |
| | | // 根据uuid将loginUser缓存 |
| | | String userKey = getTokenKeyByWorker(loginUserInfo.getToken()); |
| | | redisService.setCacheObject(userKey, loginUserInfo, expireTime, TimeUnit.MINUTES); |
| | | } |
| | | |
| | | private String getTokenKey(String token) { |
| | | return ACCESS_TOKEN + token; |
| | | } |
| | | |
| | | private String getTokenKeyByUser(String token) { |
| | | return CacheConstants.LOGIN_USER_TOKEN_KEY + token; |
| | | } |
| | | |
| | | private String getTokenKeyByWorker(String token) { |
| | | return CacheConstants.LOGIN_WORKER_TOKEN_KEY + token; |
| | | } |
| | | |
| | | /** |
| | | * 师傅端删除用户缓存信息 |
| | | */ |
| | | public void delLoginWorker(String token) { |
| | | if (StringUtils.isNotEmpty(token)) { |
| | | String userkey = JwtUtils.getUserKeyByWorker(token); |
| | | redisService.deleteObject(getTokenKeyByWorker(userkey)); |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.security.utils; |
| | | |
| | | import com.alibaba.fastjson2.JSONArray; |
| | | import com.ruoyi.common.core.constant.CacheConstants; |
| | | import com.ruoyi.common.core.utils.SpringUtils; |
| | | import com.ruoyi.common.core.utils.StringUtils; |
| | | import com.ruoyi.common.redis.service.RedisService; |
| | | import com.ruoyi.system.api.domain.SysDictData; |
| | | |
| | | import java.util.Collection; |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * 字典工具类 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class DictUtils { |
| | | /** |
| | | * 设置字典缓存 |
| | | * |
| | | * @param key 参数键 |
| | | * @param dictDatas 字典数据列表 |
| | | */ |
| | | public static void setDictCache(String key, List<SysDictData> dictDatas) { |
| | | SpringUtils.getBean(RedisService.class).setCacheObject(getCacheKey(key), dictDatas); |
| | | } |
| | | |
| | | /** |
| | | * 获取字典缓存 |
| | | * |
| | | * @param key 参数键 |
| | | * @return dictDatas 字典数据列表 |
| | | */ |
| | | public static List<SysDictData> getDictCache(String key) { |
| | | JSONArray arrayCache = SpringUtils.getBean(RedisService.class).getCacheObject(getCacheKey(key)); |
| | | if (StringUtils.isNotNull(arrayCache)) { |
| | | return arrayCache.toList(SysDictData.class); |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | /** |
| | | * 删除指定字典缓存 |
| | | * |
| | | * @param key 字典键 |
| | | */ |
| | | public static void removeDictCache(String key) { |
| | | SpringUtils.getBean(RedisService.class).deleteObject(getCacheKey(key)); |
| | | } |
| | | |
| | | /** |
| | | * 清空字典缓存 |
| | | */ |
| | | public static void clearDictCache() { |
| | | Collection<String> keys = SpringUtils.getBean(RedisService.class).keys(CacheConstants.SYS_DICT_KEY + "*"); |
| | | SpringUtils.getBean(RedisService.class).deleteObject(keys); |
| | | } |
| | | |
| | | /** |
| | | * 设置cache key |
| | | * |
| | | * @param configKey 参数键 |
| | | * @return 缓存键key |
| | | */ |
| | | public static String getCacheKey(String configKey) { |
| | | return CacheConstants.SYS_DICT_KEY + configKey; |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.security.utils; |
| | | |
| | | import com.ruoyi.common.core.constant.SecurityConstants; |
| | | import com.ruoyi.common.core.constant.TokenConstants; |
| | | import com.ruoyi.common.core.context.SecurityContextHolder; |
| | | import com.ruoyi.common.core.utils.ServletUtils; |
| | | import com.ruoyi.common.core.utils.StringUtils; |
| | | import com.ruoyi.system.api.model.LoginUser; |
| | | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; |
| | | |
| | | import javax.servlet.http.HttpServletRequest; |
| | | |
| | | /** |
| | | * 权限获取工具类 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class SecurityUtils { |
| | | /** |
| | | * 获取用户ID |
| | | */ |
| | | public static Long getUserId() { |
| | | return SecurityContextHolder.getUserId(); |
| | | } |
| | | |
| | | /** |
| | | * 获取用户名称 |
| | | */ |
| | | public static String getUsername() { |
| | | return SecurityContextHolder.getUserName(); |
| | | } |
| | | |
| | | /** |
| | | * 获取用户key |
| | | */ |
| | | public static String getUserKey() { |
| | | return SecurityContextHolder.getUserKey(); |
| | | } |
| | | |
| | | /** |
| | | * 获取登录用户信息 |
| | | */ |
| | | public static LoginUser getLoginUser() { |
| | | return SecurityContextHolder.get(SecurityConstants.LOGIN_USER, LoginUser.class); |
| | | } |
| | | |
| | | /** |
| | | * 获取请求token |
| | | */ |
| | | public static String getToken() { |
| | | return getToken(ServletUtils.getRequest()); |
| | | } |
| | | |
| | | /** |
| | | * 根据request获取请求token |
| | | */ |
| | | public static String getToken(HttpServletRequest request) { |
| | | // 从header获取token标识 |
| | | String token = request.getHeader(TokenConstants.AUTHENTICATION); |
| | | return replaceTokenPrefix(token); |
| | | } |
| | | |
| | | /** |
| | | * 裁剪token前缀 |
| | | */ |
| | | public static String replaceTokenPrefix(String token) { |
| | | // 如果前端设置了令牌前缀,则裁剪掉前缀 |
| | | if (StringUtils.isNotEmpty(token) && token.startsWith(TokenConstants.PREFIX)) { |
| | | token = token.replaceFirst(TokenConstants.PREFIX, ""); |
| | | } |
| | | return token; |
| | | } |
| | | |
| | | /** |
| | | * 是否为管理员 |
| | | * |
| | | * @param userId 用户ID |
| | | * @return 结果 |
| | | */ |
| | | public static boolean isAdmin(Long userId) { |
| | | return userId != null && 1L == userId; |
| | | } |
| | | |
| | | /** |
| | | * 生成BCryptPasswordEncoder密码 |
| | | * |
| | | * @param password 密码 |
| | | * @return 加密字符串 |
| | | */ |
| | | public static String encryptPassword(String password) { |
| | | BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); |
| | | return passwordEncoder.encode(password); |
| | | } |
| | | |
| | | /** |
| | | * 判断密码是否相同 |
| | | * |
| | | * @param rawPassword 真实密码 |
| | | * @param encodedPassword 加密后字符 |
| | | * @return 结果 |
| | | */ |
| | | public static boolean matchesPassword(String rawPassword, String encodedPassword) { |
| | | BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); |
| | | return passwordEncoder.matches(rawPassword, encodedPassword); |
| | | } |
| | | } |
New file |
| | |
| | | com.ruoyi.common.security.config.WebMvcConfig |
| | | com.ruoyi.common.security.service.TokenService |
| | | com.ruoyi.common.security.aspect.PreAuthorizeAspect |
| | | com.ruoyi.common.security.aspect.InnerAuthAspect |
| | | com.ruoyi.common.security.handler.GlobalExceptionHandler |
New file |
| | |
| | | <?xml version="1.0" encoding="UTF-8"?> |
| | | <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
| | | xmlns="http://maven.apache.org/POM/4.0.0" |
| | | xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
| | | <parent> |
| | | <groupId>com.ruoyi</groupId> |
| | | <artifactId>ruoyi-common</artifactId> |
| | | <version>3.6.4</version> |
| | | </parent> |
| | | <modelVersion>4.0.0</modelVersion> |
| | | |
| | | <artifactId>ruoyi-common-sensitive</artifactId> |
| | | |
| | | <description> |
| | | ruoyi-common-sensitive数据脱敏 |
| | | </description> |
| | | |
| | | <dependencies> |
| | | |
| | | <!-- RuoYi Common Security --> |
| | | <dependency> |
| | | <groupId>com.ruoyi</groupId> |
| | | <artifactId>ruoyi-common-security</artifactId> |
| | | </dependency> |
| | | |
| | | </dependencies> |
| | | </project> |
New file |
| | |
| | | package com.ruoyi.common.sensitive.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.sensitive.config.SensitiveJsonSerializer; |
| | | import com.ruoyi.common.sensitive.enums.DesensitizedType; |
| | | |
| | | /** |
| | | * 数据脱敏注解 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | @Retention(RetentionPolicy.RUNTIME) |
| | | @Target(ElementType.FIELD) |
| | | @JacksonAnnotationsInside |
| | | @JsonSerialize(using = SensitiveJsonSerializer.class) |
| | | public @interface Sensitive |
| | | { |
| | | DesensitizedType desensitizedType(); |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.sensitive.config; |
| | | |
| | | 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.security.utils.SecurityUtils; |
| | | import com.ruoyi.common.sensitive.annotation.Sensitive; |
| | | import com.ruoyi.common.sensitive.enums.DesensitizedType; |
| | | import com.ruoyi.system.api.model.LoginUser; |
| | | |
| | | /** |
| | | * 数据脱敏序列化过滤 |
| | | * |
| | | * @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.getSysUser().isAdmin(); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | return true; |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.sensitive.enums; |
| | | |
| | | import java.util.function.Function; |
| | | import com.ruoyi.common.sensitive.utils.DesensitizedUtil; |
| | | |
| | | /** |
| | | * 脱敏类型 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public enum DesensitizedType |
| | | { |
| | | /** |
| | | * 姓名,第2位星号替换 |
| | | */ |
| | | USERNAME(s -> s.replaceAll("(\\S)\\S(\\S*)", "$1*$2")), |
| | | |
| | | /** |
| | | * 密码,全部字符都用*代替 |
| | | */ |
| | | PASSWORD(DesensitizedUtil::password), |
| | | |
| | | /** |
| | | * 身份证,中间10位星号替换 |
| | | */ |
| | | ID_CARD(s -> s.replaceAll("(\\d{4})\\d{10}(\\d{4})", "$1** **** ****$2")), |
| | | |
| | | /** |
| | | * 手机号,中间4位星号替换 |
| | | */ |
| | | PHONE(s -> s.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2")), |
| | | |
| | | /** |
| | | * 电子邮箱,仅显示第一个字母和@后面的地址显示,其他星号替换 |
| | | */ |
| | | EMAIL(s -> s.replaceAll("(^.)[^@]*(@.*$)", "$1****$2")), |
| | | |
| | | /** |
| | | * 银行卡号,保留最后4位,其他星号替换 |
| | | */ |
| | | BANK_CARD(s -> s.replaceAll("\\d{15}(\\d{3})", "**** **** **** **** $1")), |
| | | |
| | | /** |
| | | * 车牌号码,包含普通车辆、新能源车辆 |
| | | */ |
| | | CAR_LICENSE(DesensitizedUtil::carLicense); |
| | | |
| | | private final Function<String, String> desensitizer; |
| | | |
| | | DesensitizedType(Function<String, String> desensitizer) |
| | | { |
| | | this.desensitizer = desensitizer; |
| | | } |
| | | |
| | | public Function<String, String> desensitizer() |
| | | { |
| | | return desensitizer; |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.sensitive.utils; |
| | | |
| | | import com.ruoyi.common.core.utils.StringUtils; |
| | | |
| | | /** |
| | | * 脱敏工具类 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class DesensitizedUtil |
| | | { |
| | | /** |
| | | * 密码的全部字符都用*代替,比如:****** |
| | | * |
| | | * @param password 密码 |
| | | * @return 脱敏后的密码 |
| | | */ |
| | | public static String password(String password) |
| | | { |
| | | if (StringUtils.isBlank(password)) |
| | | { |
| | | return StringUtils.EMPTY; |
| | | } |
| | | return StringUtils.repeat('*', password.length()); |
| | | } |
| | | |
| | | /** |
| | | * 车牌中间用*代替,如果是错误的车牌,不处理 |
| | | * |
| | | * @param carLicense 完整的车牌号 |
| | | * @return 脱敏后的车牌 |
| | | */ |
| | | public static String carLicense(String carLicense) |
| | | { |
| | | if (StringUtils.isBlank(carLicense)) |
| | | { |
| | | return StringUtils.EMPTY; |
| | | } |
| | | // 普通车牌 |
| | | if (carLicense.length() == 7) |
| | | { |
| | | carLicense = StringUtils.hide(carLicense, 3, 6); |
| | | } |
| | | else if (carLicense.length() == 8) |
| | | { |
| | | // 新能源车牌 |
| | | carLicense = StringUtils.hide(carLicense, 3, 7); |
| | | } |
| | | return carLicense; |
| | | } |
| | | } |
New file |
| | |
| | | <?xml version="1.0" encoding="UTF-8"?> |
| | | <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
| | | xmlns="http://maven.apache.org/POM/4.0.0" |
| | | xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
| | | <parent> |
| | | <groupId>com.ruoyi</groupId> |
| | | <artifactId>ruoyi-common</artifactId> |
| | | <version>3.6.4</version> |
| | | </parent> |
| | | <modelVersion>4.0.0</modelVersion> |
| | | |
| | | <artifactId>ruoyi-common-swagger</artifactId> |
| | | |
| | | <description> |
| | | ruoyi-common-swagger系统接口 |
| | | </description> |
| | | |
| | | <dependencies> |
| | | |
| | | <!-- SpringBoot Web --> |
| | | <dependency> |
| | | <groupId>org.springframework.boot</groupId> |
| | | <artifactId>spring-boot-starter-web</artifactId> |
| | | </dependency> |
| | | |
| | | <!-- Swagger --> |
| | | <dependency> |
| | | <groupId>io.springfox</groupId> |
| | | <artifactId>springfox-swagger2</artifactId> |
| | | <version>${swagger.fox.version}</version> |
| | | </dependency> |
| | | |
| | | </dependencies> |
| | | </project> |
New file |
| | |
| | | package com.ruoyi.common.swagger.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 org.springframework.context.annotation.Import; |
| | | import com.ruoyi.common.swagger.config.SwaggerAutoConfiguration; |
| | | |
| | | @Target({ ElementType.TYPE }) |
| | | @Retention(RetentionPolicy.RUNTIME) |
| | | @Documented |
| | | @Inherited |
| | | @Import({ SwaggerAutoConfiguration.class }) |
| | | public @interface EnableCustomSwagger2 |
| | | { |
| | | |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.swagger.config; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.Arrays; |
| | | import java.util.List; |
| | | import java.util.function.Predicate; |
| | | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
| | | import org.springframework.boot.context.properties.EnableConfigurationProperties; |
| | | import org.springframework.context.annotation.Bean; |
| | | import org.springframework.context.annotation.Configuration; |
| | | import org.springframework.context.annotation.Import; |
| | | import springfox.documentation.builders.ApiInfoBuilder; |
| | | import springfox.documentation.builders.PathSelectors; |
| | | import springfox.documentation.builders.RequestHandlerSelectors; |
| | | import springfox.documentation.service.ApiInfo; |
| | | import springfox.documentation.service.ApiKey; |
| | | import springfox.documentation.service.AuthorizationScope; |
| | | import springfox.documentation.service.Contact; |
| | | import springfox.documentation.service.SecurityReference; |
| | | import springfox.documentation.service.SecurityScheme; |
| | | import springfox.documentation.spi.DocumentationType; |
| | | import springfox.documentation.spi.service.contexts.SecurityContext; |
| | | import springfox.documentation.spring.web.plugins.ApiSelectorBuilder; |
| | | import springfox.documentation.spring.web.plugins.Docket; |
| | | import springfox.documentation.swagger2.annotations.EnableSwagger2; |
| | | |
| | | @Configuration |
| | | @EnableSwagger2 |
| | | @EnableConfigurationProperties(SwaggerProperties.class) |
| | | @ConditionalOnProperty(name = "swagger.enabled", matchIfMissing = true) |
| | | @Import({SwaggerBeanPostProcessor.class, SwaggerWebConfiguration.class}) |
| | | public class SwaggerAutoConfiguration |
| | | { |
| | | /** |
| | | * 默认的排除路径,排除Spring Boot默认的错误处理路径和端点 |
| | | */ |
| | | private static final List<String> DEFAULT_EXCLUDE_PATH = Arrays.asList("/error", "/actuator/**"); |
| | | |
| | | private static final String BASE_PATH = "/**"; |
| | | |
| | | @Bean |
| | | public Docket api(SwaggerProperties swaggerProperties) |
| | | { |
| | | // base-path处理 |
| | | if (swaggerProperties.getBasePath().isEmpty()) |
| | | { |
| | | swaggerProperties.getBasePath().add(BASE_PATH); |
| | | } |
| | | // noinspection unchecked |
| | | List<Predicate<String>> basePath = new ArrayList<Predicate<String>>(); |
| | | swaggerProperties.getBasePath().forEach(path -> basePath.add(PathSelectors.ant(path))); |
| | | |
| | | // exclude-path处理 |
| | | if (swaggerProperties.getExcludePath().isEmpty()) |
| | | { |
| | | swaggerProperties.getExcludePath().addAll(DEFAULT_EXCLUDE_PATH); |
| | | } |
| | | |
| | | List<Predicate<String>> excludePath = new ArrayList<>(); |
| | | swaggerProperties.getExcludePath().forEach(path -> excludePath.add(PathSelectors.ant(path))); |
| | | |
| | | ApiSelectorBuilder builder = new Docket(DocumentationType.SWAGGER_2).host(swaggerProperties.getHost()) |
| | | .apiInfo(apiInfo(swaggerProperties)).select() |
| | | .apis(RequestHandlerSelectors.basePackage(swaggerProperties.getBasePackage())); |
| | | |
| | | swaggerProperties.getBasePath().forEach(p -> builder.paths(PathSelectors.ant(p))); |
| | | swaggerProperties.getExcludePath().forEach(p -> builder.paths(PathSelectors.ant(p).negate())); |
| | | |
| | | return builder.build().securitySchemes(securitySchemes()).securityContexts(securityContexts()).pathMapping("/"); |
| | | } |
| | | |
| | | /** |
| | | * 安全模式,这里指定token通过Authorization头请求头传递 |
| | | */ |
| | | private List<SecurityScheme> securitySchemes() |
| | | { |
| | | List<SecurityScheme> apiKeyList = new ArrayList<SecurityScheme>(); |
| | | apiKeyList.add(new ApiKey("Authorization", "Authorization", "header")); |
| | | return apiKeyList; |
| | | } |
| | | |
| | | /** |
| | | * 安全上下文 |
| | | */ |
| | | private List<SecurityContext> securityContexts() |
| | | { |
| | | List<SecurityContext> securityContexts = new ArrayList<>(); |
| | | securityContexts.add( |
| | | SecurityContext.builder() |
| | | .securityReferences(defaultAuth()) |
| | | .operationSelector(o -> o.requestMappingPattern().matches("/.*")) |
| | | .build()); |
| | | return securityContexts; |
| | | } |
| | | |
| | | /** |
| | | * 默认的全局鉴权策略 |
| | | * |
| | | * @return |
| | | */ |
| | | private List<SecurityReference> defaultAuth() |
| | | { |
| | | AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything"); |
| | | AuthorizationScope[] authorizationScopes = new AuthorizationScope[1]; |
| | | authorizationScopes[0] = authorizationScope; |
| | | List<SecurityReference> securityReferences = new ArrayList<>(); |
| | | securityReferences.add(new SecurityReference("Authorization", authorizationScopes)); |
| | | return securityReferences; |
| | | } |
| | | |
| | | private ApiInfo apiInfo(SwaggerProperties swaggerProperties) |
| | | { |
| | | return new ApiInfoBuilder() |
| | | .title(swaggerProperties.getTitle()) |
| | | .description(swaggerProperties.getDescription()) |
| | | .license(swaggerProperties.getLicense()) |
| | | .licenseUrl(swaggerProperties.getLicenseUrl()) |
| | | .termsOfServiceUrl(swaggerProperties.getTermsOfServiceUrl()) |
| | | .contact(new Contact(swaggerProperties.getContact().getName(), swaggerProperties.getContact().getUrl(), swaggerProperties.getContact().getEmail())) |
| | | .version(swaggerProperties.getVersion()) |
| | | .build(); |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.swagger.config; |
| | | |
| | | import org.springframework.beans.BeansException; |
| | | import org.springframework.beans.factory.config.BeanPostProcessor; |
| | | import org.springframework.util.ReflectionUtils; |
| | | import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping; |
| | | import springfox.documentation.spring.web.plugins.WebFluxRequestHandlerProvider; |
| | | import springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider; |
| | | import java.lang.reflect.Field; |
| | | import java.util.List; |
| | | import java.util.stream.Collectors; |
| | | |
| | | /** |
| | | * swagger 在 springboot 2.6.x 不兼容问题的处理 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class SwaggerBeanPostProcessor implements BeanPostProcessor |
| | | { |
| | | @Override |
| | | public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException |
| | | { |
| | | if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) |
| | | { |
| | | customizeSpringfoxHandlerMappings(getHandlerMappings(bean)); |
| | | } |
| | | return bean; |
| | | } |
| | | |
| | | private <T extends RequestMappingInfoHandlerMapping> void customizeSpringfoxHandlerMappings(List<T> mappings) |
| | | { |
| | | List<T> copy = mappings.stream().filter(mapping -> mapping.getPatternParser() == null) |
| | | .collect(Collectors.toList()); |
| | | mappings.clear(); |
| | | mappings.addAll(copy); |
| | | } |
| | | |
| | | @SuppressWarnings("unchecked") |
| | | private List<RequestMappingInfoHandlerMapping> getHandlerMappings(Object bean) |
| | | { |
| | | try |
| | | { |
| | | Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings"); |
| | | field.setAccessible(true); |
| | | return (List<RequestMappingInfoHandlerMapping>) field.get(bean); |
| | | } |
| | | catch (IllegalArgumentException | IllegalAccessException e) |
| | | { |
| | | throw new IllegalStateException(e); |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.swagger.config; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | import org.springframework.boot.context.properties.ConfigurationProperties; |
| | | |
| | | @ConfigurationProperties("swagger") |
| | | public class SwaggerProperties |
| | | { |
| | | /** |
| | | * 是否开启swagger |
| | | */ |
| | | private Boolean enabled; |
| | | |
| | | /** |
| | | * swagger会解析的包路径 |
| | | **/ |
| | | private String basePackage = ""; |
| | | |
| | | /** |
| | | * swagger会解析的url规则 |
| | | **/ |
| | | private List<String> basePath = new ArrayList<>(); |
| | | |
| | | /** |
| | | * 在basePath基础上需要排除的url规则 |
| | | **/ |
| | | private List<String> excludePath = new ArrayList<>(); |
| | | |
| | | /** |
| | | * 标题 |
| | | **/ |
| | | private String title = ""; |
| | | |
| | | /** |
| | | * 描述 |
| | | **/ |
| | | private String description = ""; |
| | | |
| | | /** |
| | | * 版本 |
| | | **/ |
| | | private String version = ""; |
| | | |
| | | /** |
| | | * 许可证 |
| | | **/ |
| | | private String license = ""; |
| | | |
| | | /** |
| | | * 许可证URL |
| | | **/ |
| | | private String licenseUrl = ""; |
| | | |
| | | /** |
| | | * 服务条款URL |
| | | **/ |
| | | private String termsOfServiceUrl = ""; |
| | | |
| | | /** |
| | | * host信息 |
| | | **/ |
| | | private String host = ""; |
| | | |
| | | /** |
| | | * 联系人信息 |
| | | */ |
| | | private Contact contact = new Contact(); |
| | | |
| | | /** |
| | | * 全局统一鉴权配置 |
| | | **/ |
| | | private Authorization authorization = new Authorization(); |
| | | |
| | | public Boolean getEnabled() |
| | | { |
| | | return enabled; |
| | | } |
| | | |
| | | public void setEnabled(Boolean enabled) |
| | | { |
| | | this.enabled = enabled; |
| | | } |
| | | |
| | | public String getBasePackage() |
| | | { |
| | | return basePackage; |
| | | } |
| | | |
| | | public void setBasePackage(String basePackage) |
| | | { |
| | | this.basePackage = basePackage; |
| | | } |
| | | |
| | | public List<String> getBasePath() |
| | | { |
| | | return basePath; |
| | | } |
| | | |
| | | public void setBasePath(List<String> basePath) |
| | | { |
| | | this.basePath = basePath; |
| | | } |
| | | |
| | | public List<String> getExcludePath() |
| | | { |
| | | return excludePath; |
| | | } |
| | | |
| | | public void setExcludePath(List<String> excludePath) |
| | | { |
| | | this.excludePath = excludePath; |
| | | } |
| | | |
| | | public String getTitle() |
| | | { |
| | | return title; |
| | | } |
| | | |
| | | public void setTitle(String title) |
| | | { |
| | | this.title = title; |
| | | } |
| | | |
| | | public String getDescription() |
| | | { |
| | | return description; |
| | | } |
| | | |
| | | public void setDescription(String description) |
| | | { |
| | | this.description = description; |
| | | } |
| | | |
| | | public String getVersion() |
| | | { |
| | | return version; |
| | | } |
| | | |
| | | public void setVersion(String version) |
| | | { |
| | | this.version = version; |
| | | } |
| | | |
| | | public String getLicense() |
| | | { |
| | | return license; |
| | | } |
| | | |
| | | public void setLicense(String license) |
| | | { |
| | | this.license = license; |
| | | } |
| | | |
| | | public String getLicenseUrl() |
| | | { |
| | | return licenseUrl; |
| | | } |
| | | |
| | | public void setLicenseUrl(String licenseUrl) |
| | | { |
| | | this.licenseUrl = licenseUrl; |
| | | } |
| | | |
| | | public String getTermsOfServiceUrl() |
| | | { |
| | | return termsOfServiceUrl; |
| | | } |
| | | |
| | | public void setTermsOfServiceUrl(String termsOfServiceUrl) |
| | | { |
| | | this.termsOfServiceUrl = termsOfServiceUrl; |
| | | } |
| | | |
| | | public String getHost() |
| | | { |
| | | return host; |
| | | } |
| | | |
| | | public void setHost(String host) |
| | | { |
| | | this.host = host; |
| | | } |
| | | |
| | | public Contact getContact() |
| | | { |
| | | return contact; |
| | | } |
| | | |
| | | public void setContact(Contact contact) |
| | | { |
| | | this.contact = contact; |
| | | } |
| | | |
| | | public Authorization getAuthorization() |
| | | { |
| | | return authorization; |
| | | } |
| | | |
| | | public void setAuthorization(Authorization authorization) |
| | | { |
| | | this.authorization = authorization; |
| | | } |
| | | |
| | | public static class Contact |
| | | { |
| | | /** |
| | | * 联系人 |
| | | **/ |
| | | private String name = ""; |
| | | /** |
| | | * 联系人url |
| | | **/ |
| | | private String url = ""; |
| | | /** |
| | | * 联系人email |
| | | **/ |
| | | private String email = ""; |
| | | |
| | | public String getName() |
| | | { |
| | | return name; |
| | | } |
| | | |
| | | public void setName(String name) |
| | | { |
| | | this.name = name; |
| | | } |
| | | |
| | | public String getUrl() |
| | | { |
| | | return url; |
| | | } |
| | | |
| | | public void setUrl(String url) |
| | | { |
| | | this.url = url; |
| | | } |
| | | |
| | | public String getEmail() |
| | | { |
| | | return email; |
| | | } |
| | | |
| | | public void setEmail(String email) |
| | | { |
| | | this.email = email; |
| | | } |
| | | } |
| | | |
| | | public static class Authorization |
| | | { |
| | | /** |
| | | * 鉴权策略ID,需要和SecurityReferences ID保持一致 |
| | | */ |
| | | private String name = ""; |
| | | |
| | | /** |
| | | * 需要开启鉴权URL的正则 |
| | | */ |
| | | private String authRegex = "^.*$"; |
| | | |
| | | /** |
| | | * 鉴权作用域列表 |
| | | */ |
| | | private List<AuthorizationScope> authorizationScopeList = new ArrayList<>(); |
| | | |
| | | private List<String> tokenUrlList = new ArrayList<>(); |
| | | |
| | | public String getName() |
| | | { |
| | | return name; |
| | | } |
| | | |
| | | public void setName(String name) |
| | | { |
| | | this.name = name; |
| | | } |
| | | |
| | | public String getAuthRegex() |
| | | { |
| | | return authRegex; |
| | | } |
| | | |
| | | public void setAuthRegex(String authRegex) |
| | | { |
| | | this.authRegex = authRegex; |
| | | } |
| | | |
| | | public List<AuthorizationScope> getAuthorizationScopeList() |
| | | { |
| | | return authorizationScopeList; |
| | | } |
| | | |
| | | public void setAuthorizationScopeList(List<AuthorizationScope> authorizationScopeList) |
| | | { |
| | | this.authorizationScopeList = authorizationScopeList; |
| | | } |
| | | |
| | | public List<String> getTokenUrlList() |
| | | { |
| | | return tokenUrlList; |
| | | } |
| | | |
| | | public void setTokenUrlList(List<String> tokenUrlList) |
| | | { |
| | | this.tokenUrlList = tokenUrlList; |
| | | } |
| | | } |
| | | |
| | | public static class AuthorizationScope |
| | | { |
| | | /** |
| | | * 作用域名称 |
| | | */ |
| | | private String scope = ""; |
| | | |
| | | /** |
| | | * 作用域描述 |
| | | */ |
| | | private String description = ""; |
| | | |
| | | public String getScope() |
| | | { |
| | | return scope; |
| | | } |
| | | |
| | | public void setScope(String scope) |
| | | { |
| | | this.scope = scope; |
| | | } |
| | | |
| | | public String getDescription() |
| | | { |
| | | return description; |
| | | } |
| | | |
| | | public void setDescription(String description) |
| | | { |
| | | this.description = description; |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.common.swagger.config; |
| | | |
| | | import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; |
| | | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; |
| | | |
| | | /** |
| | | * swagger 资源映射路径 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class SwaggerWebConfiguration implements WebMvcConfigurer |
| | | { |
| | | @Override |
| | | public void addResourceHandlers(ResourceHandlerRegistry registry) |
| | | { |
| | | /** swagger-ui 地址 */ |
| | | registry.addResourceHandler("/swagger-ui/**","*/doc.html") |
| | | .addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/"); |
| | | } |
| | | } |
New file |
| | |
| | | # com.ruoyi.common.swagger.config.SwaggerAutoConfiguration |
| | | # com.ruoyi.common.swagger.config.SwaggerWebConfiguration |
| | | # com.ruoyi.common.swagger.config.SwaggerBeanPostProcessor |
New file |
| | |
| | | <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> |
| | | <groupId>com.ruoyi</groupId> |
| | | <artifactId>ruoyi</artifactId> |
| | | <version>3.6.4</version> |
| | | </parent> |
| | | <modelVersion>4.0.0</modelVersion> |
| | | |
| | | <artifactId>ruoyi-gateway</artifactId> |
| | | |
| | | <description> |
| | | ruoyi-gateway网关模块 |
| | | </description> |
| | | |
| | | <dependencies> |
| | | |
| | | <!-- SpringCloud Gateway --> |
| | | <dependency> |
| | | <groupId>org.springframework.cloud</groupId> |
| | | <artifactId>spring-cloud-starter-gateway</artifactId> |
| | | </dependency> |
| | | |
| | | <!-- SpringCloud Alibaba Nacos --> |
| | | <dependency> |
| | | <groupId>com.alibaba.cloud</groupId> |
| | | <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> |
| | | </dependency> |
| | | |
| | | <!-- SpringCloud Alibaba Nacos Config --> |
| | | <dependency> |
| | | <groupId>com.alibaba.cloud</groupId> |
| | | <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> |
| | | </dependency> |
| | | |
| | | <!-- SpringCloud Alibaba Sentinel --> |
| | | <dependency> |
| | | <groupId>com.alibaba.cloud</groupId> |
| | | <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> |
| | | </dependency> |
| | | |
| | | <!-- SpringCloud Alibaba Sentinel Gateway --> |
| | | <dependency> |
| | | <groupId>com.alibaba.cloud</groupId> |
| | | <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId> |
| | | </dependency> |
| | | |
| | | <!-- Sentinel Datasource Nacos --> |
| | | <dependency> |
| | | <groupId>com.alibaba.csp</groupId> |
| | | <artifactId>sentinel-datasource-nacos</artifactId> |
| | | </dependency> |
| | | |
| | | <!-- SpringBoot Actuator --> |
| | | <dependency> |
| | | <groupId>org.springframework.boot</groupId> |
| | | <artifactId>spring-boot-starter-actuator</artifactId> |
| | | </dependency> |
| | | |
| | | <!-- SpringCloud Loadbalancer --> |
| | | <dependency> |
| | | <groupId>org.springframework.cloud</groupId> |
| | | <artifactId>spring-cloud-loadbalancer</artifactId> |
| | | </dependency> |
| | | |
| | | <!--验证码 --> |
| | | <dependency> |
| | | <groupId>pro.fessional</groupId> |
| | | <artifactId>kaptcha</artifactId> |
| | | </dependency> |
| | | |
| | | <!-- RuoYi Common Redis--> |
| | | <dependency> |
| | | <groupId>com.ruoyi</groupId> |
| | | <artifactId>ruoyi-common-redis</artifactId> |
| | | </dependency> |
| | | |
| | | <!-- Swagger --> |
| | | <dependency> |
| | | <groupId>io.springfox</groupId> |
| | | <artifactId>springfox-swagger-ui</artifactId> |
| | | <version>${swagger.fox.version}</version> |
| | | </dependency> |
| | | <dependency> |
| | | <groupId>io.springfox</groupId> |
| | | <artifactId>springfox-swagger2</artifactId> |
| | | <version>${swagger.fox.version}</version> |
| | | </dependency> |
| | | |
| | | </dependencies> |
| | | |
| | | <build> |
| | | <finalName>${project.artifactId}</finalName> |
| | | <plugins> |
| | | <plugin> |
| | | <groupId>org.springframework.boot</groupId> |
| | | <artifactId>spring-boot-maven-plugin</artifactId> |
| | | <executions> |
| | | <execution> |
| | | <goals> |
| | | <goal>repackage</goal> |
| | | </goals> |
| | | </execution> |
| | | </executions> |
| | | </plugin> |
| | | </plugins> |
| | | </build> |
| | | |
| | | </project> |
New file |
| | |
| | | package com.ruoyi.gateway; |
| | | |
| | | import org.springframework.boot.SpringApplication; |
| | | import org.springframework.boot.autoconfigure.SpringBootApplication; |
| | | import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; |
| | | |
| | | /** |
| | | * 网关启动程序 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class }) |
| | | public class RuoYiGatewayApplication |
| | | { |
| | | public static void main(String[] args) |
| | | { |
| | | SpringApplication.run(RuoYiGatewayApplication.class, args); |
| | | System.out.println("(♥◠‿◠)ノ゙ 若依网关启动成功 ლ(´ڡ`ლ)゙ \n" + |
| | | " .-------. ____ __ \n" + |
| | | " | _ _ \\ \\ \\ / / \n" + |
| | | " | ( ' ) | \\ _. / ' \n" + |
| | | " |(_ o _) / _( )_ .' \n" + |
| | | " | (_,_).' __ ___(_ o _)' \n" + |
| | | " | |\\ \\ | || |(_,_)' \n" + |
| | | " | | \\ `' /| `-' / \n" + |
| | | " | | \\ / \\ / \n" + |
| | | " ''-' `'-' `-..-' "); |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.gateway.config; |
| | | |
| | | import com.google.code.kaptcha.impl.DefaultKaptcha; |
| | | import com.google.code.kaptcha.util.Config; |
| | | import org.springframework.context.annotation.Bean; |
| | | import org.springframework.context.annotation.Configuration; |
| | | |
| | | import java.util.Properties; |
| | | |
| | | import static com.google.code.kaptcha.Constants.*; |
| | | |
| | | /** |
| | | * 验证码配置 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | @Configuration |
| | | public class CaptchaConfig { |
| | | @Bean(name = "captchaProducer") |
| | | public DefaultKaptcha getKaptchaBean() { |
| | | DefaultKaptcha defaultKaptcha = new DefaultKaptcha(); |
| | | Properties properties = new Properties(); |
| | | // 是否有边框 默认为true 我们可以自己设置yes,no |
| | | properties.setProperty(KAPTCHA_BORDER, "yes"); |
| | | // 验证码文本字符颜色 默认为Color.BLACK |
| | | properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "black"); |
| | | // 验证码图片宽度 默认为200 |
| | | properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160"); |
| | | // 验证码图片高度 默认为50 |
| | | properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60"); |
| | | // 验证码文本字符大小 默认为40 |
| | | properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "38"); |
| | | // KAPTCHA_SESSION_KEY |
| | | properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCode"); |
| | | // 验证码文本字符长度 默认为5 |
| | | properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "4"); |
| | | // 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize) |
| | | properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier"); |
| | | // 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpy |
| | | properties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy"); |
| | | Config config = new Config(properties); |
| | | defaultKaptcha.setConfig(config); |
| | | return defaultKaptcha; |
| | | } |
| | | |
| | | @Bean(name = "captchaProducerMath") |
| | | public DefaultKaptcha getKaptchaBeanMath() { |
| | | DefaultKaptcha defaultKaptcha = new DefaultKaptcha(); |
| | | Properties properties = new Properties(); |
| | | // 是否有边框 默认为true 我们可以自己设置yes,no |
| | | properties.setProperty(KAPTCHA_BORDER, "yes"); |
| | | // 边框颜色 默认为Color.BLACK |
| | | properties.setProperty(KAPTCHA_BORDER_COLOR, "105,179,90"); |
| | | // 验证码文本字符颜色 默认为Color.BLACK |
| | | properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "blue"); |
| | | // 验证码图片宽度 默认为200 |
| | | properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160"); |
| | | // 验证码图片高度 默认为50 |
| | | properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60"); |
| | | // 验证码文本字符大小 默认为40 |
| | | properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "35"); |
| | | // KAPTCHA_SESSION_KEY |
| | | properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCodeMath"); |
| | | // 验证码文本生成器 |
| | | properties.setProperty(KAPTCHA_TEXTPRODUCER_IMPL, "com.ruoyi.gateway.config.KaptchaTextCreator"); |
| | | // 验证码文本字符间距 默认为2 |
| | | properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_SPACE, "3"); |
| | | // 验证码文本字符长度 默认为5 |
| | | properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "6"); |
| | | // 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize) |
| | | properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier"); |
| | | // 验证码噪点颜色 默认为Color.BLACK |
| | | properties.setProperty(KAPTCHA_NOISE_COLOR, "white"); |
| | | // 干扰实现类 |
| | | properties.setProperty(KAPTCHA_NOISE_IMPL, "com.google.code.kaptcha.impl.NoNoise"); |
| | | // 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpy |
| | | properties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy"); |
| | | Config config = new Config(properties); |
| | | defaultKaptcha.setConfig(config); |
| | | return defaultKaptcha; |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.gateway.config; |
| | | |
| | | import org.springframework.context.annotation.Bean; |
| | | import org.springframework.context.annotation.Configuration; |
| | | import org.springframework.core.Ordered; |
| | | import org.springframework.core.annotation.Order; |
| | | import com.ruoyi.gateway.handler.SentinelFallbackHandler; |
| | | |
| | | /** |
| | | * 网关限流配置 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | @Configuration |
| | | public class GatewayConfig |
| | | { |
| | | @Bean |
| | | @Order(Ordered.HIGHEST_PRECEDENCE) |
| | | public SentinelFallbackHandler sentinelGatewayExceptionHandler() |
| | | { |
| | | return new SentinelFallbackHandler(); |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.gateway.config; |
| | | |
| | | import java.util.Random; |
| | | import com.google.code.kaptcha.text.impl.DefaultTextCreator; |
| | | |
| | | /** |
| | | * 验证码文本生成器 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class KaptchaTextCreator extends DefaultTextCreator |
| | | { |
| | | private static final String[] CNUMBERS = "0,1,2,3,4,5,6,7,8,9,10".split(","); |
| | | |
| | | @Override |
| | | public String getText() |
| | | { |
| | | Integer result = 0; |
| | | Random random = new Random(); |
| | | int x = random.nextInt(10); |
| | | int y = random.nextInt(10); |
| | | StringBuilder suChinese = new StringBuilder(); |
| | | int randomoperands = random.nextInt(3); |
| | | if (randomoperands == 0) |
| | | { |
| | | result = x * y; |
| | | suChinese.append(CNUMBERS[x]); |
| | | suChinese.append("*"); |
| | | suChinese.append(CNUMBERS[y]); |
| | | } |
| | | else if (randomoperands == 1) |
| | | { |
| | | if ((x != 0) && y % x == 0) |
| | | { |
| | | result = y / x; |
| | | suChinese.append(CNUMBERS[y]); |
| | | suChinese.append("/"); |
| | | suChinese.append(CNUMBERS[x]); |
| | | } |
| | | else |
| | | { |
| | | result = x + y; |
| | | suChinese.append(CNUMBERS[x]); |
| | | suChinese.append("+"); |
| | | suChinese.append(CNUMBERS[y]); |
| | | } |
| | | } |
| | | else if (randomoperands == 2) |
| | | { |
| | | if (x >= y) |
| | | { |
| | | result = x - y; |
| | | suChinese.append(CNUMBERS[x]); |
| | | suChinese.append("-"); |
| | | suChinese.append(CNUMBERS[y]); |
| | | } |
| | | else |
| | | { |
| | | result = y - x; |
| | | suChinese.append(CNUMBERS[y]); |
| | | suChinese.append("-"); |
| | | suChinese.append(CNUMBERS[x]); |
| | | } |
| | | } |
| | | else |
| | | { |
| | | result = x + y; |
| | | suChinese.append(CNUMBERS[x]); |
| | | suChinese.append("+"); |
| | | suChinese.append(CNUMBERS[y]); |
| | | } |
| | | suChinese.append("=?@" + result); |
| | | return suChinese.toString(); |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.gateway.config; |
| | | |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.context.annotation.Bean; |
| | | import org.springframework.context.annotation.Configuration; |
| | | import org.springframework.http.MediaType; |
| | | import org.springframework.web.reactive.function.server.RequestPredicates; |
| | | import org.springframework.web.reactive.function.server.RouterFunction; |
| | | import org.springframework.web.reactive.function.server.RouterFunctions; |
| | | import com.ruoyi.gateway.handler.ValidateCodeHandler; |
| | | |
| | | /** |
| | | * 路由配置信息 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | @Configuration |
| | | public class RouterFunctionConfiguration { |
| | | @Autowired |
| | | private ValidateCodeHandler validateCodeHandler; |
| | | |
| | | @SuppressWarnings("rawtypes") |
| | | @Bean |
| | | public RouterFunction routerFunction() { |
| | | return RouterFunctions.route( |
| | | RequestPredicates.GET("/code").and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), |
| | | validateCodeHandler); |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.gateway.config; |
| | | |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.cloud.gateway.config.GatewayProperties; |
| | | import org.springframework.cloud.gateway.route.RouteLocator; |
| | | import org.springframework.cloud.gateway.support.NameUtils; |
| | | import org.springframework.context.annotation.Lazy; |
| | | import org.springframework.stereotype.Component; |
| | | import org.springframework.web.reactive.config.ResourceHandlerRegistry; |
| | | import org.springframework.web.reactive.config.WebFluxConfigurer; |
| | | import springfox.documentation.swagger.web.SwaggerResource; |
| | | import springfox.documentation.swagger.web.SwaggerResourcesProvider; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * 聚合系统接口 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | @Component |
| | | public class SwaggerProvider implements SwaggerResourcesProvider, WebFluxConfigurer { |
| | | /** |
| | | * Swagger2默认的url后缀 |
| | | */ |
| | | public static final String SWAGGER2URL = "/v2/api-docs"; |
| | | |
| | | /** |
| | | * 网关路由 |
| | | */ |
| | | @Lazy |
| | | @Autowired |
| | | private RouteLocator routeLocator; |
| | | |
| | | @Autowired |
| | | private GatewayProperties gatewayProperties; |
| | | |
| | | /** |
| | | * 聚合其他服务接口 |
| | | * |
| | | * @return |
| | | */ |
| | | @Override |
| | | public List<SwaggerResource> get() { |
| | | List<SwaggerResource> resourceList = new ArrayList<>(); |
| | | List<String> routes = new ArrayList<>(); |
| | | // 获取网关中配置的route |
| | | routeLocator.getRoutes().subscribe(route -> routes.add(route.getId())); |
| | | gatewayProperties.getRoutes().stream() |
| | | .filter(routeDefinition -> routes |
| | | .contains(routeDefinition.getId())) |
| | | .forEach(routeDefinition -> routeDefinition.getPredicates().stream() |
| | | .filter(predicateDefinition -> "Path".equalsIgnoreCase(predicateDefinition.getName())) |
| | | .filter(predicateDefinition -> !"ruoyi-auth".equalsIgnoreCase(routeDefinition.getId())) |
| | | .forEach(predicateDefinition -> resourceList |
| | | .add(swaggerResource(routeDefinition.getId(), predicateDefinition.getArgs() |
| | | .get(NameUtils.GENERATED_NAME_PREFIX + "0").replace("/**", SWAGGER2URL))))); |
| | | return resourceList; |
| | | } |
| | | |
| | | private SwaggerResource swaggerResource(String name, String location) { |
| | | SwaggerResource swaggerResource = new SwaggerResource(); |
| | | swaggerResource.setName(name); |
| | | swaggerResource.setLocation(location); |
| | | swaggerResource.setSwaggerVersion("2.0"); |
| | | return swaggerResource; |
| | | } |
| | | |
| | | @Override |
| | | public void addResourceHandlers(ResourceHandlerRegistry registry) { |
| | | /** swagger-ui 地址 */ |
| | | registry.addResourceHandler("/swagger-ui/**") |
| | | .addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/"); |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.gateway.config.properties; |
| | | |
| | | import org.springframework.boot.context.properties.ConfigurationProperties; |
| | | import org.springframework.cloud.context.config.annotation.RefreshScope; |
| | | import org.springframework.context.annotation.Configuration; |
| | | |
| | | /** |
| | | * 验证码配置 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | @Configuration |
| | | @RefreshScope |
| | | @ConfigurationProperties(prefix = "security.captcha") |
| | | public class CaptchaProperties |
| | | { |
| | | /** |
| | | * 验证码开关 |
| | | */ |
| | | private Boolean enabled; |
| | | |
| | | /** |
| | | * 验证码类型(math 数组计算 char 字符) |
| | | */ |
| | | private String type; |
| | | |
| | | public Boolean getEnabled() |
| | | { |
| | | return enabled; |
| | | } |
| | | |
| | | public void setEnabled(Boolean enabled) |
| | | { |
| | | this.enabled = enabled; |
| | | } |
| | | |
| | | public String getType() |
| | | { |
| | | return type; |
| | | } |
| | | |
| | | public void setType(String type) |
| | | { |
| | | this.type = type; |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.gateway.config.properties; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | import org.springframework.boot.context.properties.ConfigurationProperties; |
| | | import org.springframework.cloud.context.config.annotation.RefreshScope; |
| | | import org.springframework.context.annotation.Configuration; |
| | | |
| | | /** |
| | | * 放行白名单配置 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | @Configuration |
| | | @RefreshScope |
| | | @ConfigurationProperties(prefix = "security.ignore") |
| | | public class IgnoreWhiteProperties |
| | | { |
| | | /** |
| | | * 放行白名单配置,网关不校验此处的白名单 |
| | | */ |
| | | private List<String> whites = new ArrayList<>(); |
| | | |
| | | public List<String> getWhites() |
| | | { |
| | | return whites; |
| | | } |
| | | |
| | | public void setWhites(List<String> whites) |
| | | { |
| | | this.whites = whites; |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.gateway.config.properties; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | import org.springframework.boot.context.properties.ConfigurationProperties; |
| | | import org.springframework.cloud.context.config.annotation.RefreshScope; |
| | | import org.springframework.context.annotation.Configuration; |
| | | |
| | | /** |
| | | * XSS跨站脚本配置 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | @Configuration |
| | | @RefreshScope |
| | | @ConfigurationProperties(prefix = "security.xss") |
| | | public class XssProperties |
| | | { |
| | | /** |
| | | * Xss开关 |
| | | */ |
| | | private Boolean enabled; |
| | | |
| | | /** |
| | | * 排除路径 |
| | | */ |
| | | private List<String> excludeUrls = new ArrayList<>(); |
| | | |
| | | public Boolean getEnabled() |
| | | { |
| | | return enabled; |
| | | } |
| | | |
| | | public void setEnabled(Boolean enabled) |
| | | { |
| | | this.enabled = enabled; |
| | | } |
| | | |
| | | public List<String> getExcludeUrls() |
| | | { |
| | | return excludeUrls; |
| | | } |
| | | |
| | | public void setExcludeUrls(List<String> excludeUrls) |
| | | { |
| | | this.excludeUrls = excludeUrls; |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.gateway.filter; |
| | | |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.cloud.gateway.filter.GatewayFilterChain; |
| | | import org.springframework.cloud.gateway.filter.GlobalFilter; |
| | | import org.springframework.core.Ordered; |
| | | import org.springframework.http.server.reactive.ServerHttpRequest; |
| | | import org.springframework.stereotype.Component; |
| | | import org.springframework.web.server.ServerWebExchange; |
| | | import com.ruoyi.common.core.constant.CacheConstants; |
| | | import com.ruoyi.common.core.constant.HttpStatus; |
| | | import com.ruoyi.common.core.constant.SecurityConstants; |
| | | import com.ruoyi.common.core.constant.TokenConstants; |
| | | import com.ruoyi.common.core.utils.JwtUtils; |
| | | import com.ruoyi.common.core.utils.ServletUtils; |
| | | import com.ruoyi.common.core.utils.StringUtils; |
| | | import com.ruoyi.common.redis.service.RedisService; |
| | | import com.ruoyi.gateway.config.properties.IgnoreWhiteProperties; |
| | | import io.jsonwebtoken.Claims; |
| | | import reactor.core.publisher.Mono; |
| | | |
| | | /** |
| | | * 网关鉴权 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | @Component |
| | | public class AuthFilter implements GlobalFilter, Ordered { |
| | | private static final Logger log = LoggerFactory.getLogger(AuthFilter.class); |
| | | |
| | | /** |
| | | * 排除过滤的 uri 地址,nacos自行添加 |
| | | */ |
| | | @Autowired |
| | | private IgnoreWhiteProperties ignoreWhite; |
| | | |
| | | @Autowired |
| | | private RedisService redisService; |
| | | |
| | | |
| | | @Override |
| | | public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { |
| | | ServerHttpRequest request = exchange.getRequest(); |
| | | ServerHttpRequest.Builder mutate = request.mutate(); |
| | | |
| | | String url = request.getURI().getPath(); |
| | | // 跳过不需要验证的路径 |
| | | if (StringUtils.matches(url, ignoreWhite.getWhites())) { |
| | | return chain.filter(exchange); |
| | | } |
| | | String token = getToken(request); |
| | | if (StringUtils.isEmpty(token)) { |
| | | return unauthorizedResponse(exchange, "令牌不能为空"); |
| | | } |
| | | Claims claims = JwtUtils.parseToken(token); |
| | | if (claims == null) { |
| | | return unauthorizedResponse(exchange, "令牌已过期或验证不正确!"); |
| | | } |
| | | String userkey = JwtUtils.getUserKey(claims); |
| | | String userkey1 = JwtUtils.getUserKeyByUser(claims); |
| | | String userkey2 = JwtUtils.getUserKeyByWorker(claims); |
| | | boolean islogin = redisService.hasKey(getTokenKey(userkey)); |
| | | boolean islogin1 = redisService.hasKey(getTokenKeyByUser(userkey1)); |
| | | boolean islogin2 = redisService.hasKey(getTokenKeyByWorker(userkey2)); |
| | | if (!islogin && !islogin1 && !islogin2) { |
| | | return unauthorizedResponse(exchange, "登录状态已过期"); |
| | | } |
| | | String userid = JwtUtils.getUserId(claims); |
| | | String username = JwtUtils.getUserName(claims); |
| | | if (StringUtils.isEmpty(userid) || StringUtils.isEmpty(username)) { |
| | | return unauthorizedResponse(exchange, "令牌验证失败"); |
| | | } |
| | | |
| | | // 设置用户信息到请求 |
| | | if (StringUtils.isNoneBlank(userkey)) { |
| | | addHeader(mutate, SecurityConstants.USER_KEY, userkey); |
| | | } |
| | | if (StringUtils.isNoneBlank(userkey1)) { |
| | | addHeader(mutate, SecurityConstants.USER_USER_KEY, userkey1); |
| | | } |
| | | if (StringUtils.isNoneBlank(userkey2)) { |
| | | addHeader(mutate, SecurityConstants.USER_WORKER_KEY, userkey2); |
| | | } |
| | | addHeader(mutate, SecurityConstants.DETAILS_USER_ID, userid); |
| | | addHeader(mutate, SecurityConstants.DETAILS_USERNAME, username); |
| | | // 内部请求来源参数清除 |
| | | removeHeader(mutate, SecurityConstants.FROM_SOURCE); |
| | | return chain.filter(exchange.mutate().request(mutate.build()).build()); |
| | | } |
| | | |
| | | private void addHeader(ServerHttpRequest.Builder mutate, String name, Object value) { |
| | | if (value == null) { |
| | | return; |
| | | } |
| | | String valueStr = value.toString(); |
| | | String valueEncode = ServletUtils.urlEncode(valueStr); |
| | | mutate.header(name, valueEncode); |
| | | } |
| | | |
| | | private void removeHeader(ServerHttpRequest.Builder mutate, String name) { |
| | | mutate.headers(httpHeaders -> httpHeaders.remove(name)).build(); |
| | | } |
| | | |
| | | private Mono<Void> unauthorizedResponse(ServerWebExchange exchange, String msg) { |
| | | log.error("[鉴权异常处理]请求路径:{}", exchange.getRequest().getPath()); |
| | | return ServletUtils.webFluxResponseWriter(exchange.getResponse(), msg, HttpStatus.UNAUTHORIZED); |
| | | } |
| | | |
| | | /** |
| | | * 获取缓存key |
| | | */ |
| | | private String getTokenKey(String token) { |
| | | return CacheConstants.LOGIN_TOKEN_KEY + token; |
| | | } |
| | | |
| | | /** |
| | | * 获取缓存key |
| | | */ |
| | | private String getTokenKeyByUser(String token) { |
| | | return CacheConstants.LOGIN_USER_TOKEN_KEY + token; |
| | | } |
| | | |
| | | /** |
| | | * 获取缓存key |
| | | */ |
| | | private String getTokenKeyByWorker(String token) { |
| | | return CacheConstants.LOGIN_WORKER_TOKEN_KEY + token; |
| | | } |
| | | |
| | | /** |
| | | * 获取请求token |
| | | */ |
| | | private String getToken(ServerHttpRequest request) { |
| | | String token = request.getHeaders().getFirst(TokenConstants.AUTHENTICATION); |
| | | // 如果前端设置了令牌前缀,则裁剪掉前缀 |
| | | if (StringUtils.isNotEmpty(token) && token.startsWith(TokenConstants.PREFIX)) { |
| | | token = token.replaceFirst(TokenConstants.PREFIX, StringUtils.EMPTY); |
| | | } |
| | | return token; |
| | | } |
| | | |
| | | @Override |
| | | public int getOrder() { |
| | | return -200; |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.gateway.filter; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | import java.util.regex.Pattern; |
| | | import org.springframework.cloud.gateway.filter.GatewayFilter; |
| | | import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory; |
| | | import org.springframework.stereotype.Component; |
| | | import com.ruoyi.common.core.utils.ServletUtils; |
| | | |
| | | /** |
| | | * 黑名单过滤器 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | @Component |
| | | public class BlackListUrlFilter extends AbstractGatewayFilterFactory<BlackListUrlFilter.Config> |
| | | { |
| | | @Override |
| | | public GatewayFilter apply(Config config) |
| | | { |
| | | return (exchange, chain) -> { |
| | | |
| | | String url = exchange.getRequest().getURI().getPath(); |
| | | if (config.matchBlacklist(url)) |
| | | { |
| | | return ServletUtils.webFluxResponseWriter(exchange.getResponse(), "请求地址不允许访问"); |
| | | } |
| | | |
| | | return chain.filter(exchange); |
| | | }; |
| | | } |
| | | |
| | | public BlackListUrlFilter() |
| | | { |
| | | super(Config.class); |
| | | } |
| | | |
| | | public static class Config |
| | | { |
| | | private List<String> blacklistUrl; |
| | | |
| | | private List<Pattern> blacklistUrlPattern = new ArrayList<>(); |
| | | |
| | | public boolean matchBlacklist(String url) |
| | | { |
| | | return !blacklistUrlPattern.isEmpty() && blacklistUrlPattern.stream().anyMatch(p -> p.matcher(url).find()); |
| | | } |
| | | |
| | | public List<String> getBlacklistUrl() |
| | | { |
| | | return blacklistUrl; |
| | | } |
| | | |
| | | public void setBlacklistUrl(List<String> blacklistUrl) |
| | | { |
| | | this.blacklistUrl = blacklistUrl; |
| | | this.blacklistUrlPattern.clear(); |
| | | this.blacklistUrl.forEach(url -> { |
| | | this.blacklistUrlPattern.add(Pattern.compile(url.replaceAll("\\*\\*", "(.*?)"), Pattern.CASE_INSENSITIVE)); |
| | | }); |
| | | } |
| | | } |
| | | |
| | | } |
New file |
| | |
| | | package com.ruoyi.gateway.filter; |
| | | |
| | | import org.springframework.cloud.gateway.filter.GatewayFilter; |
| | | import org.springframework.cloud.gateway.filter.GatewayFilterChain; |
| | | import org.springframework.cloud.gateway.filter.OrderedGatewayFilter; |
| | | import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory; |
| | | import org.springframework.cloud.gateway.support.ServerWebExchangeUtils; |
| | | import org.springframework.http.HttpMethod; |
| | | import org.springframework.stereotype.Component; |
| | | import org.springframework.web.server.ServerWebExchange; |
| | | import reactor.core.publisher.Mono; |
| | | |
| | | import java.util.Collections; |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * 获取body请求数据(解决流不能重复读取问题) |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | @Component |
| | | public class CacheRequestFilter extends AbstractGatewayFilterFactory<CacheRequestFilter.Config> { |
| | | public CacheRequestFilter() { |
| | | super(Config.class); |
| | | } |
| | | |
| | | @Override |
| | | public String name() { |
| | | return "CacheRequestFilter"; |
| | | } |
| | | |
| | | @Override |
| | | public GatewayFilter apply(Config config) { |
| | | CacheRequestGatewayFilter cacheRequestGatewayFilter = new CacheRequestGatewayFilter(); |
| | | Integer order = config.getOrder(); |
| | | if (order == null) { |
| | | return cacheRequestGatewayFilter; |
| | | } |
| | | return new OrderedGatewayFilter(cacheRequestGatewayFilter, order); |
| | | } |
| | | |
| | | public static class CacheRequestGatewayFilter implements GatewayFilter { |
| | | @Override |
| | | public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { |
| | | // GET DELETE 不过滤 |
| | | HttpMethod method = exchange.getRequest().getMethod(); |
| | | if (method == null || method == HttpMethod.GET || method == HttpMethod.DELETE) { |
| | | return chain.filter(exchange); |
| | | } |
| | | return ServerWebExchangeUtils.cacheRequestBodyAndRequest(exchange, (serverHttpRequest) -> { |
| | | if (serverHttpRequest == exchange.getRequest()) { |
| | | return chain.filter(exchange); |
| | | } |
| | | return chain.filter(exchange.mutate().request(serverHttpRequest).build()); |
| | | }); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public List<String> shortcutFieldOrder() { |
| | | return Collections.singletonList("order"); |
| | | } |
| | | |
| | | static class Config { |
| | | private Integer order; |
| | | |
| | | public Integer getOrder() { |
| | | return order; |
| | | } |
| | | |
| | | public void setOrder(Integer order) { |
| | | this.order = order; |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.gateway.filter; |
| | | |
| | | import com.alibaba.fastjson2.JSON; |
| | | import com.alibaba.fastjson2.JSONObject; |
| | | import com.ruoyi.common.core.utils.ServletUtils; |
| | | import com.ruoyi.common.core.utils.StringUtils; |
| | | import com.ruoyi.gateway.config.properties.CaptchaProperties; |
| | | import com.ruoyi.gateway.service.ValidateCodeService; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.cloud.gateway.filter.GatewayFilter; |
| | | import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory; |
| | | import org.springframework.core.io.buffer.DataBuffer; |
| | | import org.springframework.core.io.buffer.DataBufferUtils; |
| | | import org.springframework.http.server.reactive.ServerHttpRequest; |
| | | import org.springframework.stereotype.Component; |
| | | import reactor.core.publisher.Flux; |
| | | |
| | | import java.nio.CharBuffer; |
| | | import java.nio.charset.StandardCharsets; |
| | | import java.util.concurrent.atomic.AtomicReference; |
| | | |
| | | /** |
| | | * 验证码过滤器 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | @Component |
| | | public class ValidateCodeFilter extends AbstractGatewayFilterFactory<Object> { |
| | | private final static String[] VALIDATE_URL = new String[]{"/auth/login", "/auth/register"}; |
| | | |
| | | @Autowired |
| | | private ValidateCodeService validateCodeService; |
| | | |
| | | @Autowired |
| | | private CaptchaProperties captchaProperties; |
| | | |
| | | private static final String CODE = "code"; |
| | | |
| | | private static final String UUID = "uuid"; |
| | | |
| | | @Override |
| | | public GatewayFilter apply(Object config) { |
| | | return (exchange, chain) -> { |
| | | ServerHttpRequest request = exchange.getRequest(); |
| | | |
| | | // 非登录/注册请求或验证码关闭,不处理 |
| | | if (!StringUtils.equalsAnyIgnoreCase(request.getURI().getPath(), VALIDATE_URL) || !captchaProperties.getEnabled()) { |
| | | return chain.filter(exchange); |
| | | } |
| | | |
| | | try { |
| | | String rspStr = resolveBodyFromRequest(request); |
| | | JSONObject obj = JSON.parseObject(rspStr); |
| | | validateCodeService.checkCaptcha(obj.getString(CODE), obj.getString(UUID)); |
| | | } catch (Exception e) { |
| | | return ServletUtils.webFluxResponseWriter(exchange.getResponse(), e.getMessage()); |
| | | } |
| | | return chain.filter(exchange); |
| | | }; |
| | | } |
| | | |
| | | private String resolveBodyFromRequest(ServerHttpRequest serverHttpRequest) { |
| | | // 获取请求体 |
| | | Flux<DataBuffer> body = serverHttpRequest.getBody(); |
| | | AtomicReference<String> bodyRef = new AtomicReference<>(); |
| | | body.subscribe(buffer -> { |
| | | CharBuffer charBuffer = StandardCharsets.UTF_8.decode(buffer.asByteBuffer()); |
| | | DataBufferUtils.release(buffer); |
| | | bodyRef.set(charBuffer.toString()); |
| | | }); |
| | | return bodyRef.get(); |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.gateway.filter; |
| | | |
| | | import com.ruoyi.common.core.utils.StringUtils; |
| | | import com.ruoyi.common.core.utils.html.EscapeUtil; |
| | | import com.ruoyi.gateway.config.properties.XssProperties; |
| | | import io.netty.buffer.ByteBufAllocator; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
| | | import org.springframework.cloud.gateway.filter.GatewayFilterChain; |
| | | import org.springframework.cloud.gateway.filter.GlobalFilter; |
| | | import org.springframework.core.Ordered; |
| | | import org.springframework.core.io.buffer.*; |
| | | import org.springframework.http.HttpHeaders; |
| | | import org.springframework.http.HttpMethod; |
| | | import org.springframework.http.MediaType; |
| | | import org.springframework.http.server.reactive.ServerHttpRequest; |
| | | import org.springframework.http.server.reactive.ServerHttpRequestDecorator; |
| | | import org.springframework.stereotype.Component; |
| | | import org.springframework.web.server.ServerWebExchange; |
| | | import reactor.core.publisher.Flux; |
| | | import reactor.core.publisher.Mono; |
| | | |
| | | import java.nio.charset.StandardCharsets; |
| | | |
| | | /** |
| | | * 跨站脚本过滤器 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | @Component |
| | | @ConditionalOnProperty(value = "security.xss.enabled", havingValue = "true") |
| | | public class XssFilter implements GlobalFilter, Ordered { |
| | | |
| | | /** |
| | | * 跨站脚本的 xss 配置,nacos自行添加 |
| | | */ |
| | | @Autowired |
| | | private XssProperties xss; |
| | | |
| | | @Override |
| | | public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { |
| | | ServerHttpRequest request = exchange.getRequest(); |
| | | // xss开关未开启 或 通过nacos关闭,不过滤 |
| | | if (!xss.getEnabled()) { |
| | | return chain.filter(exchange); |
| | | } |
| | | // GET DELETE 不过滤 |
| | | HttpMethod method = request.getMethod(); |
| | | if (method == null || method == HttpMethod.GET || method == HttpMethod.DELETE) { |
| | | return chain.filter(exchange); |
| | | } |
| | | // 非json类型,不过滤 |
| | | if (!isJsonRequest(exchange)) { |
| | | return chain.filter(exchange); |
| | | } |
| | | // excludeUrls 不过滤 |
| | | String url = request.getURI().getPath(); |
| | | if (StringUtils.matches(url, xss.getExcludeUrls())) { |
| | | return chain.filter(exchange); |
| | | } |
| | | ServerHttpRequestDecorator httpRequestDecorator = requestDecorator(exchange); |
| | | return chain.filter(exchange.mutate().request(httpRequestDecorator).build()); |
| | | |
| | | } |
| | | |
| | | private ServerHttpRequestDecorator requestDecorator(ServerWebExchange exchange) { |
| | | ServerHttpRequestDecorator serverHttpRequestDecorator = new ServerHttpRequestDecorator(exchange.getRequest()) { |
| | | @Override |
| | | public Flux<DataBuffer> getBody() { |
| | | Flux<DataBuffer> body = super.getBody(); |
| | | return body.buffer().map(dataBuffers -> { |
| | | DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory(); |
| | | DataBuffer join = dataBufferFactory.join(dataBuffers); |
| | | byte[] content = new byte[join.readableByteCount()]; |
| | | join.read(content); |
| | | DataBufferUtils.release(join); |
| | | String bodyStr = new String(content, StandardCharsets.UTF_8); |
| | | // 防xss攻击过滤 |
| | | bodyStr = EscapeUtil.clean(bodyStr); |
| | | // 转成字节 |
| | | byte[] bytes = bodyStr.getBytes(StandardCharsets.UTF_8); |
| | | NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(ByteBufAllocator.DEFAULT); |
| | | DataBuffer buffer = nettyDataBufferFactory.allocateBuffer(bytes.length); |
| | | buffer.write(bytes); |
| | | return buffer; |
| | | }); |
| | | } |
| | | |
| | | @Override |
| | | public HttpHeaders getHeaders() { |
| | | HttpHeaders httpHeaders = new HttpHeaders(); |
| | | httpHeaders.putAll(super.getHeaders()); |
| | | // 由于修改了请求体的body,导致content-length长度不确定,因此需要删除原先的content-length |
| | | httpHeaders.remove(HttpHeaders.CONTENT_LENGTH); |
| | | httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked"); |
| | | return httpHeaders; |
| | | } |
| | | |
| | | }; |
| | | return serverHttpRequestDecorator; |
| | | } |
| | | |
| | | /** |
| | | * 是否是Json请求 |
| | | * |
| | | * @param exchange HTTP请求 |
| | | */ |
| | | public boolean isJsonRequest(ServerWebExchange exchange) { |
| | | String header = exchange.getRequest().getHeaders().getFirst(HttpHeaders.CONTENT_TYPE); |
| | | return StringUtils.startsWithIgnoreCase(header, MediaType.APPLICATION_JSON_VALUE); |
| | | } |
| | | |
| | | @Override |
| | | public int getOrder() { |
| | | return -100; |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.gateway.handler; |
| | | |
| | | import org.springframework.cloud.gateway.support.NotFoundException; |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler; |
| | | import org.springframework.context.annotation.Configuration; |
| | | import org.springframework.core.annotation.Order; |
| | | import org.springframework.http.server.reactive.ServerHttpResponse; |
| | | import org.springframework.web.server.ResponseStatusException; |
| | | import org.springframework.web.server.ServerWebExchange; |
| | | import com.ruoyi.common.core.utils.ServletUtils; |
| | | import reactor.core.publisher.Mono; |
| | | |
| | | /** |
| | | * 网关统一异常处理 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | @Order(-1) |
| | | @Configuration |
| | | public class GatewayExceptionHandler implements ErrorWebExceptionHandler |
| | | { |
| | | private static final Logger log = LoggerFactory.getLogger(GatewayExceptionHandler.class); |
| | | |
| | | @Override |
| | | public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) |
| | | { |
| | | ServerHttpResponse response = exchange.getResponse(); |
| | | |
| | | if (exchange.getResponse().isCommitted()) |
| | | { |
| | | return Mono.error(ex); |
| | | } |
| | | |
| | | String msg; |
| | | |
| | | if (ex instanceof NotFoundException) |
| | | { |
| | | msg = "服务未找到"; |
| | | } |
| | | else if (ex instanceof ResponseStatusException) |
| | | { |
| | | ResponseStatusException responseStatusException = (ResponseStatusException) ex; |
| | | msg = responseStatusException.getMessage(); |
| | | } |
| | | else |
| | | { |
| | | msg = "内部服务器错误"; |
| | | } |
| | | |
| | | log.error("[网关异常处理]请求路径:{},异常信息:{}", exchange.getRequest().getPath(), ex.getMessage()); |
| | | |
| | | return ServletUtils.webFluxResponseWriter(response, msg); |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.gateway.handler; |
| | | |
| | | import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager; |
| | | import com.alibaba.csp.sentinel.slots.block.BlockException; |
| | | import com.ruoyi.common.core.utils.ServletUtils; |
| | | import org.springframework.web.reactive.function.server.ServerResponse; |
| | | import org.springframework.web.server.ServerWebExchange; |
| | | import org.springframework.web.server.WebExceptionHandler; |
| | | import reactor.core.publisher.Mono; |
| | | |
| | | /** |
| | | * 自定义限流异常处理 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public class SentinelFallbackHandler implements WebExceptionHandler |
| | | { |
| | | private Mono<Void> writeResponse(ServerResponse response, ServerWebExchange exchange) |
| | | { |
| | | return ServletUtils.webFluxResponseWriter(exchange.getResponse(), "请求超过最大数,请稍候再试"); |
| | | } |
| | | |
| | | @Override |
| | | public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) |
| | | { |
| | | if (exchange.getResponse().isCommitted()) |
| | | { |
| | | return Mono.error(ex); |
| | | } |
| | | if (!BlockException.isBlockException(ex)) |
| | | { |
| | | return Mono.error(ex); |
| | | } |
| | | return handleBlockedRequest(exchange, ex).flatMap(response -> writeResponse(response, exchange)); |
| | | } |
| | | |
| | | private Mono<ServerResponse> handleBlockedRequest(ServerWebExchange exchange, Throwable throwable) |
| | | { |
| | | return GatewayCallbackManager.getBlockHandler().handleRequest(exchange, throwable); |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.gateway.handler; |
| | | |
| | | import java.util.Optional; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.http.HttpStatus; |
| | | import org.springframework.http.ResponseEntity; |
| | | import org.springframework.web.bind.annotation.GetMapping; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | import reactor.core.publisher.Mono; |
| | | import springfox.documentation.swagger.web.SecurityConfiguration; |
| | | import springfox.documentation.swagger.web.SecurityConfigurationBuilder; |
| | | import springfox.documentation.swagger.web.SwaggerResourcesProvider; |
| | | import springfox.documentation.swagger.web.UiConfiguration; |
| | | import springfox.documentation.swagger.web.UiConfigurationBuilder; |
| | | |
| | | @RestController |
| | | @RequestMapping("/swagger-resources") |
| | | public class SwaggerHandler |
| | | { |
| | | @Autowired(required = false) |
| | | private SecurityConfiguration securityConfiguration; |
| | | |
| | | @Autowired(required = false) |
| | | private UiConfiguration uiConfiguration; |
| | | |
| | | private final SwaggerResourcesProvider swaggerResources; |
| | | |
| | | @Autowired |
| | | public SwaggerHandler(SwaggerResourcesProvider swaggerResources) |
| | | { |
| | | this.swaggerResources = swaggerResources; |
| | | } |
| | | |
| | | @GetMapping("/configuration/security") |
| | | public Mono<ResponseEntity<SecurityConfiguration>> securityConfiguration() |
| | | { |
| | | return Mono.just(new ResponseEntity<>( |
| | | Optional.ofNullable(securityConfiguration).orElse(SecurityConfigurationBuilder.builder().build()), |
| | | HttpStatus.OK)); |
| | | } |
| | | |
| | | @GetMapping("/configuration/ui") |
| | | public Mono<ResponseEntity<UiConfiguration>> uiConfiguration() |
| | | { |
| | | return Mono.just(new ResponseEntity<>( |
| | | Optional.ofNullable(uiConfiguration).orElse(UiConfigurationBuilder.builder().build()), HttpStatus.OK)); |
| | | } |
| | | |
| | | @SuppressWarnings("rawtypes") |
| | | @GetMapping("") |
| | | public Mono<ResponseEntity> swaggerResources() |
| | | { |
| | | return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK))); |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.gateway.handler; |
| | | |
| | | import com.ruoyi.common.core.exception.CaptchaException; |
| | | import com.ruoyi.common.core.web.domain.AjaxResult; |
| | | import com.ruoyi.gateway.service.ValidateCodeService; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.http.HttpStatus; |
| | | import org.springframework.stereotype.Component; |
| | | import org.springframework.web.reactive.function.BodyInserters; |
| | | import org.springframework.web.reactive.function.server.HandlerFunction; |
| | | import org.springframework.web.reactive.function.server.ServerRequest; |
| | | import org.springframework.web.reactive.function.server.ServerResponse; |
| | | import reactor.core.publisher.Mono; |
| | | |
| | | import java.io.IOException; |
| | | |
| | | /** |
| | | * 验证码获取 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | @Component |
| | | public class ValidateCodeHandler implements HandlerFunction<ServerResponse> { |
| | | @Autowired |
| | | private ValidateCodeService validateCodeService; |
| | | |
| | | @Override |
| | | public Mono<ServerResponse> handle(ServerRequest serverRequest) { |
| | | AjaxResult ajax; |
| | | try { |
| | | ajax = validateCodeService.createCaptcha(); |
| | | } catch (CaptchaException | IOException e) { |
| | | return Mono.error(e); |
| | | } |
| | | return ServerResponse.status(HttpStatus.OK).body(BodyInserters.fromValue(ajax)); |
| | | } |
| | | } |
New file |
| | |
| | | package com.ruoyi.gateway.service; |
| | | |
| | | import java.io.IOException; |
| | | import com.ruoyi.common.core.exception.CaptchaException; |
| | | import com.ruoyi.common.core.web.domain.AjaxResult; |
| | | |
| | | /** |
| | | * 验证码处理 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | public interface ValidateCodeService |
| | | { |
| | | /** |
| | | * 生成验证码 |
| | | */ |
| | | public AjaxResult createCaptcha() throws IOException, CaptchaException; |
| | | |
| | | /** |
| | | * 校验验证码 |
| | | */ |
| | | public void checkCaptcha(String key, String value) throws CaptchaException; |
| | | } |
New file |
| | |
| | | package com.ruoyi.gateway.service.impl; |
| | | |
| | | import com.google.code.kaptcha.Producer; |
| | | import com.ruoyi.common.core.constant.CacheConstants; |
| | | import com.ruoyi.common.core.constant.Constants; |
| | | import com.ruoyi.common.core.exception.CaptchaException; |
| | | import com.ruoyi.common.core.utils.StringUtils; |
| | | import com.ruoyi.common.core.utils.sign.Base64; |
| | | import com.ruoyi.common.core.utils.uuid.IdUtils; |
| | | import com.ruoyi.common.core.web.domain.AjaxResult; |
| | | import com.ruoyi.common.redis.service.RedisService; |
| | | import com.ruoyi.gateway.config.properties.CaptchaProperties; |
| | | import com.ruoyi.gateway.service.ValidateCodeService; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.util.FastByteArrayOutputStream; |
| | | |
| | | import javax.annotation.Resource; |
| | | import javax.imageio.ImageIO; |
| | | import java.awt.image.BufferedImage; |
| | | import java.io.IOException; |
| | | import java.util.concurrent.TimeUnit; |
| | | |
| | | /** |
| | | * 验证码实现处理 |
| | | * |
| | | * @author ruoyi |
| | | */ |
| | | @Service |
| | | public class ValidateCodeServiceImpl implements ValidateCodeService { |
| | | @Resource(name = "captchaProducer") |
| | | private Producer captchaProducer; |
| | | |
| | | @Resource(name = "captchaProducerMath") |
| | | private Producer captchaProducerMath; |
| | | |
| | | @Autowired |
| | | private RedisService redisService; |
| | | |
| | | @Autowired |
| | | private CaptchaProperties captchaProperties; |
| | | |
| | | /** |
| | | * 生成验证码 |
| | | */ |
| | | @Override |
| | | public AjaxResult createCaptcha() throws IOException, CaptchaException { |
| | | AjaxResult ajax = AjaxResult.success(); |
| | | boolean captchaEnabled = captchaProperties.getEnabled(); |
| | | ajax.put("captchaEnabled", captchaEnabled); |
| | | if (!captchaEnabled) { |
| | | return ajax; |
| | | } |
| | | |
| | | // 保存验证码信息 |
| | | String uuid = IdUtils.simpleUUID(); |
| | | String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + uuid; |
| | | |
| | | String capStr = null, code = null; |
| | | BufferedImage image = null; |
| | | |
| | | String captchaType = captchaProperties.getType(); |
| | | // 生成验证码 |
| | | 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); |
| | | } |
| | | |
| | | redisService.setCacheObject(verifyKey, 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; |
| | | } |
| | | |
| | | /** |
| | | * 校验验证码 |
| | | */ |
| | | @Override |
| | | public void checkCaptcha(String code, String uuid) throws CaptchaException { |
| | | if (StringUtils.isEmpty(code)) { |
| | | throw new CaptchaException("验证码不能为空"); |
| | | } |
| | | if (StringUtils.isEmpty(uuid)) { |
| | | throw new CaptchaException("验证码已失效"); |
| | | } |
| | | String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + uuid; |
| | | String captcha = redisService.getCacheObject(verifyKey); |
| | | redisService.deleteObject(verifyKey); |
| | | |
| | | if (!code.equalsIgnoreCase(captcha)) { |
| | | throw new CaptchaException("验证码错误"); |
| | | } |
| | | } |
| | | } |
ruoyi-gateway/src/main/resources/banner.txt
ruoyi-gateway/src/main/resources/bootstrap.yml
ruoyi-gateway/src/main/resources/logback.xml
ruoyi-modules/pom.xml
ruoyi-modules/ruoyi-file/pom.xml
ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/RuoYiFileApplication.java
ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/config/MinioConfig.java
ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/config/ResourcesConfig.java
ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/controller/SysFileController.java
ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/service/FastDfsSysFileServiceImpl.java
ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/service/ISysFileService.java
ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/service/LocalSysFileServiceImpl.java
ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/service/MinioSysFileServiceImpl.java
ruoyi-modules/ruoyi-file/src/main/java/com/ruoyi/file/utils/FileUploadUtils.java
ruoyi-modules/ruoyi-file/src/main/resources/banner.txt
ruoyi-modules/ruoyi-file/src/main/resources/bootstrap.yml
ruoyi-modules/ruoyi-file/src/main/resources/logback.xml
ruoyi-modules/ruoyi-gen/pom.xml
ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/RuoYiGenApplication.java
ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/config/GenConfig.java
ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/controller/GenController.java
ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/domain/GenTable.java
ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/domain/GenTableColumn.java
ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/mapper/GenTableColumnMapper.java
ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/mapper/GenTableMapper.java
ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/service/GenTableColumnServiceImpl.java
ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/service/GenTableServiceImpl.java
ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/service/IGenTableColumnService.java
ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/service/IGenTableService.java
ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/util/GenUtils.java
ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/util/VelocityInitializer.java
ruoyi-modules/ruoyi-gen/src/main/java/com/ruoyi/gen/util/VelocityUtils.java
ruoyi-modules/ruoyi-gen/src/main/resources/banner.txt
ruoyi-modules/ruoyi-gen/src/main/resources/bootstrap.yml
ruoyi-modules/ruoyi-gen/src/main/resources/logback.xml
ruoyi-modules/ruoyi-gen/src/main/resources/mapper/generator/GenTableColumnMapper.xml
ruoyi-modules/ruoyi-gen/src/main/resources/mapper/generator/GenTableMapper.xml
ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/controller.java.vm
ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/domain.java.vm
ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/mapper.java.vm
ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/service.java.vm
ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/serviceImpl.java.vm
ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/sub-domain.java.vm
ruoyi-modules/ruoyi-gen/src/main/resources/vm/js/api.js.vm
ruoyi-modules/ruoyi-gen/src/main/resources/vm/sql/sql.vm
ruoyi-modules/ruoyi-gen/src/main/resources/vm/vue/index-tree.vue.vm
ruoyi-modules/ruoyi-gen/src/main/resources/vm/vue/index.vue.vm
ruoyi-modules/ruoyi-gen/src/main/resources/vm/vue/v3/index-tree.vue.vm
ruoyi-modules/ruoyi-gen/src/main/resources/vm/vue/v3/index.vue.vm
ruoyi-modules/ruoyi-gen/src/main/resources/vm/xml/mapper.xml.vm
ruoyi-modules/ruoyi-job/pom.xml
ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/RuoYiJobApplication.java
ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/config/ScheduleConfig.java
ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/controller/SysJobController.java
ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/controller/SysJobLogController.java
ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/domain/SysJob.java
ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/domain/SysJobLog.java
ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/mapper/SysJobLogMapper.java
ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/mapper/SysJobMapper.java
ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/service/ISysJobLogService.java
ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/service/ISysJobService.java
ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/service/SysJobLogServiceImpl.java
ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/service/SysJobServiceImpl.java
ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/task/RyTask.java
ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/util/AbstractQuartzJob.java
ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/util/CronUtils.java
ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/util/JobInvokeUtil.java
ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/util/QuartzDisallowConcurrentExecution.java
ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/util/QuartzJobExecution.java
ruoyi-modules/ruoyi-job/src/main/java/com/ruoyi/job/util/ScheduleUtils.java
ruoyi-modules/ruoyi-job/src/main/resources/banner.txt
ruoyi-modules/ruoyi-job/src/main/resources/bootstrap.yml
ruoyi-modules/ruoyi-job/src/main/resources/logback.xml
ruoyi-modules/ruoyi-job/src/main/resources/mapper/job/SysJobLogMapper.xml
ruoyi-modules/ruoyi-job/src/main/resources/mapper/job/SysJobMapper.xml
ruoyi-modules/ruoyi-system/pom.xml
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/RuoYiSystemApplication.java
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysConfigController.java
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysDictDataController.java
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysDictTypeController.java
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysLogininforController.java
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysMenuController.java
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysNoticeController.java
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysOperlogController.java
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysPostController.java
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysProfileController.java
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysRoleController.java
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysUserController.java
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysUserOnlineController.java
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysConfig.java
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysMenu.java
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysNotice.java
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysPost.java
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysRoleDept.java
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysRoleMenu.java
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserOnline.java
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserPost.java
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserRole.java
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/MetaVo.java
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/RouterVo.java
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/TreeSelect.java
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysConfigMapper.java
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDictDataMapper.java
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDictTypeMapper.java
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysLogininforMapper.java
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysMenuMapper.java
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysNoticeMapper.java
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysOperLogMapper.java
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysPostMapper.java
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleDeptMapper.java
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleMapper.java
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleMenuMapper.java
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserPostMapper.java
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserRoleMapper.java
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/request/SysRoleRequest.java
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysConfigService.java
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDictDataService.java
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDictTypeService.java
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysLogininforService.java
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysMenuService.java
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysNoticeService.java
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysOperLogService.java
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysPermissionService.java
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysPostService.java
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysRoleService.java
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserOnlineService.java
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserService.java
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysConfigServiceImpl.java
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictDataServiceImpl.java
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictTypeServiceImpl.java
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysLogininforServiceImpl.java
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysNoticeServiceImpl.java
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOperLogServiceImpl.java
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysPermissionServiceImpl.java
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysPostServiceImpl.java
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysRoleServiceImpl.java
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserOnlineServiceImpl.java
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/vo/MenuTreeVO.java
ruoyi-modules/ruoyi-system/src/main/resources/applications.yaml
ruoyi-modules/ruoyi-system/src/main/resources/banner.txt
ruoyi-modules/ruoyi-system/src/main/resources/bootstrap.yml
ruoyi-modules/ruoyi-system/src/main/resources/logback.xml
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysConfigMapper.xml
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysDictDataMapper.xml
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysDictTypeMapper.xml
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysLogininforMapper.xml
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysMenuMapper.xml
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysNoticeMapper.xml
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysOperLogMapper.xml
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysPostMapper.xml
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysRoleDeptMapper.xml
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysRoleMapper.xml
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysRoleMenuMapper.xml
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserPostMapper.xml
ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysUserRoleMapper.xml
ruoyi-modules/ruoyi-system/src/main/resources/mybatis-config.xml
ruoyi-service/.gitignore
ruoyi-service/pom.xml
ruoyi-service/ruoyi-admin/.gitignore
ruoyi-service/ruoyi-admin/pom.xml
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/RuoYiAdminApplication.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/config/MyDateObjectHandler.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/config/MybatisPlusConfig.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/controller/AgreementController.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/controller/ChangeDispatchController.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/controller/EvaluateController.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/controller/FranchiseeController.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/controller/HomePageController.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/controller/MasterWorkerController.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/controller/NoticesController.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/controller/OrderController.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/controller/OssController.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/controller/PrizeController.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/controller/ProblemController.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/controller/RecoveryClassifyController.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/controller/RecoveryServeController.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/controller/RecoveryServePriceController.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/controller/RegionController.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/controller/RotateController.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/controller/SendSmsController.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/controller/ServeAdvantageController.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/controller/ServeCoordinateController.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/controller/ServeRecordController.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/controller/SiteController.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/controller/UserController.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/controller/UserManageController.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/controller/WithdrawController.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/controller/WithdrawalSettingController.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/controller/WorkerProcessController.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/entity/Agreement.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/entity/ChangeDispatch.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/entity/Evaluate.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/entity/Franchisee.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/entity/MasterWorker.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/entity/Notices.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/entity/Order.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/entity/Prize.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/entity/Problem.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/entity/RecoveryClassify.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/entity/RecoveryServe.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/entity/RecoveryServePrice.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/entity/Region.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/entity/Rotate.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/entity/ServeAdvantage.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/entity/ServeCoordinate.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/entity/ServeRecord.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/entity/Site.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/entity/User.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/entity/Withdraw.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/entity/WithdrawalSetting.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/entity/WorkerProcess.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/mapper/AgreementMapper.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/mapper/ChangeDispatchMapper.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/mapper/EvaluateMapper.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/mapper/FranchiseeMapper.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/mapper/MasterWorkerMapper.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/mapper/NoticesMapper.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/mapper/OrderMapper.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/mapper/PrizeMapper.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/mapper/ProblemMapper.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/mapper/RecoveryClassifyMapper.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/mapper/RecoveryServeMapper.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/mapper/RecoveryServePriceMapper.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/mapper/RegionMapper.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/mapper/RotateMapper.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/mapper/ServeAdvantageMapper.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/mapper/ServeCoordinateMapper.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/mapper/ServeRecordMapper.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/mapper/SiteMapper.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/mapper/UserMapper.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/mapper/WithdrawMapper.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/mapper/WithdrawalSettingMapper.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/mapper/WorkerProcessMapper.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/request/OrderCountRequest.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/request/OrderQueryRequest.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/request/UserDataCountRequest.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/service/AgreementService.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/service/ChangeDispatchService.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/service/EvaluateService.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/service/FranchiseeService.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/service/MasterWorkerService.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/service/NoticesService.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/service/OrderService.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/service/PrizeService.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/service/ProblemService.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/service/RecoveryClassifyService.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/service/RecoveryServePriceService.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/service/RecoveryServeService.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/service/RegionService.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/service/RotateService.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/service/SendSmsService.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/service/ServeAdvantageService.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/service/ServeCoordinateService.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/service/ServeRecordService.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/service/SiteService.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/service/UserService.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/service/WithdrawService.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/service/WithdrawalSettingService.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/service/WorkerProcessService.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/service/impl/AgreementServiceImpl.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/service/impl/ChangeDispatchServiceImpl.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/service/impl/EvaluateServiceImpl.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/service/impl/FranchiseeServiceImpl.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/service/impl/MasterWorkerServiceImpl.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/service/impl/NoticesServiceImpl.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/service/impl/OrderServiceImpl.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/service/impl/PrizeServiceImpl.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/service/impl/ProblemServiceImpl.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/service/impl/RecoveryClassifyServiceImpl.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/service/impl/RecoveryServePriceServiceImpl.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/service/impl/RecoveryServeServiceImpl.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/service/impl/RegionServiceImpl.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/service/impl/RotateServiceImpl.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/service/impl/SendSmsServiceImpl.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/service/impl/ServeAdvantageServiceImpl.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/service/impl/ServeCoordinateServiceImpl.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/service/impl/ServeRecordServiceImpl.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/service/impl/SiteServiceImpl.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/service/impl/UserServiceImpl.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/service/impl/WithdrawServiceImpl.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/service/impl/WithdrawalSettingServiceImpl.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/service/impl/WorkerProcessServiceImpl.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/utils/GeneratorCodeConfig.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/utils/ObsUploadUtil.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/vo/EvaluatePageVO.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/vo/MasterWorkerDetailVO.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/vo/MasterWorkerRankVO.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/vo/OrderByEvaluateVO.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/vo/OrderByServeRecordVO.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/vo/OrderByServeVO.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/vo/OrderByUserInfoVO.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/vo/OrderByWorkerVO.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/vo/OrderCountVO.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/vo/OrderDetailVO.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/vo/OrderInfoVO.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/vo/OrderQueryVO.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/vo/OrderResultVO.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/vo/UserDataCountVO.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/vo/UserTrendsVO.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/vo/UserWithdrawRecordRequestVO.java
ruoyi-service/ruoyi-admin/src/main/java/com/ruoyi/admin/vo/UserWithdrawRecordVO.java
ruoyi-service/ruoyi-admin/src/main/resources/bootstrap.yaml
ruoyi-service/ruoyi-admin/src/main/resources/mapper/admin/AgreementMapper.xml
ruoyi-service/ruoyi-admin/src/main/resources/mapper/admin/ChangeDispatchMapper.xml
ruoyi-service/ruoyi-admin/src/main/resources/mapper/admin/EvaluateMapper.xml
ruoyi-service/ruoyi-admin/src/main/resources/mapper/admin/FranchiseeMapper.xml
ruoyi-service/ruoyi-admin/src/main/resources/mapper/admin/MasterWorkerMapper.xml
ruoyi-service/ruoyi-admin/src/main/resources/mapper/admin/NoticesMapper.xml
ruoyi-service/ruoyi-admin/src/main/resources/mapper/admin/OrderMapper.xml
ruoyi-service/ruoyi-admin/src/main/resources/mapper/admin/PrizeMapper.xml
ruoyi-service/ruoyi-admin/src/main/resources/mapper/admin/ProblemMapper.xml
ruoyi-service/ruoyi-admin/src/main/resources/mapper/admin/RecoveryClassifyMapper.xml
ruoyi-service/ruoyi-admin/src/main/resources/mapper/admin/RecoveryServeMapper.xml
ruoyi-service/ruoyi-admin/src/main/resources/mapper/admin/RecoveryServePriceMapper.xml
ruoyi-service/ruoyi-admin/src/main/resources/mapper/admin/RegionMapper.xml
ruoyi-service/ruoyi-admin/src/main/resources/mapper/admin/RotateMapper.xml
ruoyi-service/ruoyi-admin/src/main/resources/mapper/admin/ServeAdvantageMapper.xml
ruoyi-service/ruoyi-admin/src/main/resources/mapper/admin/ServeCoordinateMapper.xml
ruoyi-service/ruoyi-admin/src/main/resources/mapper/admin/ServeRecordMapper.xml
ruoyi-service/ruoyi-admin/src/main/resources/mapper/admin/SiteMapper.xml
ruoyi-service/ruoyi-admin/src/main/resources/mapper/admin/UserMapper.xml
ruoyi-service/ruoyi-admin/src/main/resources/mapper/admin/WithdrawMapper.xml
ruoyi-service/ruoyi-admin/src/main/resources/mapper/admin/WithdrawalSettingMapper.xml
ruoyi-service/ruoyi-admin/src/main/resources/mapper/admin/WorkerProcessMapper.xml
ruoyi-service/ruoyi-admin/src/main/resources/mybatis-config.xml
ruoyi-service/ruoyi-admin/src/main/resources/template/提现记录.xlsx
ruoyi-service/ruoyi-admin/src/main/resources/template/订单记录.xlsx
ruoyi-service/ruoyi-user/.gitignore
ruoyi-service/ruoyi-user/pom.xml
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/RuoYiUserApplication.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/annotate/PasswordLogin.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/annotate/PhoneCodeLogin.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/config/MyDateObjectHandler.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/config/MybatisPlusConfig.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/controller/EvaluateController.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/controller/OrderController.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/controller/RecoveryClassifyController.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/controller/RecoveryServeController.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/controller/RegionController.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/controller/ServeRecordController.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/controller/UserCollectController.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/controller/UserController.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/controller/UserRecipientController.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/controller/WithdrawController.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/entity/Evaluate.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/entity/Order.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/entity/RecoveryClassify.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/entity/RecoveryServe.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/entity/Region.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/entity/ServeRecord.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/entity/User.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/entity/UserCollect.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/entity/UserRecipient.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/entity/Withdraw.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/mapper/EvaluateMapper.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/mapper/OrderMapper.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/mapper/RecoveryClassifyMapper.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/mapper/RecoveryServeMapper.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/mapper/RegionMapper.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/mapper/ServeRecordMapper.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/mapper/UserCollectMapper.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/mapper/UserMapper.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/mapper/UserRecipientMapper.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/mapper/WithdrawMapper.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/request/LoginPhoneRequest.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/request/OrderEvaluateRequest.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/request/OrderRequest.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/service/EvaluateService.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/service/OrderService.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/service/RecoveryClassifyService.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/service/RecoveryServeService.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/service/RegionService.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/service/ServeRecordService.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/service/UserCollectService.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/service/UserRecipientService.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/service/UserService.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/service/WithdrawService.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/service/impl/EvaluateServiceImpl.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/service/impl/OrderServiceImpl.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/service/impl/RecoveryClassifyServiceImpl.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/service/impl/RecoveryServeServiceImpl.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/service/impl/RegionServiceImpl.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/service/impl/ServeRecordServiceImpl.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/service/impl/UserCollectServiceImpl.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/service/impl/UserRecipientServiceImpl.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/service/impl/UserServiceImpl.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/service/impl/WithdrawServiceImpl.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/vo/EvaluateInfoVO.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/vo/EvaluateOrderInfoVO.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/vo/OrderDetailVO.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/vo/OrderEvaluateVO.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/vo/ServeListVO.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/vo/ServeProbablyVO.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/vo/ServeQueryVO.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/vo/UserCollectVO.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/vo/WithdrawListVO.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/vo/WithdrawMoneyVO.java
ruoyi-service/ruoyi-user/src/main/java/com/ruoyi/user/vo/WithdrawOrderVO.java
ruoyi-service/ruoyi-user/src/main/resources/bootstrap.yaml
ruoyi-service/ruoyi-user/src/main/resources/mapper/user/EvaluateMapper.xml
ruoyi-service/ruoyi-user/src/main/resources/mapper/user/OrderMapper.xml
ruoyi-service/ruoyi-user/src/main/resources/mapper/user/RecoveryClassifyMapper.xml
ruoyi-service/ruoyi-user/src/main/resources/mapper/user/RecoveryServeMapper.xml
ruoyi-service/ruoyi-user/src/main/resources/mapper/user/RegionMapper.xml
ruoyi-service/ruoyi-user/src/main/resources/mapper/user/ServeRecordMapper.xml
ruoyi-service/ruoyi-user/src/main/resources/mapper/user/UserCollectMapper.xml
ruoyi-service/ruoyi-user/src/main/resources/mapper/user/UserMapper.xml
ruoyi-service/ruoyi-user/src/main/resources/mapper/user/UserRecipientMapper.xml
ruoyi-service/ruoyi-user/src/main/resources/mapper/user/WithdrawMapper.xml
ruoyi-service/ruoyi-user/src/main/resources/mybatis-config.xml
ruoyi-service/ruoyi-worker/.gitignore
ruoyi-service/ruoyi-worker/pom.xml
ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/RuoYiWorkerApplication.java
ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/config/MyDateObjectHandler.java
ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/config/MybatisPlusConfig.java
ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/controller/EvaluateController.java
ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/controller/MasterWorkerController.java
ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/controller/OrderController.java
ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/controller/RecoveryClassifyController.java
ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/controller/RecoveryServeController.java
ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/controller/ServeRecordController.java
ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/controller/WorkerProcessController.java
ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/entity/Evaluate.java
ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/entity/MasterWorker.java
ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/entity/Order.java
ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/entity/RecoveryClassify.java
ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/entity/RecoveryServe.java
ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/entity/ServeRecord.java
ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/entity/WorkerProcess.java
ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/mapper/EvaluateMapper.java
ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/mapper/MasterWorkerMapper.java
ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/mapper/OrderMapper.java
ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/mapper/RecoveryClassifyMapper.java
ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/mapper/RecoveryServeMapper.java
ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/mapper/ServeRecordMapper.java
ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/mapper/WorkerProcessMapper.java
ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/request/LoginPasswordRequest.java
ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/request/LoginPhoneRequest.java
ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/request/OrderSubmitRequest.java
ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/request/WorkerLoginRequest.java
ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/service/EvaluateService.java
ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/service/MasterWorkerService.java
ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/service/OrderService.java
ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/service/RecoveryClassifyService.java
ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/service/RecoveryServeService.java
ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/service/ServeRecordService.java
ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/service/WorkerProcessService.java
ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/service/impl/EvaluateServiceImpl.java
ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/service/impl/MasterWorkerServiceImpl.java
ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/service/impl/OrderServiceImpl.java
ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/service/impl/RecoveryClassifyServiceImpl.java
ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/service/impl/RecoveryServeServiceImpl.java
ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/service/impl/ServeRecordServiceImpl.java
ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/service/impl/WorkerProcessServiceImpl.java
ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/vo/HomePageInfoVO.java
ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/vo/OrderCountVO.java
ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/vo/OrderDetailVO.java
ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/vo/OrderListVO.java
ruoyi-service/ruoyi-worker/src/main/java/com/ruoyi/worker/vo/OrderNotHandleVO.java
ruoyi-service/ruoyi-worker/src/main/resources/bootstrap.yaml
ruoyi-service/ruoyi-worker/src/main/resources/mapper/worker/EvaluateMapper.xml
ruoyi-service/ruoyi-worker/src/main/resources/mapper/worker/MasterWorkerMapper.xml
ruoyi-service/ruoyi-worker/src/main/resources/mapper/worker/OrderMapper.xml
ruoyi-service/ruoyi-worker/src/main/resources/mapper/worker/RecoveryClassifyMapper.xml
ruoyi-service/ruoyi-worker/src/main/resources/mapper/worker/RecoveryServeMapper.xml
ruoyi-service/ruoyi-worker/src/main/resources/mapper/worker/ServeRecordMapper.xml
ruoyi-service/ruoyi-worker/src/main/resources/mapper/worker/WorkerProcessMapper.xml
ruoyi-service/ruoyi-worker/src/main/resources/mybatis-config.xml
ruoyi-ui/.editorconfig
ruoyi-ui/.env.development
ruoyi-ui/.env.production
ruoyi-ui/.env.staging
ruoyi-ui/.eslintignore
ruoyi-ui/.eslintrc.js
ruoyi-ui/.gitignore
ruoyi-ui/README.md
ruoyi-ui/babel.config.js
ruoyi-ui/bin/build.bat
ruoyi-ui/bin/package.bat
ruoyi-ui/bin/run-web.bat
ruoyi-ui/build/index.js
ruoyi-ui/package.json
ruoyi-ui/public/favicon.ico
ruoyi-ui/public/html/ie.html
ruoyi-ui/public/index.html
ruoyi-ui/public/robots.txt
ruoyi-ui/src/App.vue
ruoyi-ui/src/api/login.js
ruoyi-ui/src/api/menu.js
ruoyi-ui/src/api/monitor/job.js
ruoyi-ui/src/api/monitor/jobLog.js
ruoyi-ui/src/api/monitor/online.js
ruoyi-ui/src/api/system/config.js
ruoyi-ui/src/api/system/dept.js
ruoyi-ui/src/api/system/dict/data.js
ruoyi-ui/src/api/system/dict/type.js
ruoyi-ui/src/api/system/logininfor.js
ruoyi-ui/src/api/system/menu.js
ruoyi-ui/src/api/system/notice.js
ruoyi-ui/src/api/system/operlog.js
ruoyi-ui/src/api/system/post.js
ruoyi-ui/src/api/system/role.js
ruoyi-ui/src/api/system/user.js
ruoyi-ui/src/api/tool/gen.js
ruoyi-ui/src/assets/401_images/401.gif
ruoyi-ui/src/assets/404_images/404.png
ruoyi-ui/src/assets/404_images/404_cloud.png
ruoyi-ui/src/assets/icons/index.js
ruoyi-ui/src/assets/icons/svg/404.svg
ruoyi-ui/src/assets/icons/svg/bug.svg
ruoyi-ui/src/assets/icons/svg/build.svg
ruoyi-ui/src/assets/icons/svg/button.svg
ruoyi-ui/src/assets/icons/svg/cascader.svg
ruoyi-ui/src/assets/icons/svg/chart.svg
ruoyi-ui/src/assets/icons/svg/checkbox.svg
ruoyi-ui/src/assets/icons/svg/client.svg
ruoyi-ui/src/assets/icons/svg/clipboard.svg
ruoyi-ui/src/assets/icons/svg/code.svg
ruoyi-ui/src/assets/icons/svg/color.svg
ruoyi-ui/src/assets/icons/svg/component.svg
ruoyi-ui/src/assets/icons/svg/dashboard.svg
ruoyi-ui/src/assets/icons/svg/date-range.svg
ruoyi-ui/src/assets/icons/svg/date.svg
ruoyi-ui/src/assets/icons/svg/dict.svg
ruoyi-ui/src/assets/icons/svg/documentation.svg
ruoyi-ui/src/assets/icons/svg/download.svg
ruoyi-ui/src/assets/icons/svg/drag.svg
ruoyi-ui/src/assets/icons/svg/druid.svg
ruoyi-ui/src/assets/icons/svg/edit.svg
ruoyi-ui/src/assets/icons/svg/education.svg
ruoyi-ui/src/assets/icons/svg/email.svg
ruoyi-ui/src/assets/icons/svg/example.svg
ruoyi-ui/src/assets/icons/svg/excel.svg
ruoyi-ui/src/assets/icons/svg/exit-fullscreen.svg
ruoyi-ui/src/assets/icons/svg/eye-open.svg
ruoyi-ui/src/assets/icons/svg/eye.svg
ruoyi-ui/src/assets/icons/svg/form.svg
ruoyi-ui/src/assets/icons/svg/fullscreen.svg
ruoyi-ui/src/assets/icons/svg/github.svg
ruoyi-ui/src/assets/icons/svg/guide.svg
ruoyi-ui/src/assets/icons/svg/icon.svg
ruoyi-ui/src/assets/icons/svg/input.svg
ruoyi-ui/src/assets/icons/svg/international.svg
ruoyi-ui/src/assets/icons/svg/job.svg
ruoyi-ui/src/assets/icons/svg/language.svg
ruoyi-ui/src/assets/icons/svg/link.svg
ruoyi-ui/src/assets/icons/svg/list.svg
ruoyi-ui/src/assets/icons/svg/lock.svg
ruoyi-ui/src/assets/icons/svg/log.svg
ruoyi-ui/src/assets/icons/svg/logininfor.svg
ruoyi-ui/src/assets/icons/svg/message.svg
ruoyi-ui/src/assets/icons/svg/money.svg
ruoyi-ui/src/assets/icons/svg/monitor.svg
ruoyi-ui/src/assets/icons/svg/nacos.svg
ruoyi-ui/src/assets/icons/svg/nested.svg
ruoyi-ui/src/assets/icons/svg/number.svg
ruoyi-ui/src/assets/icons/svg/online.svg
ruoyi-ui/src/assets/icons/svg/password.svg
ruoyi-ui/src/assets/icons/svg/pdf.svg
ruoyi-ui/src/assets/icons/svg/people.svg
ruoyi-ui/src/assets/icons/svg/peoples.svg
ruoyi-ui/src/assets/icons/svg/phone.svg
ruoyi-ui/src/assets/icons/svg/post.svg
ruoyi-ui/src/assets/icons/svg/qq.svg
ruoyi-ui/src/assets/icons/svg/question.svg
ruoyi-ui/src/assets/icons/svg/radio.svg
ruoyi-ui/src/assets/icons/svg/rate.svg
ruoyi-ui/src/assets/icons/svg/row.svg
ruoyi-ui/src/assets/icons/svg/search.svg
ruoyi-ui/src/assets/icons/svg/select.svg
ruoyi-ui/src/assets/icons/svg/sentinel.svg
ruoyi-ui/src/assets/icons/svg/server.svg
ruoyi-ui/src/assets/icons/svg/shopping.svg
ruoyi-ui/src/assets/icons/svg/size.svg
ruoyi-ui/src/assets/icons/svg/skill.svg
ruoyi-ui/src/assets/icons/svg/slider.svg
ruoyi-ui/src/assets/icons/svg/star.svg
ruoyi-ui/src/assets/icons/svg/swagger.svg
ruoyi-ui/src/assets/icons/svg/switch.svg
ruoyi-ui/src/assets/icons/svg/system.svg
ruoyi-ui/src/assets/icons/svg/tab.svg
ruoyi-ui/src/assets/icons/svg/table.svg
ruoyi-ui/src/assets/icons/svg/textarea.svg
ruoyi-ui/src/assets/icons/svg/theme.svg
ruoyi-ui/src/assets/icons/svg/time-range.svg
ruoyi-ui/src/assets/icons/svg/time.svg
ruoyi-ui/src/assets/icons/svg/tool.svg
ruoyi-ui/src/assets/icons/svg/tree-table.svg
ruoyi-ui/src/assets/icons/svg/tree.svg
ruoyi-ui/src/assets/icons/svg/upload.svg
ruoyi-ui/src/assets/icons/svg/user.svg
ruoyi-ui/src/assets/icons/svg/validCode.svg
ruoyi-ui/src/assets/icons/svg/wechat.svg
ruoyi-ui/src/assets/icons/svg/zip.svg
ruoyi-ui/src/assets/icons/svgo.yml
ruoyi-ui/src/assets/images/dark.svg
ruoyi-ui/src/assets/images/light.svg
ruoyi-ui/src/assets/images/login-background.jpg
ruoyi-ui/src/assets/images/pay.png
ruoyi-ui/src/assets/images/profile.jpg
ruoyi-ui/src/assets/logo/logo.png
ruoyi-ui/src/assets/styles/btn.scss
ruoyi-ui/src/assets/styles/element-ui.scss
ruoyi-ui/src/assets/styles/element-variables.scss
ruoyi-ui/src/assets/styles/index.scss
ruoyi-ui/src/assets/styles/mixin.scss
ruoyi-ui/src/assets/styles/ruoyi.scss
ruoyi-ui/src/assets/styles/sidebar.scss
ruoyi-ui/src/assets/styles/transition.scss
ruoyi-ui/src/assets/styles/variables.scss
ruoyi-ui/src/components/Breadcrumb/index.vue
ruoyi-ui/src/components/Crontab/day.vue
ruoyi-ui/src/components/Crontab/hour.vue
ruoyi-ui/src/components/Crontab/index.vue
ruoyi-ui/src/components/Crontab/min.vue
ruoyi-ui/src/components/Crontab/month.vue
ruoyi-ui/src/components/Crontab/result.vue
ruoyi-ui/src/components/Crontab/second.vue
ruoyi-ui/src/components/Crontab/week.vue
ruoyi-ui/src/components/Crontab/year.vue
ruoyi-ui/src/components/DictData/index.js
ruoyi-ui/src/components/DictTag/index.vue
ruoyi-ui/src/components/Editor/index.vue
ruoyi-ui/src/components/FileUpload/index.vue
ruoyi-ui/src/components/Hamburger/index.vue
ruoyi-ui/src/components/HeaderSearch/index.vue
ruoyi-ui/src/components/IconSelect/index.vue
ruoyi-ui/src/components/IconSelect/requireIcons.js
ruoyi-ui/src/components/ImagePreview/index.vue
ruoyi-ui/src/components/ImageUpload/index.vue
ruoyi-ui/src/components/Pagination/index.vue
ruoyi-ui/src/components/PanThumb/index.vue
ruoyi-ui/src/components/ParentView/index.vue
ruoyi-ui/src/components/RightPanel/index.vue
ruoyi-ui/src/components/RightToolbar/index.vue
ruoyi-ui/src/components/RuoYi/Doc/index.vue
ruoyi-ui/src/components/RuoYi/Git/index.vue
ruoyi-ui/src/components/Screenfull/index.vue
ruoyi-ui/src/components/SizeSelect/index.vue
ruoyi-ui/src/components/SvgIcon/index.vue
ruoyi-ui/src/components/ThemePicker/index.vue
ruoyi-ui/src/components/TopNav/index.vue
ruoyi-ui/src/components/iFrame/index.vue
ruoyi-ui/src/directive/dialog/drag.js
ruoyi-ui/src/directive/dialog/dragHeight.js
ruoyi-ui/src/directive/dialog/dragWidth.js
ruoyi-ui/src/directive/index.js
ruoyi-ui/src/directive/module/clipboard.js
ruoyi-ui/src/directive/permission/hasPermi.js
ruoyi-ui/src/directive/permission/hasRole.js
ruoyi-ui/src/layout/components/AppMain.vue
ruoyi-ui/src/layout/components/IframeToggle/index.vue
ruoyi-ui/src/layout/components/InnerLink/index.vue
ruoyi-ui/src/layout/components/Navbar.vue
ruoyi-ui/src/layout/components/Settings/index.vue
ruoyi-ui/src/layout/components/Sidebar/FixiOSBug.js
ruoyi-ui/src/layout/components/Sidebar/Item.vue
ruoyi-ui/src/layout/components/Sidebar/Link.vue
ruoyi-ui/src/layout/components/Sidebar/Logo.vue
ruoyi-ui/src/layout/components/Sidebar/SidebarItem.vue
ruoyi-ui/src/layout/components/Sidebar/index.vue
ruoyi-ui/src/layout/components/TagsView/ScrollPane.vue
ruoyi-ui/src/layout/components/TagsView/index.vue
ruoyi-ui/src/layout/components/index.js
ruoyi-ui/src/layout/index.vue
ruoyi-ui/src/layout/mixin/ResizeHandler.js
ruoyi-ui/src/main.js
ruoyi-ui/src/permission.js
ruoyi-ui/src/plugins/auth.js
ruoyi-ui/src/plugins/cache.js
ruoyi-ui/src/plugins/download.js
ruoyi-ui/src/plugins/index.js
ruoyi-ui/src/plugins/modal.js
ruoyi-ui/src/plugins/tab.js
ruoyi-ui/src/router/index.js
ruoyi-ui/src/settings.js
ruoyi-ui/src/store/getters.js
ruoyi-ui/src/store/index.js
ruoyi-ui/src/store/modules/app.js
ruoyi-ui/src/store/modules/dict.js
ruoyi-ui/src/store/modules/permission.js
ruoyi-ui/src/store/modules/settings.js
ruoyi-ui/src/store/modules/tagsView.js
ruoyi-ui/src/store/modules/user.js
ruoyi-ui/src/utils/auth.js
ruoyi-ui/src/utils/dict/Dict.js
ruoyi-ui/src/utils/dict/DictConverter.js
ruoyi-ui/src/utils/dict/DictData.js
ruoyi-ui/src/utils/dict/DictMeta.js
ruoyi-ui/src/utils/dict/DictOptions.js
ruoyi-ui/src/utils/dict/index.js
ruoyi-ui/src/utils/errorCode.js
ruoyi-ui/src/utils/generator/config.js
ruoyi-ui/src/utils/generator/css.js
ruoyi-ui/src/utils/generator/drawingDefault.js
ruoyi-ui/src/utils/generator/html.js
ruoyi-ui/src/utils/generator/icon.json
ruoyi-ui/src/utils/generator/js.js
ruoyi-ui/src/utils/generator/render.js
ruoyi-ui/src/utils/index.js
ruoyi-ui/src/utils/jsencrypt.js
ruoyi-ui/src/utils/permission.js
ruoyi-ui/src/utils/request.js
ruoyi-ui/src/utils/ruoyi.js
ruoyi-ui/src/utils/scroll-to.js
ruoyi-ui/src/utils/validate.js
ruoyi-ui/src/views/dashboard/BarChart.vue
ruoyi-ui/src/views/dashboard/LineChart.vue
ruoyi-ui/src/views/dashboard/PanelGroup.vue
ruoyi-ui/src/views/dashboard/PieChart.vue
ruoyi-ui/src/views/dashboard/RaddarChart.vue
ruoyi-ui/src/views/dashboard/mixins/resize.js
ruoyi-ui/src/views/error/401.vue
ruoyi-ui/src/views/error/404.vue
ruoyi-ui/src/views/index.vue
ruoyi-ui/src/views/index_v1.vue
ruoyi-ui/src/views/login.vue
ruoyi-ui/src/views/monitor/job/index.vue
ruoyi-ui/src/views/monitor/job/log.vue
ruoyi-ui/src/views/monitor/online/index.vue
ruoyi-ui/src/views/redirect.vue
ruoyi-ui/src/views/register.vue
ruoyi-ui/src/views/system/config/index.vue
ruoyi-ui/src/views/system/dept/index.vue
ruoyi-ui/src/views/system/dict/data.vue
ruoyi-ui/src/views/system/dict/index.vue
ruoyi-ui/src/views/system/logininfor/index.vue
ruoyi-ui/src/views/system/menu/index.vue
ruoyi-ui/src/views/system/notice/index.vue
ruoyi-ui/src/views/system/operlog/index.vue
ruoyi-ui/src/views/system/post/index.vue
ruoyi-ui/src/views/system/role/authUser.vue
ruoyi-ui/src/views/system/role/index.vue
ruoyi-ui/src/views/system/role/selectUser.vue
ruoyi-ui/src/views/system/user/authRole.vue
ruoyi-ui/src/views/system/user/index.vue
ruoyi-ui/src/views/system/user/profile/index.vue
ruoyi-ui/src/views/system/user/profile/resetPwd.vue
ruoyi-ui/src/views/system/user/profile/userAvatar.vue
ruoyi-ui/src/views/system/user/profile/userInfo.vue
ruoyi-ui/src/views/tool/build/CodeTypeDialog.vue
ruoyi-ui/src/views/tool/build/DraggableItem.vue
ruoyi-ui/src/views/tool/build/IconsDialog.vue
ruoyi-ui/src/views/tool/build/RightPanel.vue
ruoyi-ui/src/views/tool/build/TreeNodeDialog.vue
ruoyi-ui/src/views/tool/build/index.vue
ruoyi-ui/src/views/tool/gen/basicInfoForm.vue
ruoyi-ui/src/views/tool/gen/editTable.vue
ruoyi-ui/src/views/tool/gen/genInfoForm.vue
ruoyi-ui/src/views/tool/gen/importTable.vue
ruoyi-ui/src/views/tool/gen/index.vue
ruoyi-ui/vue.config.js
ruoyi-visual/pom.xml
ruoyi-visual/ruoyi-monitor/pom.xml
ruoyi-visual/ruoyi-monitor/src/main/java/com/ruoyi/modules/monitor/RuoYiMonitorApplication.java
ruoyi-visual/ruoyi-monitor/src/main/java/com/ruoyi/modules/monitor/config/WebSecurityConfigurer.java
ruoyi-visual/ruoyi-monitor/src/main/resources/banner.txt
ruoyi-visual/ruoyi-monitor/src/main/resources/bootstrap.yml
ruoyi-visual/ruoyi-monitor/src/main/resources/logback.xml
sql/quartz.sql
sql/ry_20231130.sql
sql/ry_config_20231204.sql
sql/ry_seata_20210128.sql |