Initial commit
This commit is contained in:
commit
460211a9d8
|
|
@ -0,0 +1 @@
|
||||||
|
custom: http://doc.ruoyi.vip/ruoyi/other/donate.html
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
######################################################################
|
||||||
|
# Build Tools
|
||||||
|
|
||||||
|
.gradle
|
||||||
|
/build/
|
||||||
|
!gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
|
target/
|
||||||
|
!.mvn/wrapper/maven-wrapper.jar
|
||||||
|
|
||||||
|
######################################################################
|
||||||
|
# IDE
|
||||||
|
|
||||||
|
### STS ###
|
||||||
|
.apt_generated
|
||||||
|
.classpath
|
||||||
|
.factorypath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.springBeans
|
||||||
|
|
||||||
|
### IntelliJ IDEA ###
|
||||||
|
.idea
|
||||||
|
*.iws
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
|
||||||
|
### JRebel ###
|
||||||
|
rebel.xml
|
||||||
|
### NetBeans ###
|
||||||
|
nbproject/private/
|
||||||
|
build/*
|
||||||
|
nbbuild/
|
||||||
|
dist/
|
||||||
|
nbdist/
|
||||||
|
.nb-gradle/
|
||||||
|
|
||||||
|
######################################################################
|
||||||
|
# Others
|
||||||
|
*.log
|
||||||
|
*.xml.versionsBackup
|
||||||
|
*.swp
|
||||||
|
|
||||||
|
!*/build/*.java
|
||||||
|
!*/build/*.html
|
||||||
|
!*/build/*.xml
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2018 RuoYi
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
@ -0,0 +1,101 @@
|
||||||
|
<p align="center">
|
||||||
|
<img alt="logo" src="https://oscimg.oschina.net/oscnet/up-dd77653d7c9f197dd9d93684f3c8dcfbab6.png">
|
||||||
|
</p>
|
||||||
|
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">RuoYi v4.7.9</h1>
|
||||||
|
<h4 align="center">基于SpringBoot开发的轻量级Java快速开发框架</h4>
|
||||||
|
<p align="center">
|
||||||
|
<a href="https://gitee.com/y_project/RuoYi/stargazers"><img src="https://gitee.com/y_project/RuoYi/badge/star.svg?theme=gvp"></a>
|
||||||
|
<a href="https://gitee.com/y_project/RuoYi"><img src="https://img.shields.io/badge/RuoYi-v4.7.9-brightgreen.svg"></a>
|
||||||
|
<a href="https://gitee.com/y_project/RuoYi/blob/master/LICENSE"><img src="https://img.shields.io/github/license/mashape/apistatus.svg"></a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
## 平台简介
|
||||||
|
|
||||||
|
一直想做一款后台管理系统,看了很多优秀的开源项目但是发现没有合适的。于是利用空闲休息时间开始自己写了一套后台系统。如此有了若依。她可以用于所有的Web应用程序,如网站管理后台,网站会员中心,CMS,CRM,OA。所有前端后台代码封装过后十分精简易上手,出错概率低。同时支持移动客户端访问。系统会陆续更新一些实用功能。
|
||||||
|
|
||||||
|
性别男,若依是给女儿取的名字(寓意:你若不离不弃,我必生死相依)
|
||||||
|
|
||||||
|
若依是一套全部开源的快速开发平台,毫无保留给个人及企业免费使用。
|
||||||
|
|
||||||
|
* 前后端分离版本,请移步[RuoYi-Vue](https://gitee.com/y_project/RuoYi-Vue),微服务版本,请移步[RuoYi-Cloud](https://gitee.com/y_project/RuoYi-Cloud)
|
||||||
|
* 阿里云折扣场:[点我进入](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)
|
||||||
|
|
||||||
|
## 内置功能
|
||||||
|
|
||||||
|
1. 用户管理:用户是系统操作者,该功能主要完成系统用户配置。
|
||||||
|
2. 部门管理:配置系统组织机构(公司、部门、小组),树结构展现支持数据权限。
|
||||||
|
3. 岗位管理:配置系统用户所属担任职务。
|
||||||
|
4. 菜单管理:配置系统菜单,操作权限,按钮权限标识等。
|
||||||
|
5. 角色管理:角色菜单权限分配、设置角色按机构进行数据范围权限划分。
|
||||||
|
6. 字典管理:对系统中经常使用的一些较为固定的数据进行维护。
|
||||||
|
7. 参数管理:对系统动态配置常用参数。
|
||||||
|
8. 通知公告:系统通知公告信息发布维护。
|
||||||
|
9. 操作日志:系统正常操作日志记录和查询;系统异常信息日志记录和查询。
|
||||||
|
10. 登录日志:系统登录日志记录查询包含登录异常。
|
||||||
|
11. 在线用户:当前系统中活跃用户状态监控。
|
||||||
|
12. 定时任务:在线(添加、修改、删除)任务调度包含执行结果日志。
|
||||||
|
13. 代码生成:前后端代码的生成(java、html、xml、sql)支持CRUD下载 。
|
||||||
|
14. 系统接口:根据业务代码自动生成相关的api接口文档。
|
||||||
|
15. 服务监控:监视当前系统CPU、内存、磁盘、堆栈等相关信息。
|
||||||
|
16. 缓存监控:对系统的缓存查询,删除、清空等操作。
|
||||||
|
17. 在线构建器:拖动表单元素生成相应的HTML代码。
|
||||||
|
18. 连接池监视:监视当前系统数据库连接池状态,可进行分析SQL找出系统性能瓶颈。
|
||||||
|
|
||||||
|
## 在线体验
|
||||||
|
|
||||||
|
- admin/admin123
|
||||||
|
- 陆陆续续收到一些打赏,为了更好的体验已用于演示服务器升级。谢谢各位小伙伴。
|
||||||
|
|
||||||
|
演示地址:http://ruoyi.vip
|
||||||
|
文档地址:http://doc.ruoyi.vip
|
||||||
|
|
||||||
|
## 演示图
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td><img src="https://oscimg.oschina.net/oscnet/up-42e518aa72a24d228427a1261cb3679f395.png"/></td>
|
||||||
|
<td><img src="https://oscimg.oschina.net/oscnet/up-7f20dd0edba25e5187c5c4dd3ec7d3d9797.png"/></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><img src="https://oscimg.oschina.net/oscnet/up-2dae3d87f6a8ca05057db059cd9a411d51d.png"/></td>
|
||||||
|
<td><img src="https://oscimg.oschina.net/oscnet/up-ea4d98423471e55fba784694e45d12bd4bb.png"/></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><img src="https://oscimg.oschina.net/oscnet/up-7f6c6e9f5873efca09bd2870ee8468b8fce.png"/></td>
|
||||||
|
<td><img src="https://oscimg.oschina.net/oscnet/up-c708b65f2c382a03f69fe1efa8d341e6cff.png"/></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><img src="https://oscimg.oschina.net/oscnet/up-9ab586c47dd5c7b92bca0d727962c90e3b8.png"/></td>
|
||||||
|
<td><img src="https://oscimg.oschina.net/oscnet/up-ef954122a2080e02013112db21754b955c6.png"/></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><img src="https://oscimg.oschina.net/oscnet/up-088edb4d531e122415a1e2342bccb1a9691.png"/></td>
|
||||||
|
<td><img src="https://oscimg.oschina.net/oscnet/up-f886fe19bd820c0efae82f680223cac196c.png"/></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><img src="https://oscimg.oschina.net/oscnet/up-c7a2eb71fa65d6e660294b4bccca613d638.png"/></td>
|
||||||
|
<td><img src="https://oscimg.oschina.net/oscnet/up-e60137fb0787defe613bd83331dc4755a70.png"/></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><img src="https://oscimg.oschina.net/oscnet/up-7c51c1b5758f0a0f92ed3c60469b7526f9f.png"/></td>
|
||||||
|
<td><img src="https://oscimg.oschina.net/oscnet/up-15181aed45bb2461aa97b594cbf2f86ea5f.png"/></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><img src="https://oscimg.oschina.net/oscnet/up-83326ad52ea63f67233d126226738054d98.png"/></td>
|
||||||
|
<td><img src="https://oscimg.oschina.net/oscnet/up-3bd6d31e913b70df00107db51d64ef81df7.png"/></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><img src="https://oscimg.oschina.net/oscnet/up-70a2225836bc82042a6785edf6299e2586a.png"/></td>
|
||||||
|
<td><img src="https://oscimg.oschina.net/oscnet/up-0184d6ab01fdc6667a14327fcaf8b46345d.png"/></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><img src="https://oscimg.oschina.net/oscnet/up-64d8086dc2c02c8f71170290482f7640098.png"/></td>
|
||||||
|
<td><img src="https://oscimg.oschina.net/oscnet/up-5e4daac0bb59612c5038448acbcef235e3a.png"/></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
|
||||||
|
## 若依交流群
|
||||||
|
|
||||||
|
QQ群: [](https://jq.qq.com/?_wv=1027&k=5HBAaYN) [](https://jq.qq.com/?_wv=1027&k=5cHeRVW) [](https://jq.qq.com/?_wv=1027&k=53R0L5Z) [](https://jq.qq.com/?_wv=1027&k=5g75dCU) [](https://jq.qq.com/?_wv=1027&k=58cPoHA) [](https://jq.qq.com/?_wv=1027&k=5Ofd4Pb) [](https://jq.qq.com/?_wv=1027&k=5yugASz) [](https://jq.qq.com/?_wv=1027&k=5Rf3d2P) [](https://jq.qq.com/?_wv=1027&k=5ZIjaeP) [](https://jq.qq.com/?_wv=1027&k=5CJw1jY) [](https://jq.qq.com/?_wv=1027&k=5omzbKc) [](https://jq.qq.com/?_wv=1027&k=qPIKBb7s) [](https://jq.qq.com/?_wv=1027&k=4NsjKbtU) [](https://jq.qq.com/?_wv=1027&k=VD2pkz2G) [](https://jq.qq.com/?_wv=1027&k=HlshFwkJ) [](https://jq.qq.com/?_wv=1027&k=0ARRrO9V) [](https://jq.qq.com/?_wv=1027&k=up9k3ZXJ) [](https://jq.qq.com/?_wv=1027&k=540WfdEr) [](https://jq.qq.com/?_wv=1027&k=ss91fC4t) [](https://jq.qq.com/?_wv=1027&k=Cqd66IKe) [](https://jq.qq.com/?_wv=1027&k=7FplYUnR) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=lqMHu_5Fskm7H2S1vNAQTtzAUokVydwc&authKey=ptw0Fpch5pbNocML3CIJKKqZBaq2DI7cusKuzIgfMNiY3t9Pvd9hP%2BA8WYx3yaY1&noverify=0&group_code=174942938) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=blYlRDmwZXSXI5pVrPPU7ZJ1stFJ6Q2Q&authKey=ForGBWffHVlPt9NE3d7g4DoOIouBh%2BqvAj2lp1CLReHfZAUaK7SRrdwsChKpRJDJ&noverify=0&group_code=287843737)
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
@echo off
|
||||||
|
echo.
|
||||||
|
echo [信息] 清理工程target生成路径。
|
||||||
|
echo.
|
||||||
|
|
||||||
|
%~d0
|
||||||
|
cd %~dp0
|
||||||
|
|
||||||
|
cd ..
|
||||||
|
call mvn clean
|
||||||
|
|
||||||
|
pause
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
@echo off
|
||||||
|
echo.
|
||||||
|
echo [信息] 打包Web工程,生成war/jar包文件。
|
||||||
|
echo.
|
||||||
|
|
||||||
|
%~d0
|
||||||
|
cd %~dp0
|
||||||
|
|
||||||
|
cd ..
|
||||||
|
call mvn clean package -Dmaven.test.skip=true
|
||||||
|
|
||||||
|
pause
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
@echo off
|
||||||
|
echo.
|
||||||
|
echo [信息] 使用内嵌Tomcat运行Web工程。
|
||||||
|
echo.
|
||||||
|
|
||||||
|
%~d0
|
||||||
|
cd %~dp0
|
||||||
|
|
||||||
|
cd ..
|
||||||
|
title %cd%
|
||||||
|
set MAVEN_OPTS=%MAVEN_OPTS% -Xms256m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m
|
||||||
|
call mvn clean spring-boot:run -Dmaven.test.skip=true -U
|
||||||
|
|
||||||
|
pause
|
||||||
|
|
@ -0,0 +1,89 @@
|
||||||
|
若依系统开发环境搭建手册
|
||||||
|
1. 准备工作
|
||||||
|
1.1 前置环境准备 -- -- 安装Maven
|
||||||
|
若依基于Maven管理项目的构建,需要先安装好相应的版本。
|
||||||
|
1.2 开发工具
|
||||||
|
若依系统采用Eclipse作为开发工具。但不局限于Eclipse。此处仅介绍在Eclipse搭建开发环境所需的操作。
|
||||||
|
2. 开发环境搭建
|
||||||
|
2.1 开发工具的配置
|
||||||
|
2.1.1 配置Maven
|
||||||
|
进入Window->Preferences->Maven->Installations页面,设置已经安装好的Maven
|
||||||
|
|
||||||
|
2.1.2 配置Maven仓库路径
|
||||||
|
进入Window->Preferences->Maven->User Settings页面,配置仓库路径
|
||||||
|
|
||||||
|
2.1.4 关闭校验
|
||||||
|
进入Window->Preferences->Validation页面,勾选"Suspend all validators",关闭校验
|
||||||
|
|
||||||
|
2.2 导入工程
|
||||||
|
通过Eclipse导入工程,步骤如下:
|
||||||
|
(1)点击左侧项目区域 -- >Import...
|
||||||
|
|
||||||
|
(2)选择RuoYi
|
||||||
|
|
||||||
|
(3)点击Finish
|
||||||
|
|
||||||
|
(4)RuoYi的代码就被导出到Eclipse中了,此时可以在工程视图中看到。
|
||||||
|
|
||||||
|
3. 运行若依系统
|
||||||
|
3.1 必要的配置
|
||||||
|
3.1.1 修改数据库连接
|
||||||
|
编辑src/main/ resources目录下的application-druid.yml 文件,修改数据库地址账号信息。
|
||||||
|
执行sql/ ry_20180423.sql,quartz.sql 两个文件 日期随版本变化
|
||||||
|
3.1.2 开发环境配置
|
||||||
|
编辑src/main/ resources目录下的application.yml 文件,
|
||||||
|
默认端口为80
|
||||||
|
|
||||||
|
3.1.3 代码生成配置
|
||||||
|
编辑src/main/ resources目录下的application.yml 文件,
|
||||||
|
|
||||||
|
默认为module,根据实际情况修改即可。生成的表要有注释
|
||||||
|
|
||||||
|
注:如对模板有特殊需求,可自行修改。编辑src/main/ resources/templates/vm目录下
|
||||||
|
|
||||||
|
|
||||||
|
3.1.4 日志配置
|
||||||
|
编辑src/main/ resources目录下的logback.yml 文件
|
||||||
|
<property name="log.path" value="/home/ruoyi/logs" />
|
||||||
|
改为自己需要的路径
|
||||||
|
|
||||||
|
3.2 启动及验证
|
||||||
|
启动RuoYiApplication.java 出现如下图表示启动成功
|
||||||
|
|
||||||
|
打开浏览器,输入:http://localhost:80/
|
||||||
|
若能正确展示登录页面,并能成功登录,登录后菜单及页面展示正常,则表明环境搭建成功。
|
||||||
|
默认密码为 admin/admin123
|
||||||
|
|
||||||
|
演示地址:http://ruoyi.vip
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
4. 部署若依系统
|
||||||
|
4.1 war部署方式
|
||||||
|
4.1.1 修改pom.xml文件。将jar修改为war
|
||||||
|
|
||||||
|
如果是分模块需要修改ruoyi-admin
|
||||||
|
|
||||||
|
|
||||||
|
4.1.2 在spring-boot-starter依赖中移除tomcat模块
|
||||||
|
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<artifactId>spring-boot-starter-tomcat</artifactId>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
|
||||||
|
4.1.3 部署到tomcat的webapps目录下面
|
||||||
|
默认为RuoYi.war
|
||||||
|
|
||||||
|
4.1.4 启动及验证
|
||||||
|
运行startup.bat 出现如下图即部署成功
|
||||||
|
|
||||||
|
4.2 Jar方式部署
|
||||||
|
执行命令:java - jar RuoYi.jar
|
||||||
|
脚本执行:ry.sh start 启动stop 停止
|
||||||
|
|
||||||
|
|
||||||
|
演示地址:ruoyi.vip
|
||||||
|
文档地址:doc.ruoyi.vip
|
||||||
|
|
@ -0,0 +1,308 @@
|
||||||
|
<?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>4.7.9</version>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<name>ruoyi</name>
|
||||||
|
<url>http://www.ruoyi.vip</url>
|
||||||
|
<description>若依管理系统</description>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-parent</artifactId>
|
||||||
|
<version>2.5.15</version>
|
||||||
|
<relativePath />
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||||
|
<java.version>1.8</java.version>
|
||||||
|
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
|
||||||
|
<shiro.version>1.13.0</shiro.version>
|
||||||
|
<spring-framework.version>5.3.33</spring-framework.version>
|
||||||
|
<thymeleaf.extras.shiro.version>2.1.0</thymeleaf.extras.shiro.version>
|
||||||
|
<pagehelper.spring.boot.starter.version>1.4.7</pagehelper.spring.boot.starter.version>
|
||||||
|
<fastjson.version>1.2.83</fastjson.version>
|
||||||
|
<druid.version>1.2.23</druid.version>
|
||||||
|
<commons.io.version>2.13.0</commons.io.version>
|
||||||
|
<bitwalker.version>1.21</bitwalker.version>
|
||||||
|
<velocity.version>2.3</velocity.version>
|
||||||
|
<kaptcha.version>2.3.3</kaptcha.version>
|
||||||
|
<swagger.version>3.0.0</swagger.version>
|
||||||
|
<poi.version>4.1.2</poi.version>
|
||||||
|
<oshi.version>6.6.1</oshi.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
|
||||||
|
<!-- SpringBoot 核心包 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- SpringBoot 测试 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- SpringBoot 拦截器 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-aop</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- SpringBoot Web容器 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- SpringBoot集成thymeleaf模板 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-thymeleaf</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- spring-boot-devtools -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-devtools</artifactId>
|
||||||
|
<optional>true</optional> <!-- 表示依赖不会传递 -->
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Mysql驱动包 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>mysql</groupId>
|
||||||
|
<artifactId>mysql-connector-java</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- pagehelper 分页插件 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.pagehelper</groupId>
|
||||||
|
<artifactId>pagehelper-spring-boot-starter</artifactId>
|
||||||
|
<version>${pagehelper.spring.boot.starter.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 阿里数据库连接池 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba</groupId>
|
||||||
|
<artifactId>druid-spring-boot-starter</artifactId>
|
||||||
|
<version>${druid.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 自定义验证注解 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-validation</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 常用工具类 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.commons</groupId>
|
||||||
|
<artifactId>commons-lang3</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- io常用工具类 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>commons-io</groupId>
|
||||||
|
<artifactId>commons-io</artifactId>
|
||||||
|
<version>${commons.io.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Shiro核心框架 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.shiro</groupId>
|
||||||
|
<artifactId>shiro-core</artifactId>
|
||||||
|
<version>${shiro.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Shiro使用Spring框架 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.shiro</groupId>
|
||||||
|
<artifactId>shiro-spring</artifactId>
|
||||||
|
<version>${shiro.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Shiro使用EhCache缓存框架 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.shiro</groupId>
|
||||||
|
<artifactId>shiro-ehcache</artifactId>
|
||||||
|
<version>${shiro.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- thymeleaf模板引擎和shiro框架的整合 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.theborakompanioni</groupId>
|
||||||
|
<artifactId>thymeleaf-extras-shiro</artifactId>
|
||||||
|
<version>${thymeleaf.extras.shiro.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 阿里JSON解析器 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba</groupId>
|
||||||
|
<artifactId>fastjson</artifactId>
|
||||||
|
<version>${fastjson.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 解析客户端操作系统、浏览器等 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>eu.bitwalker</groupId>
|
||||||
|
<artifactId>UserAgentUtils</artifactId>
|
||||||
|
<version>${bitwalker.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Spring框架基本的核心工具 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-context-support</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 定时任务 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.quartz-scheduler</groupId>
|
||||||
|
<artifactId>quartz</artifactId>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>com.mchange</groupId>
|
||||||
|
<artifactId>c3p0</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- velocity代码生成使用模板 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.velocity</groupId>
|
||||||
|
<artifactId>velocity-engine-core</artifactId>
|
||||||
|
<version>${velocity.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 验证码 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>pro.fessional</groupId>
|
||||||
|
<artifactId>kaptcha</artifactId>
|
||||||
|
<version>${kaptcha.version}</version>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<artifactId>servlet-api</artifactId>
|
||||||
|
<groupId>javax.servlet</groupId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Swagger3依赖 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.springfox</groupId>
|
||||||
|
<artifactId>springfox-boot-starter</artifactId>
|
||||||
|
<version>${swagger.version}</version>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>io.swagger</groupId>
|
||||||
|
<artifactId>swagger-models</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 防止进入swagger页面报类型转换错误,排除3.0.0中的引用,手动增加1.6.2版本 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.swagger</groupId>
|
||||||
|
<artifactId>swagger-models</artifactId>
|
||||||
|
<version>1.6.2</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 获取系统信息 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.oshi</groupId>
|
||||||
|
<artifactId>oshi-core</artifactId>
|
||||||
|
<version>${oshi.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- excel工具 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.poi</groupId>
|
||||||
|
<artifactId>poi-ooxml</artifactId>
|
||||||
|
<version>${poi.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<finalName>${project.artifactId}</finalName>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<fork>true</fork> <!-- 如果没有该配置,devtools不会生效 -->
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<!-- YUI Compressor (CSS/JS压缩)
|
||||||
|
<plugin>
|
||||||
|
<groupId>net.alchim31.maven</groupId>
|
||||||
|
<artifactId>yuicompressor-maven-plugin</artifactId>
|
||||||
|
<version>1.5.1</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<phase>prepare-package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>compress</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
<configuration>
|
||||||
|
<encoding>UTF-8</encoding>
|
||||||
|
<jswarn>false</jswarn>
|
||||||
|
<nosuffix>true</nosuffix>
|
||||||
|
<linebreakpos>50000</linebreakpos>
|
||||||
|
<sourceDirectory>src/main/resources/static</sourceDirectory>
|
||||||
|
<force>true</force>
|
||||||
|
<includes>
|
||||||
|
<include>**/*.js</include>
|
||||||
|
<include>**/*.css</include>
|
||||||
|
</includes>
|
||||||
|
<excludes>
|
||||||
|
<exclude>**/*.min.js</exclude>
|
||||||
|
<exclude>**/*.min.css</exclude>
|
||||||
|
<exclude>**/fileinput.js</exclude>
|
||||||
|
<exclude>**/validate/**</exclude>
|
||||||
|
<exclude>**/bootstrap-table/**</exclude>
|
||||||
|
</excludes>
|
||||||
|
</configuration>
|
||||||
|
</plugin> -->
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
<repositories>
|
||||||
|
<repository>
|
||||||
|
<id>public</id>
|
||||||
|
<name>aliyun nexus</name>
|
||||||
|
<url>https://maven.aliyun.com/repository/public</url>
|
||||||
|
<releases>
|
||||||
|
<enabled>true</enabled>
|
||||||
|
</releases>
|
||||||
|
</repository>
|
||||||
|
</repositories>
|
||||||
|
|
||||||
|
<pluginRepositories>
|
||||||
|
<pluginRepository>
|
||||||
|
<id>public</id>
|
||||||
|
<name>aliyun nexus</name>
|
||||||
|
<url>https://maven.aliyun.com/repository/public</url>
|
||||||
|
<releases>
|
||||||
|
<enabled>true</enabled>
|
||||||
|
</releases>
|
||||||
|
<snapshots>
|
||||||
|
<enabled>false</enabled>
|
||||||
|
</snapshots>
|
||||||
|
</pluginRepository>
|
||||||
|
</pluginRepositories>
|
||||||
|
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,67 @@
|
||||||
|
@echo off
|
||||||
|
|
||||||
|
rem jar平级目录
|
||||||
|
set AppName=ruoyi.jar
|
||||||
|
|
||||||
|
rem JVM参数
|
||||||
|
set JVM_OPTS="-Dname=%AppName% -Duser.timezone=Asia/Shanghai -Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:NewRatio=1 -XX:SurvivorRatio=30 -XX:+UseParallelGC -XX:+UseParallelOldGC"
|
||||||
|
|
||||||
|
|
||||||
|
ECHO.
|
||||||
|
ECHO. [1] 启动%AppName%
|
||||||
|
ECHO. [2] 关闭%AppName%
|
||||||
|
ECHO. [3] 重启%AppName%
|
||||||
|
ECHO. [4] 启动状态 %AppName%
|
||||||
|
ECHO. [5] 退 出
|
||||||
|
ECHO.
|
||||||
|
|
||||||
|
ECHO.请输入选择项目的序号:
|
||||||
|
set /p ID=
|
||||||
|
IF "%id%"=="1" GOTO start
|
||||||
|
IF "%id%"=="2" GOTO stop
|
||||||
|
IF "%id%"=="3" GOTO restart
|
||||||
|
IF "%id%"=="4" GOTO status
|
||||||
|
IF "%id%"=="5" EXIT
|
||||||
|
PAUSE
|
||||||
|
:start
|
||||||
|
for /f "usebackq tokens=1-2" %%a in (`jps -l ^| findstr %AppName%`) do (
|
||||||
|
set pid=%%a
|
||||||
|
set image_name=%%b
|
||||||
|
)
|
||||||
|
if defined pid (
|
||||||
|
echo %%is running
|
||||||
|
PAUSE
|
||||||
|
)
|
||||||
|
|
||||||
|
start javaw %JVM_OPTS% -jar %AppName%
|
||||||
|
|
||||||
|
echo starting……
|
||||||
|
echo Start %AppName% success...
|
||||||
|
goto:eof
|
||||||
|
|
||||||
|
rem 函数stop通过jps命令查找pid并结束进程
|
||||||
|
:stop
|
||||||
|
for /f "usebackq tokens=1-2" %%a in (`jps -l ^| findstr %AppName%`) do (
|
||||||
|
set pid=%%a
|
||||||
|
set image_name=%%b
|
||||||
|
)
|
||||||
|
if not defined pid (echo process %AppName% does not exists) else (
|
||||||
|
echo prepare to kill %image_name%
|
||||||
|
echo start kill %pid% ...
|
||||||
|
rem 根据进程ID,kill进程
|
||||||
|
taskkill /f /pid %pid%
|
||||||
|
)
|
||||||
|
goto:eof
|
||||||
|
:restart
|
||||||
|
call :stop
|
||||||
|
call :start
|
||||||
|
goto:eof
|
||||||
|
:status
|
||||||
|
for /f "usebackq tokens=1-2" %%a in (`jps -l ^| findstr %AppName%`) do (
|
||||||
|
set pid=%%a
|
||||||
|
set image_name=%%b
|
||||||
|
)
|
||||||
|
if not defined pid (echo process %AppName% is dead ) else (
|
||||||
|
echo %image_name% is running
|
||||||
|
)
|
||||||
|
goto:eof
|
||||||
|
|
@ -0,0 +1,86 @@
|
||||||
|
#!/bin/sh
|
||||||
|
# ./ry.sh start 启动 stop 停止 restart 重启 status 状态
|
||||||
|
AppName=target/ruoyi.jar
|
||||||
|
|
||||||
|
# JVM参数
|
||||||
|
JVM_OPTS="-Dname=$AppName -Duser.timezone=Asia/Shanghai -Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:NewRatio=1 -XX:SurvivorRatio=30 -XX:+UseParallelGC -XX:+UseParallelOldGC"
|
||||||
|
APP_HOME=`pwd`
|
||||||
|
LOG_PATH=$APP_HOME/logs/$AppName.log
|
||||||
|
|
||||||
|
if [ "$1" = "" ];
|
||||||
|
then
|
||||||
|
echo -e "\033[0;31m 未输入操作名 \033[0m \033[0;34m {start|stop|restart|status} \033[0m"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$AppName" = "" ];
|
||||||
|
then
|
||||||
|
echo -e "\033[0;31m 未输入应用名 \033[0m"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
function start()
|
||||||
|
{
|
||||||
|
PID=`ps -ef |grep java|grep $AppName|grep -v grep|awk '{print $2}'`
|
||||||
|
|
||||||
|
if [ x"$PID" != x"" ]; then
|
||||||
|
echo "$AppName is running..."
|
||||||
|
else
|
||||||
|
nohup java $JVM_OPTS -jar $AppName > /dev/null 2>&1 &
|
||||||
|
echo "Start $AppName success..."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function stop()
|
||||||
|
{
|
||||||
|
echo "Stop $AppName"
|
||||||
|
|
||||||
|
PID=""
|
||||||
|
query(){
|
||||||
|
PID=`ps -ef |grep java|grep $AppName|grep -v grep|awk '{print $2}'`
|
||||||
|
}
|
||||||
|
|
||||||
|
query
|
||||||
|
if [ x"$PID" != x"" ]; then
|
||||||
|
kill -TERM $PID
|
||||||
|
echo "$AppName (pid:$PID) exiting..."
|
||||||
|
while [ x"$PID" != x"" ]
|
||||||
|
do
|
||||||
|
sleep 1
|
||||||
|
query
|
||||||
|
done
|
||||||
|
echo "$AppName exited."
|
||||||
|
else
|
||||||
|
echo "$AppName already stopped."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function restart()
|
||||||
|
{
|
||||||
|
stop
|
||||||
|
sleep 2
|
||||||
|
start
|
||||||
|
}
|
||||||
|
|
||||||
|
function status()
|
||||||
|
{
|
||||||
|
PID=`ps -ef |grep java|grep $AppName|grep -v grep|wc -l`
|
||||||
|
if [ $PID != 0 ];then
|
||||||
|
echo "$AppName is running..."
|
||||||
|
else
|
||||||
|
echo "$AppName is not running..."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
case $1 in
|
||||||
|
start)
|
||||||
|
start;;
|
||||||
|
stop)
|
||||||
|
stop;;
|
||||||
|
restart)
|
||||||
|
restart;;
|
||||||
|
status)
|
||||||
|
status;;
|
||||||
|
*)
|
||||||
|
|
||||||
|
esac
|
||||||
|
|
@ -0,0 +1,174 @@
|
||||||
|
DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS;
|
||||||
|
DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS;
|
||||||
|
DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE;
|
||||||
|
DROP TABLE IF EXISTS QRTZ_LOCKS;
|
||||||
|
DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS;
|
||||||
|
DROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS;
|
||||||
|
DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS;
|
||||||
|
DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS;
|
||||||
|
DROP TABLE IF EXISTS QRTZ_TRIGGERS;
|
||||||
|
DROP TABLE IF EXISTS QRTZ_JOB_DETAILS;
|
||||||
|
DROP TABLE IF EXISTS QRTZ_CALENDARS;
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- 1、存储每一个已配置的 jobDetail 的详细信息
|
||||||
|
-- ----------------------------
|
||||||
|
create table QRTZ_JOB_DETAILS (
|
||||||
|
sched_name varchar(120) not null comment '调度名称',
|
||||||
|
job_name varchar(200) not null comment '任务名称',
|
||||||
|
job_group varchar(200) not null comment '任务组名',
|
||||||
|
description varchar(250) null comment '相关介绍',
|
||||||
|
job_class_name varchar(250) not null comment '执行任务类名称',
|
||||||
|
is_durable varchar(1) not null comment '是否持久化',
|
||||||
|
is_nonconcurrent varchar(1) not null comment '是否并发',
|
||||||
|
is_update_data varchar(1) not null comment '是否更新数据',
|
||||||
|
requests_recovery varchar(1) not null comment '是否接受恢复执行',
|
||||||
|
job_data blob null comment '存放持久化job对象',
|
||||||
|
primary key (sched_name, job_name, job_group)
|
||||||
|
) engine=innodb comment = '任务详细信息表';
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- 2、 存储已配置的 Trigger 的信息
|
||||||
|
-- ----------------------------
|
||||||
|
create table QRTZ_TRIGGERS (
|
||||||
|
sched_name varchar(120) not null comment '调度名称',
|
||||||
|
trigger_name varchar(200) not null comment '触发器的名字',
|
||||||
|
trigger_group varchar(200) not null comment '触发器所属组的名字',
|
||||||
|
job_name varchar(200) not null comment 'qrtz_job_details表job_name的外键',
|
||||||
|
job_group varchar(200) not null comment 'qrtz_job_details表job_group的外键',
|
||||||
|
description varchar(250) null comment '相关介绍',
|
||||||
|
next_fire_time bigint(13) null comment '上一次触发时间(毫秒)',
|
||||||
|
prev_fire_time bigint(13) null comment '下一次触发时间(默认为-1表示不触发)',
|
||||||
|
priority integer null comment '优先级',
|
||||||
|
trigger_state varchar(16) not null comment '触发器状态',
|
||||||
|
trigger_type varchar(8) not null comment '触发器的类型',
|
||||||
|
start_time bigint(13) not null comment '开始时间',
|
||||||
|
end_time bigint(13) null comment '结束时间',
|
||||||
|
calendar_name varchar(200) null comment '日程表名称',
|
||||||
|
misfire_instr smallint(2) null comment '补偿执行的策略',
|
||||||
|
job_data blob null comment '存放持久化job对象',
|
||||||
|
primary key (sched_name, trigger_name, trigger_group),
|
||||||
|
foreign key (sched_name, job_name, job_group) references QRTZ_JOB_DETAILS(sched_name, job_name, job_group)
|
||||||
|
) engine=innodb comment = '触发器详细信息表';
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- 3、 存储简单的 Trigger,包括重复次数,间隔,以及已触发的次数
|
||||||
|
-- ----------------------------
|
||||||
|
create table QRTZ_SIMPLE_TRIGGERS (
|
||||||
|
sched_name varchar(120) not null comment '调度名称',
|
||||||
|
trigger_name varchar(200) not null comment 'qrtz_triggers表trigger_name的外键',
|
||||||
|
trigger_group varchar(200) not null comment 'qrtz_triggers表trigger_group的外键',
|
||||||
|
repeat_count bigint(7) not null comment '重复的次数统计',
|
||||||
|
repeat_interval bigint(12) not null comment '重复的间隔时间',
|
||||||
|
times_triggered bigint(10) not null comment '已经触发的次数',
|
||||||
|
primary key (sched_name, trigger_name, trigger_group),
|
||||||
|
foreign key (sched_name, trigger_name, trigger_group) references QRTZ_TRIGGERS(sched_name, trigger_name, trigger_group)
|
||||||
|
) engine=innodb comment = '简单触发器的信息表';
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- 4、 存储 Cron Trigger,包括 Cron 表达式和时区信息
|
||||||
|
-- ----------------------------
|
||||||
|
create table QRTZ_CRON_TRIGGERS (
|
||||||
|
sched_name varchar(120) not null comment '调度名称',
|
||||||
|
trigger_name varchar(200) not null comment 'qrtz_triggers表trigger_name的外键',
|
||||||
|
trigger_group varchar(200) not null comment 'qrtz_triggers表trigger_group的外键',
|
||||||
|
cron_expression varchar(200) not null comment 'cron表达式',
|
||||||
|
time_zone_id varchar(80) comment '时区',
|
||||||
|
primary key (sched_name, trigger_name, trigger_group),
|
||||||
|
foreign key (sched_name, trigger_name, trigger_group) references QRTZ_TRIGGERS(sched_name, trigger_name, trigger_group)
|
||||||
|
) engine=innodb comment = 'Cron类型的触发器表';
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- 5、 Trigger 作为 Blob 类型存储(用于 Quartz 用户用 JDBC 创建他们自己定制的 Trigger 类型,JobStore 并不知道如何存储实例的时候)
|
||||||
|
-- ----------------------------
|
||||||
|
create table QRTZ_BLOB_TRIGGERS (
|
||||||
|
sched_name varchar(120) not null comment '调度名称',
|
||||||
|
trigger_name varchar(200) not null comment 'qrtz_triggers表trigger_name的外键',
|
||||||
|
trigger_group varchar(200) not null comment 'qrtz_triggers表trigger_group的外键',
|
||||||
|
blob_data blob null comment '存放持久化Trigger对象',
|
||||||
|
primary key (sched_name, trigger_name, trigger_group),
|
||||||
|
foreign key (sched_name, trigger_name, trigger_group) references QRTZ_TRIGGERS(sched_name, trigger_name, trigger_group)
|
||||||
|
) engine=innodb comment = 'Blob类型的触发器表';
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- 6、 以 Blob 类型存储存放日历信息, quartz可配置一个日历来指定一个时间范围
|
||||||
|
-- ----------------------------
|
||||||
|
create table QRTZ_CALENDARS (
|
||||||
|
sched_name varchar(120) not null comment '调度名称',
|
||||||
|
calendar_name varchar(200) not null comment '日历名称',
|
||||||
|
calendar blob not null comment '存放持久化calendar对象',
|
||||||
|
primary key (sched_name, calendar_name)
|
||||||
|
) engine=innodb comment = '日历信息表';
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- 7、 存储已暂停的 Trigger 组的信息
|
||||||
|
-- ----------------------------
|
||||||
|
create table QRTZ_PAUSED_TRIGGER_GRPS (
|
||||||
|
sched_name varchar(120) not null comment '调度名称',
|
||||||
|
trigger_group varchar(200) not null comment 'qrtz_triggers表trigger_group的外键',
|
||||||
|
primary key (sched_name, trigger_group)
|
||||||
|
) engine=innodb comment = '暂停的触发器表';
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- 8、 存储与已触发的 Trigger 相关的状态信息,以及相联 Job 的执行信息
|
||||||
|
-- ----------------------------
|
||||||
|
create table QRTZ_FIRED_TRIGGERS (
|
||||||
|
sched_name varchar(120) not null comment '调度名称',
|
||||||
|
entry_id varchar(95) not null comment '调度器实例id',
|
||||||
|
trigger_name varchar(200) not null comment 'qrtz_triggers表trigger_name的外键',
|
||||||
|
trigger_group varchar(200) not null comment 'qrtz_triggers表trigger_group的外键',
|
||||||
|
instance_name varchar(200) not null comment '调度器实例名',
|
||||||
|
fired_time bigint(13) not null comment '触发的时间',
|
||||||
|
sched_time bigint(13) not null comment '定时器制定的时间',
|
||||||
|
priority integer not null comment '优先级',
|
||||||
|
state varchar(16) not null comment '状态',
|
||||||
|
job_name varchar(200) null comment '任务名称',
|
||||||
|
job_group varchar(200) null comment '任务组名',
|
||||||
|
is_nonconcurrent varchar(1) null comment '是否并发',
|
||||||
|
requests_recovery varchar(1) null comment '是否接受恢复执行',
|
||||||
|
primary key (sched_name, entry_id)
|
||||||
|
) engine=innodb comment = '已触发的触发器表';
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- 9、 存储少量的有关 Scheduler 的状态信息,假如是用于集群中,可以看到其他的 Scheduler 实例
|
||||||
|
-- ----------------------------
|
||||||
|
create table QRTZ_SCHEDULER_STATE (
|
||||||
|
sched_name varchar(120) not null comment '调度名称',
|
||||||
|
instance_name varchar(200) not null comment '实例名称',
|
||||||
|
last_checkin_time bigint(13) not null comment '上次检查时间',
|
||||||
|
checkin_interval bigint(13) not null comment '检查间隔时间',
|
||||||
|
primary key (sched_name, instance_name)
|
||||||
|
) engine=innodb comment = '调度器状态表';
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- 10、 存储程序的悲观锁的信息(假如使用了悲观锁)
|
||||||
|
-- ----------------------------
|
||||||
|
create table QRTZ_LOCKS (
|
||||||
|
sched_name varchar(120) not null comment '调度名称',
|
||||||
|
lock_name varchar(40) not null comment '悲观锁名称',
|
||||||
|
primary key (sched_name, lock_name)
|
||||||
|
) engine=innodb comment = '存储的悲观锁信息表';
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- 11、 Quartz集群实现同步机制的行锁表
|
||||||
|
-- ----------------------------
|
||||||
|
create table QRTZ_SIMPROP_TRIGGERS (
|
||||||
|
sched_name varchar(120) not null comment '调度名称',
|
||||||
|
trigger_name varchar(200) not null comment 'qrtz_triggers表trigger_name的外键',
|
||||||
|
trigger_group varchar(200) not null comment 'qrtz_triggers表trigger_group的外键',
|
||||||
|
str_prop_1 varchar(512) null comment 'String类型的trigger的第一个参数',
|
||||||
|
str_prop_2 varchar(512) null comment 'String类型的trigger的第二个参数',
|
||||||
|
str_prop_3 varchar(512) null comment 'String类型的trigger的第三个参数',
|
||||||
|
int_prop_1 int null comment 'int类型的trigger的第一个参数',
|
||||||
|
int_prop_2 int null comment 'int类型的trigger的第二个参数',
|
||||||
|
long_prop_1 bigint null comment 'long类型的trigger的第一个参数',
|
||||||
|
long_prop_2 bigint null comment 'long类型的trigger的第二个参数',
|
||||||
|
dec_prop_1 numeric(13,4) null comment 'decimal类型的trigger的第一个参数',
|
||||||
|
dec_prop_2 numeric(13,4) null comment 'decimal类型的trigger的第二个参数',
|
||||||
|
bool_prop_1 varchar(1) null comment 'Boolean类型的trigger的第一个参数',
|
||||||
|
bool_prop_2 varchar(1) null comment 'Boolean类型的trigger的第二个参数',
|
||||||
|
primary key (sched_name, trigger_name, trigger_group),
|
||||||
|
foreign key (sched_name, trigger_name, trigger_group) references QRTZ_TRIGGERS(sched_name, trigger_name, trigger_group)
|
||||||
|
) engine=innodb comment = '同步机制的行锁表';
|
||||||
|
|
||||||
|
commit;
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,722 @@
|
||||||
|
-- ----------------------------
|
||||||
|
-- 1、部门表
|
||||||
|
-- ----------------------------
|
||||||
|
drop table if exists sys_dept;
|
||||||
|
create table sys_dept (
|
||||||
|
dept_id bigint(20) not null auto_increment comment '部门id',
|
||||||
|
parent_id bigint(20) default 0 comment '父部门id',
|
||||||
|
ancestors varchar(50) default '' comment '祖级列表',
|
||||||
|
dept_name varchar(30) default '' comment '部门名称',
|
||||||
|
order_num int(4) default 0 comment '显示顺序',
|
||||||
|
leader varchar(20) default null comment '负责人',
|
||||||
|
phone varchar(11) default null comment '联系电话',
|
||||||
|
email varchar(50) default null comment '邮箱',
|
||||||
|
status char(1) default '0' comment '部门状态(0正常 1停用)',
|
||||||
|
del_flag char(1) default '0' comment '删除标志(0代表存在 2代表删除)',
|
||||||
|
create_by varchar(64) default '' comment '创建者',
|
||||||
|
create_time datetime comment '创建时间',
|
||||||
|
update_by varchar(64) default '' comment '更新者',
|
||||||
|
update_time datetime comment '更新时间',
|
||||||
|
primary key (dept_id)
|
||||||
|
) engine=innodb auto_increment=200 comment = '部门表';
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- 初始化-部门表数据
|
||||||
|
-- ----------------------------
|
||||||
|
insert into sys_dept values(100, 0, '0', '若依科技', 0, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null);
|
||||||
|
insert into sys_dept values(101, 100, '0,100', '深圳总公司', 1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null);
|
||||||
|
insert into sys_dept values(102, 100, '0,100', '长沙分公司', 2, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null);
|
||||||
|
insert into sys_dept values(103, 101, '0,100,101', '研发部门', 1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null);
|
||||||
|
insert into sys_dept values(104, 101, '0,100,101', '市场部门', 2, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null);
|
||||||
|
insert into sys_dept values(105, 101, '0,100,101', '测试部门', 3, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null);
|
||||||
|
insert into sys_dept values(106, 101, '0,100,101', '财务部门', 4, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null);
|
||||||
|
insert into sys_dept values(107, 101, '0,100,101', '运维部门', 5, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null);
|
||||||
|
insert into sys_dept values(108, 102, '0,100,102', '市场部门', 1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null);
|
||||||
|
insert into sys_dept values(109, 102, '0,100,102', '财务部门', 2, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null);
|
||||||
|
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- 2、用户信息表
|
||||||
|
-- ----------------------------
|
||||||
|
drop table if exists sys_user;
|
||||||
|
create table sys_user (
|
||||||
|
user_id bigint(20) not null auto_increment comment '用户ID',
|
||||||
|
dept_id bigint(20) default null comment '部门ID',
|
||||||
|
login_name varchar(30) not null comment '登录账号',
|
||||||
|
user_name varchar(30) default '' comment '用户昵称',
|
||||||
|
user_type varchar(2) default '00' comment '用户类型(00系统用户 01注册用户)',
|
||||||
|
email varchar(50) default '' comment '用户邮箱',
|
||||||
|
phonenumber varchar(11) default '' comment '手机号码',
|
||||||
|
sex char(1) default '0' comment '用户性别(0男 1女 2未知)',
|
||||||
|
avatar varchar(100) default '' comment '头像路径',
|
||||||
|
password varchar(50) default '' comment '密码',
|
||||||
|
salt varchar(20) default '' comment '盐加密',
|
||||||
|
status char(1) default '0' comment '帐号状态(0正常 1停用)',
|
||||||
|
del_flag char(1) default '0' comment '删除标志(0代表存在 2代表删除)',
|
||||||
|
login_ip varchar(128) default '' comment '最后登录IP',
|
||||||
|
login_date datetime comment '最后登录时间',
|
||||||
|
pwd_update_date datetime comment '密码最后更新时间',
|
||||||
|
create_by varchar(64) default '' comment '创建者',
|
||||||
|
create_time datetime comment '创建时间',
|
||||||
|
update_by varchar(64) default '' comment '更新者',
|
||||||
|
update_time datetime comment '更新时间',
|
||||||
|
remark varchar(500) default null comment '备注',
|
||||||
|
primary key (user_id)
|
||||||
|
) engine=innodb auto_increment=100 comment = '用户信息表';
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- 初始化-用户信息表数据
|
||||||
|
-- ----------------------------
|
||||||
|
insert into sys_user values(1, 103, 'admin', '若依', '00', 'ry@163.com', '15888888888', '1', '', '29c67a30398638269fe600f73a054934', '111111', '0', '0', '127.0.0.1', null, null, 'admin', sysdate(), '', null, '管理员');
|
||||||
|
insert into sys_user values(2, 105, 'ry', '若依', '00', 'ry@qq.com', '15666666666', '1', '', '8e6d98b90472783cc73c17047ddccf36', '222222', '0', '0', '127.0.0.1', null, null, 'admin', sysdate(), '', null, '测试员');
|
||||||
|
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- 3、岗位信息表
|
||||||
|
-- ----------------------------
|
||||||
|
drop table if exists sys_post;
|
||||||
|
create table sys_post
|
||||||
|
(
|
||||||
|
post_id bigint(20) not null auto_increment comment '岗位ID',
|
||||||
|
post_code varchar(64) not null comment '岗位编码',
|
||||||
|
post_name varchar(50) not null comment '岗位名称',
|
||||||
|
post_sort int(4) not null comment '显示顺序',
|
||||||
|
status char(1) not null comment '状态(0正常 1停用)',
|
||||||
|
create_by varchar(64) default '' comment '创建者',
|
||||||
|
create_time datetime comment '创建时间',
|
||||||
|
update_by varchar(64) default '' comment '更新者',
|
||||||
|
update_time datetime comment '更新时间',
|
||||||
|
remark varchar(500) default null comment '备注',
|
||||||
|
primary key (post_id)
|
||||||
|
) engine=innodb comment = '岗位信息表';
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- 初始化-岗位信息表数据
|
||||||
|
-- ----------------------------
|
||||||
|
insert into sys_post values(1, 'ceo', '董事长', 1, '0', 'admin', sysdate(), '', null, '');
|
||||||
|
insert into sys_post values(2, 'se', '项目经理', 2, '0', 'admin', sysdate(), '', null, '');
|
||||||
|
insert into sys_post values(3, 'hr', '人力资源', 3, '0', 'admin', sysdate(), '', null, '');
|
||||||
|
insert into sys_post values(4, 'user', '普通员工', 4, '0', 'admin', sysdate(), '', null, '');
|
||||||
|
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- 4、角色信息表
|
||||||
|
-- ----------------------------
|
||||||
|
drop table if exists sys_role;
|
||||||
|
create table sys_role (
|
||||||
|
role_id bigint(20) not null auto_increment comment '角色ID',
|
||||||
|
role_name varchar(30) not null comment '角色名称',
|
||||||
|
role_key varchar(100) not null comment '角色权限字符串',
|
||||||
|
role_sort int(4) not null comment '显示顺序',
|
||||||
|
data_scope char(1) default '1' comment '数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限)',
|
||||||
|
status char(1) not null comment '角色状态(0正常 1停用)',
|
||||||
|
del_flag char(1) default '0' comment '删除标志(0代表存在 2代表删除)',
|
||||||
|
create_by varchar(64) default '' comment '创建者',
|
||||||
|
create_time datetime comment '创建时间',
|
||||||
|
update_by varchar(64) default '' comment '更新者',
|
||||||
|
update_time datetime comment '更新时间',
|
||||||
|
remark varchar(500) default null comment '备注',
|
||||||
|
primary key (role_id)
|
||||||
|
) engine=innodb auto_increment=100 comment = '角色信息表';
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- 初始化-角色信息表数据
|
||||||
|
-- ----------------------------
|
||||||
|
insert into sys_role values('1', '超级管理员', 'admin', 1, 1, '0', '0', 'admin', sysdate(), '', null, '超级管理员');
|
||||||
|
insert into sys_role values('2', '普通角色', 'common', 2, 2, '0', '0', 'admin', sysdate(), '', null, '普通角色');
|
||||||
|
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- 5、菜单权限表
|
||||||
|
-- ----------------------------
|
||||||
|
drop table if exists sys_menu;
|
||||||
|
create table sys_menu (
|
||||||
|
menu_id bigint(20) not null auto_increment comment '菜单ID',
|
||||||
|
menu_name varchar(50) not null comment '菜单名称',
|
||||||
|
parent_id bigint(20) default 0 comment '父菜单ID',
|
||||||
|
order_num int(4) default 0 comment '显示顺序',
|
||||||
|
url varchar(200) default '#' comment '请求地址',
|
||||||
|
target varchar(20) default '' comment '打开方式(menuItem页签 menuBlank新窗口)',
|
||||||
|
menu_type char(1) default '' comment '菜单类型(M目录 C菜单 F按钮)',
|
||||||
|
visible char(1) default 0 comment '菜单状态(0显示 1隐藏)',
|
||||||
|
is_refresh char(1) default 1 comment '是否刷新(0刷新 1不刷新)',
|
||||||
|
perms varchar(100) default null comment '权限标识',
|
||||||
|
icon varchar(100) default '#' comment '菜单图标',
|
||||||
|
create_by varchar(64) default '' comment '创建者',
|
||||||
|
create_time datetime comment '创建时间',
|
||||||
|
update_by varchar(64) default '' comment '更新者',
|
||||||
|
update_time datetime comment '更新时间',
|
||||||
|
remark varchar(500) default '' comment '备注',
|
||||||
|
primary key (menu_id)
|
||||||
|
) engine=innodb auto_increment=2000 comment = '菜单权限表';
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- 初始化-菜单信息表数据
|
||||||
|
-- ----------------------------
|
||||||
|
-- 一级菜单
|
||||||
|
insert into sys_menu values('1', '系统管理', '0', '1', '#', '', 'M', '0', '1', '', 'fa fa-gear', 'admin', sysdate(), '', null, '系统管理目录');
|
||||||
|
insert into sys_menu values('2', '系统监控', '0', '2', '#', '', 'M', '0', '1', '', 'fa fa-video-camera', 'admin', sysdate(), '', null, '系统监控目录');
|
||||||
|
insert into sys_menu values('3', '系统工具', '0', '3', '#', '', 'M', '0', '1', '', 'fa fa-bars', 'admin', sysdate(), '', null, '系统工具目录');
|
||||||
|
insert into sys_menu values('4', '若依官网', '0', '4', 'http://ruoyi.vip', 'menuBlank', 'C', '0', '1', '', 'fa fa-location-arrow', 'admin', sysdate(), '', null, '若依官网地址');
|
||||||
|
-- 二级菜单
|
||||||
|
insert into sys_menu values('100', '用户管理', '1', '1', '/system/user', '', 'C', '0', '1', 'system:user:view', 'fa fa-user-o', 'admin', sysdate(), '', null, '用户管理菜单');
|
||||||
|
insert into sys_menu values('101', '角色管理', '1', '2', '/system/role', '', 'C', '0', '1', 'system:role:view', 'fa fa-user-secret', 'admin', sysdate(), '', null, '角色管理菜单');
|
||||||
|
insert into sys_menu values('102', '菜单管理', '1', '3', '/system/menu', '', 'C', '0', '1', 'system:menu:view', 'fa fa-th-list', 'admin', sysdate(), '', null, '菜单管理菜单');
|
||||||
|
insert into sys_menu values('103', '部门管理', '1', '4', '/system/dept', '', 'C', '0', '1', 'system:dept:view', 'fa fa-outdent', 'admin', sysdate(), '', null, '部门管理菜单');
|
||||||
|
insert into sys_menu values('104', '岗位管理', '1', '5', '/system/post', '', 'C', '0', '1', 'system:post:view', 'fa fa-address-card-o', 'admin', sysdate(), '', null, '岗位管理菜单');
|
||||||
|
insert into sys_menu values('105', '字典管理', '1', '6', '/system/dict', '', 'C', '0', '1', 'system:dict:view', 'fa fa-bookmark-o', 'admin', sysdate(), '', null, '字典管理菜单');
|
||||||
|
insert into sys_menu values('106', '参数设置', '1', '7', '/system/config', '', 'C', '0', '1', 'system:config:view', 'fa fa-sun-o', 'admin', sysdate(), '', null, '参数设置菜单');
|
||||||
|
insert into sys_menu values('107', '通知公告', '1', '8', '/system/notice', '', 'C', '0', '1', 'system:notice:view', 'fa fa-bullhorn', 'admin', sysdate(), '', null, '通知公告菜单');
|
||||||
|
insert into sys_menu values('108', '日志管理', '1', '9', '#', '', 'M', '0', '1', '', 'fa fa-pencil-square-o', 'admin', sysdate(), '', null, '日志管理菜单');
|
||||||
|
insert into sys_menu values('109', '在线用户', '2', '1', '/monitor/online', '', 'C', '0', '1', 'monitor:online:view', 'fa fa-user-circle', 'admin', sysdate(), '', null, '在线用户菜单');
|
||||||
|
insert into sys_menu values('110', '定时任务', '2', '2', '/monitor/job', '', 'C', '0', '1', 'monitor:job:view', 'fa fa-tasks', 'admin', sysdate(), '', null, '定时任务菜单');
|
||||||
|
insert into sys_menu values('111', '数据监控', '2', '3', '/monitor/data', '', 'C', '0', '1', 'monitor:data:view', 'fa fa-bug', 'admin', sysdate(), '', null, '数据监控菜单');
|
||||||
|
insert into sys_menu values('112', '服务监控', '2', '4', '/monitor/server', '', 'C', '0', '1', 'monitor:server:view', 'fa fa-server', 'admin', sysdate(), '', null, '服务监控菜单');
|
||||||
|
insert into sys_menu values('113', '缓存监控', '2', '5', '/monitor/cache', '', 'C', '0', '1', 'monitor:cache:view', 'fa fa-cube', 'admin', sysdate(), '', null, '缓存监控菜单');
|
||||||
|
insert into sys_menu values('114', '表单构建', '3', '1', '/tool/build', '', 'C', '0', '1', 'tool:build:view', 'fa fa-wpforms', 'admin', sysdate(), '', null, '表单构建菜单');
|
||||||
|
insert into sys_menu values('115', '代码生成', '3', '2', '/tool/gen', '', 'C', '0', '1', 'tool:gen:view', 'fa fa-code', 'admin', sysdate(), '', null, '代码生成菜单');
|
||||||
|
insert into sys_menu values('116', '系统接口', '3', '3', '/tool/swagger', '', 'C', '0', '1', 'tool:swagger:view', 'fa fa-gg', 'admin', sysdate(), '', null, '系统接口菜单');
|
||||||
|
-- 三级菜单
|
||||||
|
insert into sys_menu values('500', '操作日志', '108', '1', '/monitor/operlog', '', 'C', '0', '1', 'monitor:operlog:view', 'fa fa-address-book', 'admin', sysdate(), '', null, '操作日志菜单');
|
||||||
|
insert into sys_menu values('501', '登录日志', '108', '2', '/monitor/logininfor', '', 'C', '0', '1', 'monitor:logininfor:view', 'fa fa-file-image-o', 'admin', sysdate(), '', null, '登录日志菜单');
|
||||||
|
-- 用户管理按钮
|
||||||
|
insert into sys_menu values('1000', '用户查询', '100', '1', '#', '', 'F', '0', '1', 'system:user:list', '#', 'admin', sysdate(), '', null, '');
|
||||||
|
insert into sys_menu values('1001', '用户新增', '100', '2', '#', '', 'F', '0', '1', 'system:user:add', '#', 'admin', sysdate(), '', null, '');
|
||||||
|
insert into sys_menu values('1002', '用户修改', '100', '3', '#', '', 'F', '0', '1', 'system:user:edit', '#', 'admin', sysdate(), '', null, '');
|
||||||
|
insert into sys_menu values('1003', '用户删除', '100', '4', '#', '', 'F', '0', '1', 'system:user:remove', '#', 'admin', sysdate(), '', null, '');
|
||||||
|
insert into sys_menu values('1004', '用户导出', '100', '5', '#', '', 'F', '0', '1', 'system:user:export', '#', 'admin', sysdate(), '', null, '');
|
||||||
|
insert into sys_menu values('1005', '用户导入', '100', '6', '#', '', 'F', '0', '1', 'system:user:import', '#', 'admin', sysdate(), '', null, '');
|
||||||
|
insert into sys_menu values('1006', '重置密码', '100', '7', '#', '', 'F', '0', '1', 'system:user:resetPwd', '#', 'admin', sysdate(), '', null, '');
|
||||||
|
-- 角色管理按钮
|
||||||
|
insert into sys_menu values('1007', '角色查询', '101', '1', '#', '', 'F', '0', '1', 'system:role:list', '#', 'admin', sysdate(), '', null, '');
|
||||||
|
insert into sys_menu values('1008', '角色新增', '101', '2', '#', '', 'F', '0', '1', 'system:role:add', '#', 'admin', sysdate(), '', null, '');
|
||||||
|
insert into sys_menu values('1009', '角色修改', '101', '3', '#', '', 'F', '0', '1', 'system:role:edit', '#', 'admin', sysdate(), '', null, '');
|
||||||
|
insert into sys_menu values('1010', '角色删除', '101', '4', '#', '', 'F', '0', '1', 'system:role:remove', '#', 'admin', sysdate(), '', null, '');
|
||||||
|
insert into sys_menu values('1011', '角色导出', '101', '5', '#', '', 'F', '0', '1', 'system:role:export', '#', 'admin', sysdate(), '', null, '');
|
||||||
|
-- 菜单管理按钮
|
||||||
|
insert into sys_menu values('1012', '菜单查询', '102', '1', '#', '', 'F', '0', '1', 'system:menu:list', '#', 'admin', sysdate(), '', null, '');
|
||||||
|
insert into sys_menu values('1013', '菜单新增', '102', '2', '#', '', 'F', '0', '1', 'system:menu:add', '#', 'admin', sysdate(), '', null, '');
|
||||||
|
insert into sys_menu values('1014', '菜单修改', '102', '3', '#', '', 'F', '0', '1', 'system:menu:edit', '#', 'admin', sysdate(), '', null, '');
|
||||||
|
insert into sys_menu values('1015', '菜单删除', '102', '4', '#', '', 'F', '0', '1', 'system:menu:remove', '#', 'admin', sysdate(), '', null, '');
|
||||||
|
-- 部门管理按钮
|
||||||
|
insert into sys_menu values('1016', '部门查询', '103', '1', '#', '', 'F', '0', '1', 'system:dept:list', '#', 'admin', sysdate(), '', null, '');
|
||||||
|
insert into sys_menu values('1017', '部门新增', '103', '2', '#', '', 'F', '0', '1', 'system:dept:add', '#', 'admin', sysdate(), '', null, '');
|
||||||
|
insert into sys_menu values('1018', '部门修改', '103', '3', '#', '', 'F', '0', '1', 'system:dept:edit', '#', 'admin', sysdate(), '', null, '');
|
||||||
|
insert into sys_menu values('1019', '部门删除', '103', '4', '#', '', 'F', '0', '1', 'system:dept:remove', '#', 'admin', sysdate(), '', null, '');
|
||||||
|
-- 岗位管理按钮
|
||||||
|
insert into sys_menu values('1020', '岗位查询', '104', '1', '#', '', 'F', '0', '1', 'system:post:list', '#', 'admin', sysdate(), '', null, '');
|
||||||
|
insert into sys_menu values('1021', '岗位新增', '104', '2', '#', '', 'F', '0', '1', 'system:post:add', '#', 'admin', sysdate(), '', null, '');
|
||||||
|
insert into sys_menu values('1022', '岗位修改', '104', '3', '#', '', 'F', '0', '1', 'system:post:edit', '#', 'admin', sysdate(), '', null, '');
|
||||||
|
insert into sys_menu values('1023', '岗位删除', '104', '4', '#', '', 'F', '0', '1', 'system:post:remove', '#', 'admin', sysdate(), '', null, '');
|
||||||
|
insert into sys_menu values('1024', '岗位导出', '104', '5', '#', '', 'F', '0', '1', 'system:post:export', '#', 'admin', sysdate(), '', null, '');
|
||||||
|
-- 字典管理按钮
|
||||||
|
insert into sys_menu values('1025', '字典查询', '105', '1', '#', '', 'F', '0', '1', 'system:dict:list', '#', 'admin', sysdate(), '', null, '');
|
||||||
|
insert into sys_menu values('1026', '字典新增', '105', '2', '#', '', 'F', '0', '1', 'system:dict:add', '#', 'admin', sysdate(), '', null, '');
|
||||||
|
insert into sys_menu values('1027', '字典修改', '105', '3', '#', '', 'F', '0', '1', 'system:dict:edit', '#', 'admin', sysdate(), '', null, '');
|
||||||
|
insert into sys_menu values('1028', '字典删除', '105', '4', '#', '', 'F', '0', '1', 'system:dict:remove', '#', 'admin', sysdate(), '', null, '');
|
||||||
|
insert into sys_menu values('1029', '字典导出', '105', '5', '#', '', 'F', '0', '1', 'system:dict:export', '#', 'admin', sysdate(), '', null, '');
|
||||||
|
-- 参数设置按钮
|
||||||
|
insert into sys_menu values('1030', '参数查询', '106', '1', '#', '', 'F', '0', '1', 'system:config:list', '#', 'admin', sysdate(), '', null, '');
|
||||||
|
insert into sys_menu values('1031', '参数新增', '106', '2', '#', '', 'F', '0', '1', 'system:config:add', '#', 'admin', sysdate(), '', null, '');
|
||||||
|
insert into sys_menu values('1032', '参数修改', '106', '3', '#', '', 'F', '0', '1', 'system:config:edit', '#', 'admin', sysdate(), '', null, '');
|
||||||
|
insert into sys_menu values('1033', '参数删除', '106', '4', '#', '', 'F', '0', '1', 'system:config:remove', '#', 'admin', sysdate(), '', null, '');
|
||||||
|
insert into sys_menu values('1034', '参数导出', '106', '5', '#', '', 'F', '0', '1', 'system:config:export', '#', 'admin', sysdate(), '', null, '');
|
||||||
|
-- 通知公告按钮
|
||||||
|
insert into sys_menu values('1035', '公告查询', '107', '1', '#', '', 'F', '0', '1', 'system:notice:list', '#', 'admin', sysdate(), '', null, '');
|
||||||
|
insert into sys_menu values('1036', '公告新增', '107', '2', '#', '', 'F', '0', '1', 'system:notice:add', '#', 'admin', sysdate(), '', null, '');
|
||||||
|
insert into sys_menu values('1037', '公告修改', '107', '3', '#', '', 'F', '0', '1', 'system:notice:edit', '#', 'admin', sysdate(), '', null, '');
|
||||||
|
insert into sys_menu values('1038', '公告删除', '107', '4', '#', '', 'F', '0', '1', 'system:notice:remove', '#', 'admin', sysdate(), '', null, '');
|
||||||
|
-- 操作日志按钮
|
||||||
|
insert into sys_menu values('1039', '操作查询', '500', '1', '#', '', 'F', '0', '1', 'monitor:operlog:list', '#', 'admin', sysdate(), '', null, '');
|
||||||
|
insert into sys_menu values('1040', '操作删除', '500', '2', '#', '', 'F', '0', '1', 'monitor:operlog:remove', '#', 'admin', sysdate(), '', null, '');
|
||||||
|
insert into sys_menu values('1041', '详细信息', '500', '3', '#', '', 'F', '0', '1', 'monitor:operlog:detail', '#', 'admin', sysdate(), '', null, '');
|
||||||
|
insert into sys_menu values('1042', '日志导出', '500', '4', '#', '', 'F', '0', '1', 'monitor:operlog:export', '#', 'admin', sysdate(), '', null, '');
|
||||||
|
-- 登录日志按钮
|
||||||
|
insert into sys_menu values('1043', '登录查询', '501', '1', '#', '', 'F', '0', '1', 'monitor:logininfor:list', '#', 'admin', sysdate(), '', null, '');
|
||||||
|
insert into sys_menu values('1044', '登录删除', '501', '2', '#', '', 'F', '0', '1', 'monitor:logininfor:remove', '#', 'admin', sysdate(), '', null, '');
|
||||||
|
insert into sys_menu values('1045', '日志导出', '501', '3', '#', '', 'F', '0', '1', 'monitor:logininfor:export', '#', 'admin', sysdate(), '', null, '');
|
||||||
|
insert into sys_menu values('1046', '账户解锁', '501', '4', '#', '', 'F', '0', '1', 'monitor:logininfor:unlock', '#', 'admin', sysdate(), '', null, '');
|
||||||
|
-- 在线用户按钮
|
||||||
|
insert into sys_menu values('1047', '在线查询', '109', '1', '#', '', 'F', '0', '1', 'monitor:online:list', '#', 'admin', sysdate(), '', null, '');
|
||||||
|
insert into sys_menu values('1048', '批量强退', '109', '2', '#', '', 'F', '0', '1', 'monitor:online:batchForceLogout', '#', 'admin', sysdate(), '', null, '');
|
||||||
|
insert into sys_menu values('1049', '单条强退', '109', '3', '#', '', 'F', '0', '1', 'monitor:online:forceLogout', '#', 'admin', sysdate(), '', null, '');
|
||||||
|
-- 定时任务按钮
|
||||||
|
insert into sys_menu values('1050', '任务查询', '110', '1', '#', '', 'F', '0', '1', 'monitor:job:list', '#', 'admin', sysdate(), '', null, '');
|
||||||
|
insert into sys_menu values('1051', '任务新增', '110', '2', '#', '', 'F', '0', '1', 'monitor:job:add', '#', 'admin', sysdate(), '', null, '');
|
||||||
|
insert into sys_menu values('1052', '任务修改', '110', '3', '#', '', 'F', '0', '1', 'monitor:job:edit', '#', 'admin', sysdate(), '', null, '');
|
||||||
|
insert into sys_menu values('1053', '任务删除', '110', '4', '#', '', 'F', '0', '1', 'monitor:job:remove', '#', 'admin', sysdate(), '', null, '');
|
||||||
|
insert into sys_menu values('1054', '状态修改', '110', '5', '#', '', 'F', '0', '1', 'monitor:job:changeStatus', '#', 'admin', sysdate(), '', null, '');
|
||||||
|
insert into sys_menu values('1055', '任务详细', '110', '6', '#', '', 'F', '0', '1', 'monitor:job:detail', '#', 'admin', sysdate(), '', null, '');
|
||||||
|
insert into sys_menu values('1056', '任务导出', '110', '7', '#', '', 'F', '0', '1', 'monitor:job:export', '#', 'admin', sysdate(), '', null, '');
|
||||||
|
-- 代码生成按钮
|
||||||
|
insert into sys_menu values('1057', '生成查询', '115', '1', '#', '', 'F', '0', '1', 'tool:gen:list', '#', 'admin', sysdate(), '', null, '');
|
||||||
|
insert into sys_menu values('1058', '生成修改', '115', '2', '#', '', 'F', '0', '1', 'tool:gen:edit', '#', 'admin', sysdate(), '', null, '');
|
||||||
|
insert into sys_menu values('1059', '生成删除', '115', '3', '#', '', 'F', '0', '1', 'tool:gen:remove', '#', 'admin', sysdate(), '', null, '');
|
||||||
|
insert into sys_menu values('1060', '预览代码', '115', '4', '#', '', 'F', '0', '1', 'tool:gen:preview', '#', 'admin', sysdate(), '', null, '');
|
||||||
|
insert into sys_menu values('1061', '生成代码', '115', '5', '#', '', 'F', '0', '1', 'tool:gen:code', '#', 'admin', sysdate(), '', null, '');
|
||||||
|
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- 6、用户和角色关联表 用户N-1角色
|
||||||
|
-- ----------------------------
|
||||||
|
drop table if exists sys_user_role;
|
||||||
|
create table sys_user_role (
|
||||||
|
user_id bigint(20) not null comment '用户ID',
|
||||||
|
role_id bigint(20) not null comment '角色ID',
|
||||||
|
primary key(user_id, role_id)
|
||||||
|
) engine=innodb comment = '用户和角色关联表';
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- 初始化-用户和角色关联表数据
|
||||||
|
-- ----------------------------
|
||||||
|
insert into sys_user_role values ('1', '1');
|
||||||
|
insert into sys_user_role values ('2', '2');
|
||||||
|
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- 7、角色和菜单关联表 角色1-N菜单
|
||||||
|
-- ----------------------------
|
||||||
|
drop table if exists sys_role_menu;
|
||||||
|
create table sys_role_menu (
|
||||||
|
role_id bigint(20) not null comment '角色ID',
|
||||||
|
menu_id bigint(20) not null comment '菜单ID',
|
||||||
|
primary key(role_id, menu_id)
|
||||||
|
) engine=innodb comment = '角色和菜单关联表';
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- 初始化-角色和菜单关联表数据
|
||||||
|
-- ----------------------------
|
||||||
|
insert into sys_role_menu values ('2', '1');
|
||||||
|
insert into sys_role_menu values ('2', '2');
|
||||||
|
insert into sys_role_menu values ('2', '3');
|
||||||
|
insert into sys_role_menu values ('2', '4');
|
||||||
|
insert into sys_role_menu values ('2', '100');
|
||||||
|
insert into sys_role_menu values ('2', '101');
|
||||||
|
insert into sys_role_menu values ('2', '102');
|
||||||
|
insert into sys_role_menu values ('2', '103');
|
||||||
|
insert into sys_role_menu values ('2', '104');
|
||||||
|
insert into sys_role_menu values ('2', '105');
|
||||||
|
insert into sys_role_menu values ('2', '106');
|
||||||
|
insert into sys_role_menu values ('2', '107');
|
||||||
|
insert into sys_role_menu values ('2', '108');
|
||||||
|
insert into sys_role_menu values ('2', '109');
|
||||||
|
insert into sys_role_menu values ('2', '110');
|
||||||
|
insert into sys_role_menu values ('2', '111');
|
||||||
|
insert into sys_role_menu values ('2', '112');
|
||||||
|
insert into sys_role_menu values ('2', '113');
|
||||||
|
insert into sys_role_menu values ('2', '114');
|
||||||
|
insert into sys_role_menu values ('2', '115');
|
||||||
|
insert into sys_role_menu values ('2', '116');
|
||||||
|
insert into sys_role_menu values ('2', '500');
|
||||||
|
insert into sys_role_menu values ('2', '501');
|
||||||
|
insert into sys_role_menu values ('2', '1000');
|
||||||
|
insert into sys_role_menu values ('2', '1001');
|
||||||
|
insert into sys_role_menu values ('2', '1002');
|
||||||
|
insert into sys_role_menu values ('2', '1003');
|
||||||
|
insert into sys_role_menu values ('2', '1004');
|
||||||
|
insert into sys_role_menu values ('2', '1005');
|
||||||
|
insert into sys_role_menu values ('2', '1006');
|
||||||
|
insert into sys_role_menu values ('2', '1007');
|
||||||
|
insert into sys_role_menu values ('2', '1008');
|
||||||
|
insert into sys_role_menu values ('2', '1009');
|
||||||
|
insert into sys_role_menu values ('2', '1010');
|
||||||
|
insert into sys_role_menu values ('2', '1011');
|
||||||
|
insert into sys_role_menu values ('2', '1012');
|
||||||
|
insert into sys_role_menu values ('2', '1013');
|
||||||
|
insert into sys_role_menu values ('2', '1014');
|
||||||
|
insert into sys_role_menu values ('2', '1015');
|
||||||
|
insert into sys_role_menu values ('2', '1016');
|
||||||
|
insert into sys_role_menu values ('2', '1017');
|
||||||
|
insert into sys_role_menu values ('2', '1018');
|
||||||
|
insert into sys_role_menu values ('2', '1019');
|
||||||
|
insert into sys_role_menu values ('2', '1020');
|
||||||
|
insert into sys_role_menu values ('2', '1021');
|
||||||
|
insert into sys_role_menu values ('2', '1022');
|
||||||
|
insert into sys_role_menu values ('2', '1023');
|
||||||
|
insert into sys_role_menu values ('2', '1024');
|
||||||
|
insert into sys_role_menu values ('2', '1025');
|
||||||
|
insert into sys_role_menu values ('2', '1026');
|
||||||
|
insert into sys_role_menu values ('2', '1027');
|
||||||
|
insert into sys_role_menu values ('2', '1028');
|
||||||
|
insert into sys_role_menu values ('2', '1029');
|
||||||
|
insert into sys_role_menu values ('2', '1030');
|
||||||
|
insert into sys_role_menu values ('2', '1031');
|
||||||
|
insert into sys_role_menu values ('2', '1032');
|
||||||
|
insert into sys_role_menu values ('2', '1033');
|
||||||
|
insert into sys_role_menu values ('2', '1034');
|
||||||
|
insert into sys_role_menu values ('2', '1035');
|
||||||
|
insert into sys_role_menu values ('2', '1036');
|
||||||
|
insert into sys_role_menu values ('2', '1037');
|
||||||
|
insert into sys_role_menu values ('2', '1038');
|
||||||
|
insert into sys_role_menu values ('2', '1039');
|
||||||
|
insert into sys_role_menu values ('2', '1040');
|
||||||
|
insert into sys_role_menu values ('2', '1041');
|
||||||
|
insert into sys_role_menu values ('2', '1042');
|
||||||
|
insert into sys_role_menu values ('2', '1043');
|
||||||
|
insert into sys_role_menu values ('2', '1044');
|
||||||
|
insert into sys_role_menu values ('2', '1045');
|
||||||
|
insert into sys_role_menu values ('2', '1046');
|
||||||
|
insert into sys_role_menu values ('2', '1047');
|
||||||
|
insert into sys_role_menu values ('2', '1048');
|
||||||
|
insert into sys_role_menu values ('2', '1049');
|
||||||
|
insert into sys_role_menu values ('2', '1050');
|
||||||
|
insert into sys_role_menu values ('2', '1051');
|
||||||
|
insert into sys_role_menu values ('2', '1052');
|
||||||
|
insert into sys_role_menu values ('2', '1053');
|
||||||
|
insert into sys_role_menu values ('2', '1054');
|
||||||
|
insert into sys_role_menu values ('2', '1055');
|
||||||
|
insert into sys_role_menu values ('2', '1056');
|
||||||
|
insert into sys_role_menu values ('2', '1057');
|
||||||
|
insert into sys_role_menu values ('2', '1058');
|
||||||
|
insert into sys_role_menu values ('2', '1059');
|
||||||
|
insert into sys_role_menu values ('2', '1060');
|
||||||
|
insert into sys_role_menu values ('2', '1061');
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- 8、角色和部门关联表 角色1-N部门
|
||||||
|
-- ----------------------------
|
||||||
|
drop table if exists sys_role_dept;
|
||||||
|
create table sys_role_dept (
|
||||||
|
role_id bigint(20) not null comment '角色ID',
|
||||||
|
dept_id bigint(20) not null comment '部门ID',
|
||||||
|
primary key(role_id, dept_id)
|
||||||
|
) engine=innodb comment = '角色和部门关联表';
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- 初始化-角色和部门关联表数据
|
||||||
|
-- ----------------------------
|
||||||
|
insert into sys_role_dept values ('2', '100');
|
||||||
|
insert into sys_role_dept values ('2', '101');
|
||||||
|
insert into sys_role_dept values ('2', '105');
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- 9、用户与岗位关联表 用户1-N岗位
|
||||||
|
-- ----------------------------
|
||||||
|
drop table if exists sys_user_post;
|
||||||
|
create table sys_user_post
|
||||||
|
(
|
||||||
|
user_id bigint(20) not null comment '用户ID',
|
||||||
|
post_id bigint(20) not null comment '岗位ID',
|
||||||
|
primary key (user_id, post_id)
|
||||||
|
) engine=innodb comment = '用户与岗位关联表';
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- 初始化-用户与岗位关联表数据
|
||||||
|
-- ----------------------------
|
||||||
|
insert into sys_user_post values ('1', '1');
|
||||||
|
insert into sys_user_post values ('2', '2');
|
||||||
|
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- 10、操作日志记录
|
||||||
|
-- ----------------------------
|
||||||
|
drop table if exists sys_oper_log;
|
||||||
|
create table sys_oper_log (
|
||||||
|
oper_id bigint(20) not null auto_increment comment '日志主键',
|
||||||
|
title varchar(50) default '' comment '模块标题',
|
||||||
|
business_type int(2) default 0 comment '业务类型(0其它 1新增 2修改 3删除)',
|
||||||
|
method varchar(200) default '' comment '方法名称',
|
||||||
|
request_method varchar(10) default '' comment '请求方式',
|
||||||
|
operator_type int(1) default 0 comment '操作类别(0其它 1后台用户 2手机端用户)',
|
||||||
|
oper_name varchar(50) default '' comment '操作人员',
|
||||||
|
dept_name varchar(50) default '' comment '部门名称',
|
||||||
|
oper_url varchar(255) default '' comment '请求URL',
|
||||||
|
oper_ip varchar(128) default '' comment '主机地址',
|
||||||
|
oper_location varchar(255) default '' comment '操作地点',
|
||||||
|
oper_param varchar(2000) default '' comment '请求参数',
|
||||||
|
json_result varchar(2000) default '' comment '返回参数',
|
||||||
|
status int(1) default 0 comment '操作状态(0正常 1异常)',
|
||||||
|
error_msg varchar(2000) default '' comment '错误消息',
|
||||||
|
oper_time datetime comment '操作时间',
|
||||||
|
cost_time bigint(20) default 0 comment '消耗时间',
|
||||||
|
primary key (oper_id),
|
||||||
|
key idx_sys_oper_log_bt (business_type),
|
||||||
|
key idx_sys_oper_log_s (status),
|
||||||
|
key idx_sys_oper_log_ot (oper_time)
|
||||||
|
) engine=innodb auto_increment=100 comment = '操作日志记录';
|
||||||
|
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- 11、字典类型表
|
||||||
|
-- ----------------------------
|
||||||
|
drop table if exists sys_dict_type;
|
||||||
|
create table sys_dict_type
|
||||||
|
(
|
||||||
|
dict_id bigint(20) not null auto_increment comment '字典主键',
|
||||||
|
dict_name varchar(100) default '' comment '字典名称',
|
||||||
|
dict_type varchar(100) default '' comment '字典类型',
|
||||||
|
status char(1) default '0' comment '状态(0正常 1停用)',
|
||||||
|
create_by varchar(64) default '' comment '创建者',
|
||||||
|
create_time datetime comment '创建时间',
|
||||||
|
update_by varchar(64) default '' comment '更新者',
|
||||||
|
update_time datetime comment '更新时间',
|
||||||
|
remark varchar(500) default null comment '备注',
|
||||||
|
primary key (dict_id),
|
||||||
|
unique (dict_type)
|
||||||
|
) engine=innodb auto_increment=100 comment = '字典类型表';
|
||||||
|
|
||||||
|
insert into sys_dict_type values(1, '用户性别', 'sys_user_sex', '0', 'admin', sysdate(), '', null, '用户性别列表');
|
||||||
|
insert into sys_dict_type values(2, '菜单状态', 'sys_show_hide', '0', 'admin', sysdate(), '', null, '菜单状态列表');
|
||||||
|
insert into sys_dict_type values(3, '系统开关', 'sys_normal_disable', '0', 'admin', sysdate(), '', null, '系统开关列表');
|
||||||
|
insert into sys_dict_type values(4, '任务状态', 'sys_job_status', '0', 'admin', sysdate(), '', null, '任务状态列表');
|
||||||
|
insert into sys_dict_type values(5, '任务分组', 'sys_job_group', '0', 'admin', sysdate(), '', null, '任务分组列表');
|
||||||
|
insert into sys_dict_type values(6, '系统是否', 'sys_yes_no', '0', 'admin', sysdate(), '', null, '系统是否列表');
|
||||||
|
insert into sys_dict_type values(7, '通知类型', 'sys_notice_type', '0', 'admin', sysdate(), '', null, '通知类型列表');
|
||||||
|
insert into sys_dict_type values(8, '通知状态', 'sys_notice_status', '0', 'admin', sysdate(), '', null, '通知状态列表');
|
||||||
|
insert into sys_dict_type values(9, '操作类型', 'sys_oper_type', '0', 'admin', sysdate(), '', null, '操作类型列表');
|
||||||
|
insert into sys_dict_type values(10, '系统状态', 'sys_common_status', '0', 'admin', sysdate(), '', null, '登录状态列表');
|
||||||
|
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- 12、字典数据表
|
||||||
|
-- ----------------------------
|
||||||
|
drop table if exists sys_dict_data;
|
||||||
|
create table sys_dict_data
|
||||||
|
(
|
||||||
|
dict_code bigint(20) not null auto_increment comment '字典编码',
|
||||||
|
dict_sort int(4) default 0 comment '字典排序',
|
||||||
|
dict_label varchar(100) default '' comment '字典标签',
|
||||||
|
dict_value varchar(100) default '' comment '字典键值',
|
||||||
|
dict_type varchar(100) default '' comment '字典类型',
|
||||||
|
css_class varchar(100) default null comment '样式属性(其他样式扩展)',
|
||||||
|
list_class varchar(100) default null comment '表格回显样式',
|
||||||
|
is_default char(1) default 'N' comment '是否默认(Y是 N否)',
|
||||||
|
status char(1) default '0' comment '状态(0正常 1停用)',
|
||||||
|
create_by varchar(64) default '' comment '创建者',
|
||||||
|
create_time datetime comment '创建时间',
|
||||||
|
update_by varchar(64) default '' comment '更新者',
|
||||||
|
update_time datetime comment '更新时间',
|
||||||
|
remark varchar(500) default null comment '备注',
|
||||||
|
primary key (dict_code)
|
||||||
|
) engine=innodb auto_increment=100 comment = '字典数据表';
|
||||||
|
|
||||||
|
insert into sys_dict_data values(1, 1, '男', '0', 'sys_user_sex', '', '', 'Y', '0', 'admin', sysdate(), '', null, '性别男');
|
||||||
|
insert into sys_dict_data values(2, 2, '女', '1', 'sys_user_sex', '', '', 'N', '0', 'admin', sysdate(), '', null, '性别女');
|
||||||
|
insert into sys_dict_data values(3, 3, '未知', '2', 'sys_user_sex', '', '', 'N', '0', 'admin', sysdate(), '', null, '性别未知');
|
||||||
|
insert into sys_dict_data values(4, 1, '显示', '0', 'sys_show_hide', '', 'primary', 'Y', '0', 'admin', sysdate(), '', null, '显示菜单');
|
||||||
|
insert into sys_dict_data values(5, 2, '隐藏', '1', 'sys_show_hide', '', 'danger', 'N', '0', 'admin', sysdate(), '', null, '隐藏菜单');
|
||||||
|
insert into sys_dict_data values(6, 1, '正常', '0', 'sys_normal_disable', '', 'primary', 'Y', '0', 'admin', sysdate(), '', null, '正常状态');
|
||||||
|
insert into sys_dict_data values(7, 2, '停用', '1', 'sys_normal_disable', '', 'danger', 'N', '0', 'admin', sysdate(), '', null, '停用状态');
|
||||||
|
insert into sys_dict_data values(8, 1, '正常', '0', 'sys_job_status', '', 'primary', 'Y', '0', 'admin', sysdate(), '', null, '正常状态');
|
||||||
|
insert into sys_dict_data values(9, 2, '暂停', '1', 'sys_job_status', '', 'danger', 'N', '0', 'admin', sysdate(), '', null, '停用状态');
|
||||||
|
insert into sys_dict_data values(10, 1, '默认', 'DEFAULT', 'sys_job_group', '', '', 'Y', '0', 'admin', sysdate(), '', null, '默认分组');
|
||||||
|
insert into sys_dict_data values(11, 2, '系统', 'SYSTEM', 'sys_job_group', '', '', 'N', '0', 'admin', sysdate(), '', null, '系统分组');
|
||||||
|
insert into sys_dict_data values(12, 1, '是', 'Y', 'sys_yes_no', '', 'primary', 'Y', '0', 'admin', sysdate(), '', null, '系统默认是');
|
||||||
|
insert into sys_dict_data values(13, 2, '否', 'N', 'sys_yes_no', '', 'danger', 'N', '0', 'admin', sysdate(), '', null, '系统默认否');
|
||||||
|
insert into sys_dict_data values(14, 1, '通知', '1', 'sys_notice_type', '', 'warning', 'Y', '0', 'admin', sysdate(), '', null, '通知');
|
||||||
|
insert into sys_dict_data values(15, 2, '公告', '2', 'sys_notice_type', '', 'success', 'N', '0', 'admin', sysdate(), '', null, '公告');
|
||||||
|
insert into sys_dict_data values(16, 1, '正常', '0', 'sys_notice_status', '', 'primary', 'Y', '0', 'admin', sysdate(), '', null, '正常状态');
|
||||||
|
insert into sys_dict_data values(17, 2, '关闭', '1', 'sys_notice_status', '', 'danger', 'N', '0', 'admin', sysdate(), '', null, '关闭状态');
|
||||||
|
insert into sys_dict_data values(18, 99, '其他', '0', 'sys_oper_type', '', 'info', 'N', '0', 'admin', sysdate(), '', null, '其他操作');
|
||||||
|
insert into sys_dict_data values(19, 1, '新增', '1', 'sys_oper_type', '', 'info', 'N', '0', 'admin', sysdate(), '', null, '新增操作');
|
||||||
|
insert into sys_dict_data values(20, 2, '修改', '2', 'sys_oper_type', '', 'info', 'N', '0', 'admin', sysdate(), '', null, '修改操作');
|
||||||
|
insert into sys_dict_data values(21, 3, '删除', '3', 'sys_oper_type', '', 'danger', 'N', '0', 'admin', sysdate(), '', null, '删除操作');
|
||||||
|
insert into sys_dict_data values(22, 4, '授权', '4', 'sys_oper_type', '', 'primary', 'N', '0', 'admin', sysdate(), '', null, '授权操作');
|
||||||
|
insert into sys_dict_data values(23, 5, '导出', '5', 'sys_oper_type', '', 'warning', 'N', '0', 'admin', sysdate(), '', null, '导出操作');
|
||||||
|
insert into sys_dict_data values(24, 6, '导入', '6', 'sys_oper_type', '', 'warning', 'N', '0', 'admin', sysdate(), '', null, '导入操作');
|
||||||
|
insert into sys_dict_data values(25, 7, '强退', '7', 'sys_oper_type', '', 'danger', 'N', '0', 'admin', sysdate(), '', null, '强退操作');
|
||||||
|
insert into sys_dict_data values(26, 8, '生成代码', '8', 'sys_oper_type', '', 'warning', 'N', '0', 'admin', sysdate(), '', null, '生成操作');
|
||||||
|
insert into sys_dict_data values(27, 9, '清空数据', '9', 'sys_oper_type', '', 'danger', 'N', '0', 'admin', sysdate(), '', null, '清空操作');
|
||||||
|
insert into sys_dict_data values(28, 1, '成功', '0', 'sys_common_status', '', 'primary', 'N', '0', 'admin', sysdate(), '', null, '正常状态');
|
||||||
|
insert into sys_dict_data values(29, 2, '失败', '1', 'sys_common_status', '', 'danger', 'N', '0', 'admin', sysdate(), '', null, '停用状态');
|
||||||
|
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- 13、参数配置表
|
||||||
|
-- ----------------------------
|
||||||
|
drop table if exists sys_config;
|
||||||
|
create table sys_config (
|
||||||
|
config_id int(5) not null auto_increment comment '参数主键',
|
||||||
|
config_name varchar(100) default '' comment '参数名称',
|
||||||
|
config_key varchar(100) default '' comment '参数键名',
|
||||||
|
config_value varchar(500) default '' comment '参数键值',
|
||||||
|
config_type char(1) default 'N' comment '系统内置(Y是 N否)',
|
||||||
|
create_by varchar(64) default '' comment '创建者',
|
||||||
|
create_time datetime comment '创建时间',
|
||||||
|
update_by varchar(64) default '' comment '更新者',
|
||||||
|
update_time datetime comment '更新时间',
|
||||||
|
remark varchar(500) default null comment '备注',
|
||||||
|
primary key (config_id)
|
||||||
|
) engine=innodb auto_increment=100 comment = '参数配置表';
|
||||||
|
|
||||||
|
insert into sys_config values(1, '主框架页-默认皮肤样式名称', 'sys.index.skinName', 'skin-blue', 'Y', 'admin', sysdate(), '', null, '蓝色 skin-blue、绿色 skin-green、紫色 skin-purple、红色 skin-red、黄色 skin-yellow');
|
||||||
|
insert into sys_config values(2, '用户管理-账号初始密码', 'sys.user.initPassword', '123456', 'Y', 'admin', sysdate(), '', null, '初始化密码 123456');
|
||||||
|
insert into sys_config values(3, '主框架页-侧边栏主题', 'sys.index.sideTheme', 'theme-dark', 'Y', 'admin', sysdate(), '', null, '深黑主题theme-dark,浅色主题theme-light,深蓝主题theme-blue');
|
||||||
|
insert into sys_config values(4, '账号自助-是否开启用户注册功能', 'sys.account.registerUser', 'false', 'Y', 'admin', sysdate(), '', null, '是否开启注册用户功能(true开启,false关闭)');
|
||||||
|
insert into sys_config values(5, '用户管理-密码字符范围', 'sys.account.chrtype', '0', 'Y', 'admin', sysdate(), '', null, '默认任意字符范围,0任意(密码可以输入任意字符),1数字(密码只能为0-9数字),2英文字母(密码只能为a-z和A-Z字母),3字母和数字(密码必须包含字母,数字),4字母数字和特殊字符(目前支持的特殊字符包括:~!@#$%^&*()-=_+)');
|
||||||
|
insert into sys_config values(6, '用户管理-初始密码修改策略', 'sys.account.initPasswordModify', '1', 'Y', 'admin', sysdate(), '', null, '0:初始密码修改策略关闭,没有任何提示,1:提醒用户,如果未修改初始密码,则在登录时就会提醒修改密码对话框');
|
||||||
|
insert into sys_config values(7, '用户管理-账号密码更新周期', 'sys.account.passwordValidateDays', '0', 'Y', 'admin', sysdate(), '', null, '密码更新周期(填写数字,数据初始化值为0不限制,若修改必须为大于0小于365的正整数),如果超过这个周期登录系统时,则在登录时就会提醒修改密码对话框');
|
||||||
|
insert into sys_config values(8, '主框架页-菜单导航显示风格', 'sys.index.menuStyle', 'default', 'Y', 'admin', sysdate(), '', null, '菜单导航显示风格(default为左侧导航菜单,topnav为顶部导航菜单)');
|
||||||
|
insert into sys_config values(9, '主框架页-是否开启页脚', 'sys.index.footer', 'true', 'Y', 'admin', sysdate(), '', null, '是否开启底部页脚显示(true显示,false隐藏)');
|
||||||
|
insert into sys_config values(10, '主框架页-是否开启页签', 'sys.index.tagsView', 'true', 'Y', 'admin', sysdate(), '', null, '是否开启菜单多页签显示(true显示,false隐藏)');
|
||||||
|
insert into sys_config values(11, '用户登录-黑名单列表', 'sys.login.blackIPList', '', 'Y', 'admin', sysdate(), '', null, '设置登录IP黑名单限制,多个匹配项以;分隔,支持匹配(*通配、网段)');
|
||||||
|
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- 14、系统访问记录
|
||||||
|
-- ----------------------------
|
||||||
|
drop table if exists sys_logininfor;
|
||||||
|
create table sys_logininfor (
|
||||||
|
info_id bigint(20) not null auto_increment comment '访问ID',
|
||||||
|
login_name varchar(50) default '' comment '登录账号',
|
||||||
|
ipaddr varchar(128) default '' comment '登录IP地址',
|
||||||
|
login_location varchar(255) default '' comment '登录地点',
|
||||||
|
browser varchar(50) default '' comment '浏览器类型',
|
||||||
|
os varchar(50) default '' comment '操作系统',
|
||||||
|
status char(1) default '0' comment '登录状态(0成功 1失败)',
|
||||||
|
msg varchar(255) default '' comment '提示消息',
|
||||||
|
login_time datetime comment '访问时间',
|
||||||
|
primary key (info_id),
|
||||||
|
key idx_sys_logininfor_s (status),
|
||||||
|
key idx_sys_logininfor_lt (login_time)
|
||||||
|
) engine=innodb auto_increment=100 comment = '系统访问记录';
|
||||||
|
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- 15、在线用户记录
|
||||||
|
-- ----------------------------
|
||||||
|
drop table if exists sys_user_online;
|
||||||
|
create table sys_user_online (
|
||||||
|
sessionId varchar(50) default '' comment '用户会话id',
|
||||||
|
login_name varchar(50) default '' comment '登录账号',
|
||||||
|
dept_name varchar(50) default '' comment '部门名称',
|
||||||
|
ipaddr varchar(128) default '' comment '登录IP地址',
|
||||||
|
login_location varchar(255) default '' comment '登录地点',
|
||||||
|
browser varchar(50) default '' comment '浏览器类型',
|
||||||
|
os varchar(50) default '' comment '操作系统',
|
||||||
|
status varchar(10) default '' comment '在线状态on_line在线off_line离线',
|
||||||
|
start_timestamp datetime comment 'session创建时间',
|
||||||
|
last_access_time datetime comment 'session最后访问时间',
|
||||||
|
expire_time int(5) default 0 comment '超时时间,单位为分钟',
|
||||||
|
primary key (sessionId)
|
||||||
|
) engine=innodb comment = '在线用户记录';
|
||||||
|
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- 16、定时任务调度表
|
||||||
|
-- ----------------------------
|
||||||
|
drop table if exists sys_job;
|
||||||
|
create table sys_job (
|
||||||
|
job_id bigint(20) not null auto_increment comment '任务ID',
|
||||||
|
job_name varchar(64) default '' comment '任务名称',
|
||||||
|
job_group varchar(64) default 'DEFAULT' comment '任务组名',
|
||||||
|
invoke_target varchar(500) not null comment '调用目标字符串',
|
||||||
|
cron_expression varchar(255) default '' comment 'cron执行表达式',
|
||||||
|
misfire_policy varchar(20) default '3' comment '计划执行错误策略(1立即执行 2执行一次 3放弃执行)',
|
||||||
|
concurrent char(1) default '1' comment '是否并发执行(0允许 1禁止)',
|
||||||
|
status char(1) default '0' comment '状态(0正常 1暂停)',
|
||||||
|
create_by varchar(64) default '' comment '创建者',
|
||||||
|
create_time datetime comment '创建时间',
|
||||||
|
update_by varchar(64) default '' comment '更新者',
|
||||||
|
update_time datetime comment '更新时间',
|
||||||
|
remark varchar(500) default '' comment '备注信息',
|
||||||
|
primary key (job_id, job_name, job_group)
|
||||||
|
) engine=innodb auto_increment=100 comment = '定时任务调度表';
|
||||||
|
|
||||||
|
insert into sys_job values(1, '系统默认(无参)', 'DEFAULT', 'ryTask.ryNoParams', '0/10 * * * * ?', '3', '1', '1', 'admin', sysdate(), '', null, '');
|
||||||
|
insert into sys_job values(2, '系统默认(有参)', 'DEFAULT', 'ryTask.ryParams(\'ry\')', '0/15 * * * * ?', '3', '1', '1', 'admin', sysdate(), '', null, '');
|
||||||
|
insert into sys_job values(3, '系统默认(多参)', 'DEFAULT', 'ryTask.ryMultipleParams(\'ry\', true, 2000L, 316.50D, 100)', '0/20 * * * * ?', '3', '1', '1', 'admin', sysdate(), '', null, '');
|
||||||
|
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- 17、定时任务调度日志表
|
||||||
|
-- ----------------------------
|
||||||
|
drop table if exists sys_job_log;
|
||||||
|
create table sys_job_log (
|
||||||
|
job_log_id bigint(20) not null auto_increment comment '任务日志ID',
|
||||||
|
job_name varchar(64) not null comment '任务名称',
|
||||||
|
job_group varchar(64) not null comment '任务组名',
|
||||||
|
invoke_target varchar(500) not null comment '调用目标字符串',
|
||||||
|
job_message varchar(500) comment '日志信息',
|
||||||
|
status char(1) default '0' comment '执行状态(0正常 1失败)',
|
||||||
|
exception_info varchar(2000) default '' comment '异常信息',
|
||||||
|
create_time datetime comment '创建时间',
|
||||||
|
primary key (job_log_id)
|
||||||
|
) engine=innodb comment = '定时任务调度日志表';
|
||||||
|
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- 18、通知公告表
|
||||||
|
-- ----------------------------
|
||||||
|
drop table if exists sys_notice;
|
||||||
|
create table sys_notice (
|
||||||
|
notice_id int(4) not null auto_increment comment '公告ID',
|
||||||
|
notice_title varchar(50) not null comment '公告标题',
|
||||||
|
notice_type char(1) not null comment '公告类型(1通知 2公告)',
|
||||||
|
notice_content longblob default null comment '公告内容',
|
||||||
|
status char(1) default '0' comment '公告状态(0正常 1关闭)',
|
||||||
|
create_by varchar(64) default '' comment '创建者',
|
||||||
|
create_time datetime comment '创建时间',
|
||||||
|
update_by varchar(64) default '' comment '更新者',
|
||||||
|
update_time datetime comment '更新时间',
|
||||||
|
remark varchar(255) default null comment '备注',
|
||||||
|
primary key (notice_id)
|
||||||
|
) engine=innodb auto_increment=10 comment = '通知公告表';
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- 初始化-公告信息表数据
|
||||||
|
-- ----------------------------
|
||||||
|
insert into sys_notice values('1', '温馨提醒:2018-07-01 若依新版本发布啦', '2', '新版本内容', '0', 'admin', sysdate(), '', null, '管理员');
|
||||||
|
insert into sys_notice values('2', '维护通知:2018-07-01 若依系统凌晨维护', '1', '维护内容', '0', 'admin', sysdate(), '', null, '管理员');
|
||||||
|
insert into sys_notice values('3', '若依开源框架介绍', '1', '<p><span style=\"color: rgb(230, 0, 0);\">项目介绍</span></p><p><font color=\"#333333\">RuoYi开源项目是为企业用户定制的后台脚手架框架,为企业打造的一站式解决方案,降低企业开发成本,提升开发效率。主要包括用户管理、角色管理、部门管理、菜单管理、参数管理、字典管理、</font><span style=\"color: rgb(51, 51, 51);\">岗位管理</span><span style=\"color: rgb(51, 51, 51);\">、定时任务</span><span style=\"color: rgb(51, 51, 51);\">、</span><span style=\"color: rgb(51, 51, 51);\">服务监控、登录日志、操作日志、代码生成等功能。其中,还支持多数据源、数据权限、国际化、Redis缓存、Docker部署、滑动验证码、第三方认证登录、分布式事务、</span><font color=\"#333333\">分布式文件存储</font><span style=\"color: rgb(51, 51, 51);\">、分库分表处理等技术特点。</span></p><p><img src=\"https://foruda.gitee.com/images/1705030583977401651/5ed5db6a_1151004.png\" style=\"width: 64px;\"><br></p><p><span style=\"color: rgb(230, 0, 0);\">官网及演示</span></p><p><span style=\"color: rgb(51, 51, 51);\">若依官网地址: </span><a href=\"http://ruoyi.vip\" target=\"_blank\">http://ruoyi.vip</a><a href=\"http://ruoyi.vip\" target=\"_blank\"></a></p><p><span style=\"color: rgb(51, 51, 51);\">若依文档地址: </span><a href=\"http://doc.ruoyi.vip\" target=\"_blank\">http://doc.ruoyi.vip</a><br></p><p><span style=\"color: rgb(51, 51, 51);\">演示地址【不分离版】: </span><a href=\"http://demo.ruoyi.vip\" target=\"_blank\">http://demo.ruoyi.vip</a></p><p><span style=\"color: rgb(51, 51, 51);\">演示地址【分离版本】: </span><a href=\"http://vue.ruoyi.vip\" target=\"_blank\">http://vue.ruoyi.vip</a></p><p><span style=\"color: rgb(51, 51, 51);\">演示地址【微服务版】: </span><a href=\"http://cloud.ruoyi.vip\" target=\"_blank\">http://cloud.ruoyi.vip</a></p><p><span style=\"color: rgb(51, 51, 51);\">演示地址【移动端版】: </span><a href=\"http://h5.ruoyi.vip\" target=\"_blank\">http://h5.ruoyi.vip</a></p><p><br style=\"color: rgb(48, 49, 51); font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 12px;\"></p>', '0', 'admin', sysdate(), '', null, '管理员');
|
||||||
|
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- 19、代码生成业务表
|
||||||
|
-- ----------------------------
|
||||||
|
drop table if exists gen_table;
|
||||||
|
create table gen_table (
|
||||||
|
table_id bigint(20) not null auto_increment comment '编号',
|
||||||
|
table_name varchar(200) default '' comment '表名称',
|
||||||
|
table_comment varchar(500) default '' comment '表描述',
|
||||||
|
sub_table_name varchar(64) default null comment '关联子表的表名',
|
||||||
|
sub_table_fk_name varchar(64) default null comment '子表关联的外键名',
|
||||||
|
class_name varchar(100) default '' comment '实体类名称',
|
||||||
|
tpl_category varchar(200) default 'crud' comment '使用的模板(crud单表操作 tree树表操作 sub主子表操作)',
|
||||||
|
package_name varchar(100) comment '生成包路径',
|
||||||
|
module_name varchar(30) comment '生成模块名',
|
||||||
|
business_name varchar(30) comment '生成业务名',
|
||||||
|
function_name varchar(50) comment '生成功能名',
|
||||||
|
function_author varchar(50) comment '生成功能作者',
|
||||||
|
form_col_num int(1) default 1 comment '表单布局(单列 双列 三列)',
|
||||||
|
gen_type char(1) default '0' comment '生成代码方式(0zip压缩包 1自定义路径)',
|
||||||
|
gen_path varchar(200) default '/' comment '生成路径(不填默认项目路径)',
|
||||||
|
options varchar(1000) comment '其它生成选项',
|
||||||
|
create_by varchar(64) default '' comment '创建者',
|
||||||
|
create_time datetime comment '创建时间',
|
||||||
|
update_by varchar(64) default '' comment '更新者',
|
||||||
|
update_time datetime comment '更新时间',
|
||||||
|
remark varchar(500) default null comment '备注',
|
||||||
|
primary key (table_id)
|
||||||
|
) engine=innodb auto_increment=1 comment = '代码生成业务表';
|
||||||
|
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- 20、代码生成业务表字段
|
||||||
|
-- ----------------------------
|
||||||
|
drop table if exists gen_table_column;
|
||||||
|
create table gen_table_column (
|
||||||
|
column_id bigint(20) not null auto_increment comment '编号',
|
||||||
|
table_id bigint(20) comment '归属表编号',
|
||||||
|
column_name varchar(200) comment '列名称',
|
||||||
|
column_comment varchar(500) comment '列描述',
|
||||||
|
column_type varchar(100) comment '列类型',
|
||||||
|
java_type varchar(500) comment 'JAVA类型',
|
||||||
|
java_field varchar(200) comment 'JAVA字段名',
|
||||||
|
is_pk char(1) comment '是否主键(1是)',
|
||||||
|
is_increment char(1) comment '是否自增(1是)',
|
||||||
|
is_required char(1) comment '是否必填(1是)',
|
||||||
|
is_insert char(1) comment '是否为插入字段(1是)',
|
||||||
|
is_edit char(1) comment '是否编辑字段(1是)',
|
||||||
|
is_list char(1) comment '是否列表字段(1是)',
|
||||||
|
is_query char(1) comment '是否查询字段(1是)',
|
||||||
|
query_type varchar(200) default 'EQ' comment '查询方式(等于、不等于、大于、小于、范围)',
|
||||||
|
html_type varchar(200) comment '显示类型(文本框、文本域、下拉框、复选框、单选框、日期控件)',
|
||||||
|
dict_type varchar(200) default '' comment '字典类型',
|
||||||
|
sort int comment '排序',
|
||||||
|
create_by varchar(64) default '' comment '创建者',
|
||||||
|
create_time datetime comment '创建时间',
|
||||||
|
update_by varchar(64) default '' comment '更新者',
|
||||||
|
update_time datetime comment '更新时间',
|
||||||
|
primary key (column_id)
|
||||||
|
) engine=innodb auto_increment=1 comment = '代码生成业务表字段';
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
package com.ruoyi;
|
||||||
|
|
||||||
|
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 RuoYiApplication
|
||||||
|
{
|
||||||
|
public static void main(String[] args)
|
||||||
|
{
|
||||||
|
// System.setProperty("spring.devtools.restart.enabled", "false");
|
||||||
|
SpringApplication.run(RuoYiApplication.class, args);
|
||||||
|
System.out.println("(♥◠‿◠)ノ゙ 若依启动成功 ლ(´ڡ`ლ)゙ \n" +
|
||||||
|
" .-------. ____ __ \n" +
|
||||||
|
" | _ _ \\ \\ \\ / / \n" +
|
||||||
|
" | ( ' ) | \\ _. / ' \n" +
|
||||||
|
" |(_ o _) / _( )_ .' \n" +
|
||||||
|
" | (_,_).' __ ___(_ o _)' \n" +
|
||||||
|
" | |\\ \\ | || |(_,_)' \n" +
|
||||||
|
" | | \\ `' /| `-' / \n" +
|
||||||
|
" | | \\ / \\ / \n" +
|
||||||
|
" ''-' `'-' `-..-' ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
package com.ruoyi;
|
||||||
|
|
||||||
|
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||||
|
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* web容器中进行部署
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
public class RuoYiServletInitializer extends SpringBootServletInitializer
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
protected SpringApplicationBuilder configure(SpringApplicationBuilder application)
|
||||||
|
{
|
||||||
|
return application.sources(RuoYiApplication.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
package com.ruoyi.common.constant;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通用数据库映射Map数据
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
public class CommonMap
|
||||||
|
{
|
||||||
|
/** 状态编码转换 */
|
||||||
|
public static Map<String, String> javaTypeMap = new HashMap<String, String>();
|
||||||
|
|
||||||
|
static
|
||||||
|
{
|
||||||
|
initJavaTypeMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回状态映射
|
||||||
|
*/
|
||||||
|
public static void initJavaTypeMap()
|
||||||
|
{
|
||||||
|
javaTypeMap.put("tinyint", "Integer");
|
||||||
|
javaTypeMap.put("smallint", "Integer");
|
||||||
|
javaTypeMap.put("mediumint", "Integer");
|
||||||
|
javaTypeMap.put("int", "Integer");
|
||||||
|
javaTypeMap.put("number", "Integer");
|
||||||
|
javaTypeMap.put("integer", "integer");
|
||||||
|
javaTypeMap.put("bigint", "Long");
|
||||||
|
javaTypeMap.put("float", "Float");
|
||||||
|
javaTypeMap.put("double", "Double");
|
||||||
|
javaTypeMap.put("decimal", "BigDecimal");
|
||||||
|
javaTypeMap.put("bit", "Boolean");
|
||||||
|
javaTypeMap.put("char", "String");
|
||||||
|
javaTypeMap.put("varchar", "String");
|
||||||
|
javaTypeMap.put("varchar2", "String");
|
||||||
|
javaTypeMap.put("tinytext", "String");
|
||||||
|
javaTypeMap.put("text", "String");
|
||||||
|
javaTypeMap.put("mediumtext", "String");
|
||||||
|
javaTypeMap.put("longtext", "String");
|
||||||
|
javaTypeMap.put("time", "Date");
|
||||||
|
javaTypeMap.put("date", "Date");
|
||||||
|
javaTypeMap.put("datetime", "Date");
|
||||||
|
javaTypeMap.put("timestamp", "Date");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,122 @@
|
||||||
|
package com.ruoyi.common.constant;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通用常量信息
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
public class Constants
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* UTF-8 字符集
|
||||||
|
*/
|
||||||
|
public static final String UTF8 = "UTF-8";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GBK 字符集
|
||||||
|
*/
|
||||||
|
public static final String GBK = "GBK";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统语言
|
||||||
|
*/
|
||||||
|
public static final Locale DEFAULT_LOCALE = Locale.SIMPLIFIED_CHINESE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* http请求
|
||||||
|
*/
|
||||||
|
public static final String HTTP = "http://";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* https请求
|
||||||
|
*/
|
||||||
|
public static final String HTTPS = "https://";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通用成功标识
|
||||||
|
*/
|
||||||
|
public static final String SUCCESS = "0";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通用失败标识
|
||||||
|
*/
|
||||||
|
public static final String FAIL = "1";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录成功
|
||||||
|
*/
|
||||||
|
public static final String LOGIN_SUCCESS = "Success";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注销
|
||||||
|
*/
|
||||||
|
public static final String LOGOUT = "Logout";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注册
|
||||||
|
*/
|
||||||
|
public static final String REGISTER = "Register";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录失败
|
||||||
|
*/
|
||||||
|
public static final String LOGIN_FAIL = "Error";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统用户授权缓存
|
||||||
|
*/
|
||||||
|
public static final String SYS_AUTH_CACHE = "sys-authCache";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 参数管理 cache name
|
||||||
|
*/
|
||||||
|
public static final String SYS_CONFIG_CACHE = "sys-config";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 参数管理 cache key
|
||||||
|
*/
|
||||||
|
public static final String SYS_CONFIG_KEY = "sys_config:";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字典管理 cache name
|
||||||
|
*/
|
||||||
|
public static final String SYS_DICT_CACHE = "sys-dict";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字典管理 cache key
|
||||||
|
*/
|
||||||
|
public static final String SYS_DICT_KEY = "sys_dict:";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 资源映射路径 前缀
|
||||||
|
*/
|
||||||
|
public static final String RESOURCE_PREFIX = "/profile";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RMI 远程方法调用
|
||||||
|
*/
|
||||||
|
public static final String LOOKUP_RMI = "rmi:";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LDAP 远程方法调用
|
||||||
|
*/
|
||||||
|
public static final String LOOKUP_LDAP = "ldap:";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LDAPS 远程方法调用
|
||||||
|
*/
|
||||||
|
public static final String LOOKUP_LDAPS = "ldaps:";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 定时任务白名单配置(仅允许访问的包名,如其他需要可以自行添加)
|
||||||
|
*/
|
||||||
|
public static final String[] JOB_WHITELIST_STR = { "com.ruoyi.framework.task" };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 定时任务违规的字符
|
||||||
|
*/
|
||||||
|
public static final String[] JOB_ERROR_STR = { "java.net.URL", "javax.naming.InitialContext", "org.yaml.snakeyaml",
|
||||||
|
"org.springframework", "com.ruoyi.common.utils.file", "com.ruoyi.framework.config", "com.ruoyi.project.tool" };
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,114 @@
|
||||||
|
package com.ruoyi.common.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_UPLOAD = "upload";
|
||||||
|
|
||||||
|
/** 富文本控件 */
|
||||||
|
public static final String HTML_SUMMERNOTE = "summernote";
|
||||||
|
|
||||||
|
/** 字符串类型 */
|
||||||
|
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";
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
package com.ruoyi.common.constant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 权限通用常量
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
public class PermissionConstants
|
||||||
|
{
|
||||||
|
/** 新增权限 */
|
||||||
|
public static final String ADD_PERMISSION = "add";
|
||||||
|
|
||||||
|
/** 修改权限 */
|
||||||
|
public static final String EDIT_PERMISSION = "edit";
|
||||||
|
|
||||||
|
/** 删除权限 */
|
||||||
|
public static final String REMOVE_PERMISSION = "remove";
|
||||||
|
|
||||||
|
/** 导出权限 */
|
||||||
|
public static final String EXPORT_PERMISSION = "export";
|
||||||
|
|
||||||
|
/** 显示权限 */
|
||||||
|
public static final String VIEW_PERMISSION = "view";
|
||||||
|
|
||||||
|
/** 查询权限 */
|
||||||
|
public static final String LIST_PERMISSION = "list";
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
package com.ruoyi.common.constant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务调度通用常量
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
public class ScheduleConstants
|
||||||
|
{
|
||||||
|
public static final String TASK_CLASS_NAME = "TASK_CLASS_NAME";
|
||||||
|
|
||||||
|
/** 执行目标key */
|
||||||
|
public static final String TASK_PROPERTIES = "TASK_PROPERTIES";
|
||||||
|
|
||||||
|
/** 默认 */
|
||||||
|
public static final String MISFIRE_DEFAULT = "0";
|
||||||
|
|
||||||
|
/** 立即触发执行 */
|
||||||
|
public static final String MISFIRE_IGNORE_MISFIRES = "1";
|
||||||
|
|
||||||
|
/** 触发一次执行 */
|
||||||
|
public static final String MISFIRE_FIRE_AND_PROCEED = "2";
|
||||||
|
|
||||||
|
/** 不触发立即执行 */
|
||||||
|
public static final String MISFIRE_DO_NOTHING = "3";
|
||||||
|
|
||||||
|
public enum Status
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 正常
|
||||||
|
*/
|
||||||
|
NORMAL("0"),
|
||||||
|
/**
|
||||||
|
* 暂停
|
||||||
|
*/
|
||||||
|
PAUSE("1");
|
||||||
|
|
||||||
|
private String value;
|
||||||
|
|
||||||
|
private Status(String value)
|
||||||
|
{
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getValue()
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,79 @@
|
||||||
|
package com.ruoyi.common.constant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shiro通用常量
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
public class ShiroConstants
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 当前登录的用户
|
||||||
|
*/
|
||||||
|
public static final String CURRENT_USER = "currentUser";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户名字段
|
||||||
|
*/
|
||||||
|
public static final String CURRENT_USERNAME = "username";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 锁定屏幕字段
|
||||||
|
*/
|
||||||
|
public static final String LOCK_SCREEN = "lockscreen";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 消息key
|
||||||
|
*/
|
||||||
|
public static final String MESSAGE = "message";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 错误key
|
||||||
|
*/
|
||||||
|
public static final String ERROR = "errorMsg";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 编码格式
|
||||||
|
*/
|
||||||
|
public static final String ENCODING = "UTF-8";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当前在线会话
|
||||||
|
*/
|
||||||
|
public static final String ONLINE_SESSION = "online_session";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证码key
|
||||||
|
*/
|
||||||
|
public static final String CURRENT_CAPTCHA = "captcha";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证码开关
|
||||||
|
*/
|
||||||
|
public static final String CURRENT_ENABLED = "captchaEnabled";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证码类型
|
||||||
|
*/
|
||||||
|
public static final String CURRENT_TYPE = "captchaType";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证码
|
||||||
|
*/
|
||||||
|
public static final String CURRENT_VALIDATECODE = "validateCode";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证码错误
|
||||||
|
*/
|
||||||
|
public static final String CAPTCHA_ERROR = "captchaError";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录记录缓存
|
||||||
|
*/
|
||||||
|
public static final String LOGIN_RECORD_CACHE = "loginRecordCache";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统活跃用户缓存
|
||||||
|
*/
|
||||||
|
public static final String SYS_USERCACHE = "sys-userCache";
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,65 @@
|
||||||
|
package com.ruoyi.common.constant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户常量信息
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
public class UserConstants
|
||||||
|
{
|
||||||
|
/** 正常状态 */
|
||||||
|
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 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 String SYSTEM_USER_TYPE = "00";
|
||||||
|
public static final String REGISTER_USER_TYPE = "01";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 手机号码格式限制
|
||||||
|
*/
|
||||||
|
public static final String MOBILE_PHONE_NUMBER_PATTERN = "^0{0,1}(13[0-9]|15[0-9]|14[0-9]|18[0-9])[0-9]{8}$";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 邮箱格式限制
|
||||||
|
*/
|
||||||
|
public static final String EMAIL_PATTERN = "^((([a-z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])+(\\.([a-z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])+)*)|((\\x22)((((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(([\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(\\\\([\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF]))))*(((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(\\x22)))@((([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])*([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])))\\.)+(([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])*([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])))\\.?";
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
package com.ruoyi.common.exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 演示模式异常
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
public class DemoModeException extends RuntimeException
|
||||||
|
{
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
public DemoModeException()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
package com.ruoyi.common.exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 全局异常
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
public class GlobalException extends RuntimeException
|
||||||
|
{
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 错误提示
|
||||||
|
*/
|
||||||
|
private String message;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 错误明细,内部调试错误
|
||||||
|
*
|
||||||
|
* 和 {@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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
package com.ruoyi.common.exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 业务异常
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
public final class ServiceException extends RuntimeException
|
||||||
|
{
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 错误提示
|
||||||
|
*/
|
||||||
|
private String message;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 错误明细,内部调试错误
|
||||||
|
*
|
||||||
|
* 和 {@link CommonResult#getDetailMessage()} 一致的设计
|
||||||
|
*/
|
||||||
|
private String detailMessage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 空构造方法,避免反序列化问题
|
||||||
|
*/
|
||||||
|
public ServiceException()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServiceException(String message)
|
||||||
|
{
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDetailMessage()
|
||||||
|
{
|
||||||
|
return detailMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServiceException setDetailMessage(String detailMessage)
|
||||||
|
{
|
||||||
|
this.detailMessage = detailMessage;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMessage()
|
||||||
|
{
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServiceException setMessage(String message)
|
||||||
|
{
|
||||||
|
this.message = message;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
package com.ruoyi.common.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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,103 @@
|
||||||
|
package com.ruoyi.common.exception.base;
|
||||||
|
|
||||||
|
import com.ruoyi.common.utils.MessageUtils;
|
||||||
|
import com.ruoyi.common.utils.StringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 基础异常
|
||||||
|
*
|
||||||
|
* @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);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMessage()
|
||||||
|
{
|
||||||
|
String message = null;
|
||||||
|
if (!StringUtils.isEmpty(code))
|
||||||
|
{
|
||||||
|
message = MessageUtils.message(code, args);
|
||||||
|
}
|
||||||
|
if (message == null)
|
||||||
|
{
|
||||||
|
message = defaultMessage;
|
||||||
|
}
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getModule()
|
||||||
|
{
|
||||||
|
return module;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCode()
|
||||||
|
{
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object[] getArgs()
|
||||||
|
{
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDefaultMessage()
|
||||||
|
{
|
||||||
|
return defaultMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return this.getClass() + "{" + "module='" + module + '\'' + ", message='" + getMessage() + '\'' + '}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
package com.ruoyi.common.exception.file;
|
||||||
|
|
||||||
|
import com.ruoyi.common.exception.base.BaseException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件信息异常类
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
public class FileException extends BaseException
|
||||||
|
{
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
public FileException(String code, Object[] args)
|
||||||
|
{
|
||||||
|
super("file", code, args, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
package com.ruoyi.common.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 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
package com.ruoyi.common.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 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
package com.ruoyi.common.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,80 @@
|
||||||
|
package com.ruoyi.common.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 + "]后缀[" + 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
package com.ruoyi.common.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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
package com.ruoyi.common.exception.user;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 黑名单IP异常类
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
public class BlackListException extends UserException
|
||||||
|
{
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
public BlackListException()
|
||||||
|
{
|
||||||
|
super("login.blocked", null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
package com.ruoyi.common.exception.user;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证码错误异常类
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
public class CaptchaException extends UserException
|
||||||
|
{
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
public CaptchaException()
|
||||||
|
{
|
||||||
|
super("user.jcaptcha.error", null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
package com.ruoyi.common.exception.user;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 角色锁定异常类
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
public class RoleBlockedException extends UserException
|
||||||
|
{
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
public RoleBlockedException()
|
||||||
|
{
|
||||||
|
super("role.blocked", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
package com.ruoyi.common.exception.user;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户锁定异常类
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
public class UserBlockedException extends UserException
|
||||||
|
{
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
public UserBlockedException()
|
||||||
|
{
|
||||||
|
super("user.blocked", null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
package com.ruoyi.common.exception.user;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户账号已被删除
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
public class UserDeleteException extends UserException
|
||||||
|
{
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
public UserDeleteException()
|
||||||
|
{
|
||||||
|
super("user.password.delete", null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
package com.ruoyi.common.exception.user;
|
||||||
|
|
||||||
|
import com.ruoyi.common.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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
package com.ruoyi.common.exception.user;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户不存在异常类
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
public class UserNotExistsException extends UserException
|
||||||
|
{
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
public UserNotExistsException()
|
||||||
|
{
|
||||||
|
super("user.not.exists", null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
package com.ruoyi.common.exception.user;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户密码不正确或不符合规范异常类
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
public class UserPasswordNotMatchException extends UserException
|
||||||
|
{
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
public UserPasswordNotMatchException()
|
||||||
|
{
|
||||||
|
super("user.password.not.match", null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
package com.ruoyi.common.exception.user;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户错误记数异常类
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
public class UserPasswordRetryLimitCountException extends UserException
|
||||||
|
{
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
public UserPasswordRetryLimitCountException(int retryLimitCount)
|
||||||
|
{
|
||||||
|
super("user.password.retry.limit.count", new Object[] { retryLimitCount });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
package com.ruoyi.common.exception.user;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户错误最大次数异常类
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
public class UserPasswordRetryLimitExceedException extends UserException
|
||||||
|
{
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
public UserPasswordRetryLimitExceedException(int retryLimitCount)
|
||||||
|
{
|
||||||
|
super("user.password.retry.limit.exceed", new Object[] { retryLimitCount });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
package com.ruoyi.common.utils;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import com.ruoyi.common.constant.Constants;
|
||||||
|
import com.ruoyi.common.utils.http.HttpUtils;
|
||||||
|
import com.ruoyi.framework.config.RuoYiConfig;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取地址类
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
public class AddressUtils
|
||||||
|
{
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(AddressUtils.class);
|
||||||
|
|
||||||
|
// IP地址查询
|
||||||
|
public static final String IP_URL = "http://whois.pconline.com.cn/ipJson.jsp";
|
||||||
|
|
||||||
|
// 未知地址
|
||||||
|
public static final String UNKNOWN = "XX XX";
|
||||||
|
|
||||||
|
public static String getRealAddressByIP(String ip)
|
||||||
|
{
|
||||||
|
// 内网不查询
|
||||||
|
if (IpUtils.internalIp(ip))
|
||||||
|
{
|
||||||
|
return "内网IP";
|
||||||
|
}
|
||||||
|
if (RuoYiConfig.isAddressEnabled())
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
String rspStr = HttpUtils.sendGet(IP_URL, "ip=" + ip + "&json=true", Constants.GBK);
|
||||||
|
if (StringUtils.isEmpty(rspStr))
|
||||||
|
{
|
||||||
|
log.error("获取地理位置异常 {}", ip);
|
||||||
|
return UNKNOWN;
|
||||||
|
}
|
||||||
|
JSONObject obj = JSONObject.parseObject(rspStr);
|
||||||
|
String region = obj.getString("pro");
|
||||||
|
String city = obj.getString("city");
|
||||||
|
return String.format("%s %s", region, city);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
log.error("获取地理位置异常 {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return UNKNOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,114 @@
|
||||||
|
package com.ruoyi.common.utils;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.math.RoundingMode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 精确的浮点数运算
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
public class Arith
|
||||||
|
{
|
||||||
|
|
||||||
|
/** 默认除法运算精度 */
|
||||||
|
private static final int DEF_DIV_SCALE = 10;
|
||||||
|
|
||||||
|
/** 这个类不能实例化 */
|
||||||
|
private Arith()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提供精确的加法运算。
|
||||||
|
* @param v1 被加数
|
||||||
|
* @param v2 加数
|
||||||
|
* @return 两个参数的和
|
||||||
|
*/
|
||||||
|
public static double add(double v1, double v2)
|
||||||
|
{
|
||||||
|
BigDecimal b1 = new BigDecimal(Double.toString(v1));
|
||||||
|
BigDecimal b2 = new BigDecimal(Double.toString(v2));
|
||||||
|
return b1.add(b2).doubleValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提供精确的减法运算。
|
||||||
|
* @param v1 被减数
|
||||||
|
* @param v2 减数
|
||||||
|
* @return 两个参数的差
|
||||||
|
*/
|
||||||
|
public static double sub(double v1, double v2)
|
||||||
|
{
|
||||||
|
BigDecimal b1 = new BigDecimal(Double.toString(v1));
|
||||||
|
BigDecimal b2 = new BigDecimal(Double.toString(v2));
|
||||||
|
return b1.subtract(b2).doubleValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提供精确的乘法运算。
|
||||||
|
* @param v1 被乘数
|
||||||
|
* @param v2 乘数
|
||||||
|
* @return 两个参数的积
|
||||||
|
*/
|
||||||
|
public static double mul(double v1, double v2)
|
||||||
|
{
|
||||||
|
BigDecimal b1 = new BigDecimal(Double.toString(v1));
|
||||||
|
BigDecimal b2 = new BigDecimal(Double.toString(v2));
|
||||||
|
return b1.multiply(b2).doubleValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到
|
||||||
|
* 小数点以后10位,以后的数字四舍五入。
|
||||||
|
* @param v1 被除数
|
||||||
|
* @param v2 除数
|
||||||
|
* @return 两个参数的商
|
||||||
|
*/
|
||||||
|
public static double div(double v1, double v2)
|
||||||
|
{
|
||||||
|
return div(v1, v2, DEF_DIV_SCALE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指
|
||||||
|
* 定精度,以后的数字四舍五入。
|
||||||
|
* @param v1 被除数
|
||||||
|
* @param v2 除数
|
||||||
|
* @param scale 表示表示需要精确到小数点以后几位。
|
||||||
|
* @return 两个参数的商
|
||||||
|
*/
|
||||||
|
public static double div(double v1, double v2, int scale)
|
||||||
|
{
|
||||||
|
if (scale < 0)
|
||||||
|
{
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"The scale must be a positive integer or zero");
|
||||||
|
}
|
||||||
|
BigDecimal b1 = new BigDecimal(Double.toString(v1));
|
||||||
|
BigDecimal b2 = new BigDecimal(Double.toString(v2));
|
||||||
|
if (b1.compareTo(BigDecimal.ZERO) == 0)
|
||||||
|
{
|
||||||
|
return BigDecimal.ZERO.doubleValue();
|
||||||
|
}
|
||||||
|
return b1.divide(b2, scale, RoundingMode.HALF_UP).doubleValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提供精确的小数位四舍五入处理。
|
||||||
|
* @param v 需要四舍五入的数字
|
||||||
|
* @param scale 小数点后保留几位
|
||||||
|
* @return 四舍五入后的结果
|
||||||
|
*/
|
||||||
|
public static double round(double v, int scale)
|
||||||
|
{
|
||||||
|
if (scale < 0)
|
||||||
|
{
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"The scale must be a positive integer or zero");
|
||||||
|
}
|
||||||
|
BigDecimal b = new BigDecimal(Double.toString(v));
|
||||||
|
BigDecimal one = BigDecimal.ONE;
|
||||||
|
return b.divide(one, scale, RoundingMode.HALF_UP).doubleValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,197 @@
|
||||||
|
package com.ruoyi.common.utils;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Set;
|
||||||
|
import org.apache.shiro.cache.Cache;
|
||||||
|
import org.apache.shiro.cache.CacheManager;
|
||||||
|
import org.apache.shiro.cache.ehcache.EhCacheManager;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import com.ruoyi.common.utils.spring.SpringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cache工具类
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
public class CacheUtils
|
||||||
|
{
|
||||||
|
private static Logger logger = LoggerFactory.getLogger(CacheUtils.class);
|
||||||
|
|
||||||
|
private static CacheManager cacheManager = SpringUtils.getBean(CacheManager.class);
|
||||||
|
|
||||||
|
private static final String SYS_CACHE = "sys-cache";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取SYS_CACHE缓存
|
||||||
|
*
|
||||||
|
* @param key
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static Object get(String key)
|
||||||
|
{
|
||||||
|
return get(SYS_CACHE, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取SYS_CACHE缓存
|
||||||
|
*
|
||||||
|
* @param key
|
||||||
|
* @param defaultValue
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static Object get(String key, Object defaultValue)
|
||||||
|
{
|
||||||
|
Object value = get(key);
|
||||||
|
return value != null ? value : defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 写入SYS_CACHE缓存
|
||||||
|
*
|
||||||
|
* @param key
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static void put(String key, Object value)
|
||||||
|
{
|
||||||
|
put(SYS_CACHE, key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从SYS_CACHE缓存中移除
|
||||||
|
*
|
||||||
|
* @param key
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static void remove(String key)
|
||||||
|
{
|
||||||
|
remove(SYS_CACHE, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取缓存
|
||||||
|
*
|
||||||
|
* @param cacheName
|
||||||
|
* @param key
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static Object get(String cacheName, String key)
|
||||||
|
{
|
||||||
|
return getCache(cacheName).get(getKey(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取缓存
|
||||||
|
*
|
||||||
|
* @param cacheName
|
||||||
|
* @param key
|
||||||
|
* @param defaultValue
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static Object get(String cacheName, String key, Object defaultValue)
|
||||||
|
{
|
||||||
|
Object value = get(cacheName, getKey(key));
|
||||||
|
return value != null ? value : defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 写入缓存
|
||||||
|
*
|
||||||
|
* @param cacheName
|
||||||
|
* @param key
|
||||||
|
* @param value
|
||||||
|
*/
|
||||||
|
public static void put(String cacheName, String key, Object value)
|
||||||
|
{
|
||||||
|
getCache(cacheName).put(getKey(key), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从缓存中移除
|
||||||
|
*
|
||||||
|
* @param cacheName
|
||||||
|
* @param key
|
||||||
|
*/
|
||||||
|
public static void remove(String cacheName, String key)
|
||||||
|
{
|
||||||
|
getCache(cacheName).remove(getKey(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从缓存中移除所有
|
||||||
|
*
|
||||||
|
* @param cacheName
|
||||||
|
*/
|
||||||
|
public static void removeAll(String cacheName)
|
||||||
|
{
|
||||||
|
Cache<String, Object> cache = getCache(cacheName);
|
||||||
|
Set<String> keys = cache.keys();
|
||||||
|
for (Iterator<String> it = keys.iterator(); it.hasNext();)
|
||||||
|
{
|
||||||
|
cache.remove(it.next());
|
||||||
|
}
|
||||||
|
logger.info("清理缓存: {} => {}", cacheName, keys);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从缓存中移除指定key
|
||||||
|
*
|
||||||
|
* @param keys
|
||||||
|
*/
|
||||||
|
public static void removeByKeys(Set<String> keys)
|
||||||
|
{
|
||||||
|
removeByKeys(SYS_CACHE, keys);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从缓存中移除指定key
|
||||||
|
*
|
||||||
|
* @param cacheName
|
||||||
|
* @param keys
|
||||||
|
*/
|
||||||
|
public static void removeByKeys(String cacheName, Set<String> keys)
|
||||||
|
{
|
||||||
|
for (Iterator<String> it = keys.iterator(); it.hasNext();)
|
||||||
|
{
|
||||||
|
remove(it.next());
|
||||||
|
}
|
||||||
|
logger.info("清理缓存: {} => {}", cacheName, keys);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取缓存键名
|
||||||
|
*
|
||||||
|
* @param key
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private static String getKey(String key)
|
||||||
|
{
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得一个Cache,没有则显示日志。
|
||||||
|
*
|
||||||
|
* @param cacheName
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static Cache<String, Object> getCache(String cacheName)
|
||||||
|
{
|
||||||
|
Cache<String, Object> cache = cacheManager.getCache(cacheName);
|
||||||
|
if (cache == null)
|
||||||
|
{
|
||||||
|
throw new RuntimeException("当前系统中没有定义“" + cacheName + "”这个缓存。");
|
||||||
|
}
|
||||||
|
return cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有缓存
|
||||||
|
*
|
||||||
|
* @return 缓存组
|
||||||
|
*/
|
||||||
|
public static String[] getCacheNames()
|
||||||
|
{
|
||||||
|
return ((EhCacheManager) cacheManager).getCacheManager().getCacheNames();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,138 @@
|
||||||
|
package com.ruoyi.common.utils;
|
||||||
|
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.net.URLDecoder;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import javax.servlet.http.Cookie;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cookie工具类
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
public class CookieUtils
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 设置 Cookie(生成时间为1天)
|
||||||
|
*
|
||||||
|
* @param name 名称
|
||||||
|
* @param value 值
|
||||||
|
*/
|
||||||
|
public static void setCookie(HttpServletResponse response, String name, String value)
|
||||||
|
{
|
||||||
|
setCookie(response, name, value, 60 * 60 * 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置 Cookie
|
||||||
|
*
|
||||||
|
* @param name 名称
|
||||||
|
* @param value 值
|
||||||
|
* @param maxAge 生存时间(单位秒)
|
||||||
|
* @param uri 路径
|
||||||
|
*/
|
||||||
|
public static void setCookie(HttpServletResponse response, String name, String value, String path)
|
||||||
|
{
|
||||||
|
setCookie(response, name, value, path, 60 * 60 * 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置 Cookie
|
||||||
|
*
|
||||||
|
* @param name 名称
|
||||||
|
* @param value 值
|
||||||
|
* @param maxAge 生存时间(单位秒)
|
||||||
|
* @param uri 路径
|
||||||
|
*/
|
||||||
|
public static void setCookie(HttpServletResponse response, String name, String value, int maxAge)
|
||||||
|
{
|
||||||
|
setCookie(response, name, value, "/", maxAge);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置 Cookie
|
||||||
|
*
|
||||||
|
* @param name 名称
|
||||||
|
* @param value 值
|
||||||
|
* @param maxAge 生存时间(单位秒)
|
||||||
|
* @param uri 路径
|
||||||
|
*/
|
||||||
|
public static void setCookie(HttpServletResponse response, String name, String value, String path, int maxAge)
|
||||||
|
{
|
||||||
|
Cookie cookie = new Cookie(name, null);
|
||||||
|
cookie.setPath(path);
|
||||||
|
cookie.setMaxAge(maxAge);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
cookie.setValue(URLEncoder.encode(value, "utf-8"));
|
||||||
|
}
|
||||||
|
catch (UnsupportedEncodingException e)
|
||||||
|
{
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
response.addCookie(cookie);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得指定Cookie的值
|
||||||
|
*
|
||||||
|
* @param name 名称
|
||||||
|
* @return 值
|
||||||
|
*/
|
||||||
|
public static String getCookie(HttpServletRequest request, String name)
|
||||||
|
{
|
||||||
|
return getCookie(request, null, name, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得指定Cookie的值,并删除。
|
||||||
|
*
|
||||||
|
* @param name 名称
|
||||||
|
* @return 值
|
||||||
|
*/
|
||||||
|
public static String getCookie(HttpServletRequest request, HttpServletResponse response, String name)
|
||||||
|
{
|
||||||
|
return getCookie(request, response, name, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得指定Cookie的值
|
||||||
|
*
|
||||||
|
* @param request 请求对象
|
||||||
|
* @param response 响应对象
|
||||||
|
* @param name 名字
|
||||||
|
* @param isRemove 是否移除
|
||||||
|
* @return 值
|
||||||
|
*/
|
||||||
|
public static String getCookie(HttpServletRequest request, HttpServletResponse response, String name,
|
||||||
|
boolean isRemove)
|
||||||
|
{
|
||||||
|
String value = null;
|
||||||
|
Cookie[] cookies = request.getCookies();
|
||||||
|
if (cookies != null)
|
||||||
|
{
|
||||||
|
for (Cookie cookie : cookies)
|
||||||
|
{
|
||||||
|
if (cookie.getName().equals(name))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
value = URLDecoder.decode(cookie.getValue(), "utf-8");
|
||||||
|
}
|
||||||
|
catch (UnsupportedEncodingException e)
|
||||||
|
{
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
if (isRemove)
|
||||||
|
{
|
||||||
|
cookie.setMaxAge(0);
|
||||||
|
response.addCookie(cookie);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,191 @@
|
||||||
|
package com.ruoyi.common.utils;
|
||||||
|
|
||||||
|
import java.lang.management.ManagementFactory;
|
||||||
|
import java.text.ParseException;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.LocalTime;
|
||||||
|
import java.time.ZoneId;
|
||||||
|
import java.time.ZonedDateTime;
|
||||||
|
import java.util.Date;
|
||||||
|
import org.apache.commons.lang3.time.DateFormatUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 时间工具类
|
||||||
|
*
|
||||||
|
* @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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算相差天数
|
||||||
|
*/
|
||||||
|
public static int differentDaysByMillisecond(Date date1, Date date2)
|
||||||
|
{
|
||||||
|
return Math.abs((int) ((date2.getTime() - date1.getTime()) / (1000 * 3600 * 24)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算时间差
|
||||||
|
*
|
||||||
|
* @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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
package com.ruoyi.common.utils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 脱敏工具类
|
||||||
|
*
|
||||||
|
* @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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
package com.ruoyi.common.utils;
|
||||||
|
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.io.StringWriter;
|
||||||
|
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||||
|
import com.ruoyi.common.utils.StringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 错误信息处理类。
|
||||||
|
*
|
||||||
|
* @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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,370 @@
|
||||||
|
package com.ruoyi.common.utils;
|
||||||
|
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.UnknownHostException;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取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
|
||||||
|
*
|
||||||
|
* @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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,134 @@
|
||||||
|
package com.ruoyi.common.utils;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSON;
|
||||||
|
import org.apache.shiro.SecurityUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.io.StringWriter;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理并记录日志文件
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
public class LogUtils
|
||||||
|
{
|
||||||
|
public static final Logger ERROR_LOG = LoggerFactory.getLogger("sys-error");
|
||||||
|
public static final Logger ACCESS_LOG = LoggerFactory.getLogger("sys-access");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 记录访问日志 [username][jsessionid][ip][accept][UserAgent][url][params][Referer]
|
||||||
|
*
|
||||||
|
* @param request
|
||||||
|
*/
|
||||||
|
public static void logAccess(HttpServletRequest request)
|
||||||
|
{
|
||||||
|
String username = getUsername();
|
||||||
|
String jsessionId = request.getRequestedSessionId();
|
||||||
|
String ip = IpUtils.getIpAddr(request);
|
||||||
|
String accept = request.getHeader("accept");
|
||||||
|
String userAgent = request.getHeader("User-Agent");
|
||||||
|
String url = request.getRequestURI();
|
||||||
|
String params = getParams(request);
|
||||||
|
|
||||||
|
StringBuilder s = new StringBuilder();
|
||||||
|
s.append(getBlock(username));
|
||||||
|
s.append(getBlock(jsessionId));
|
||||||
|
s.append(getBlock(ip));
|
||||||
|
s.append(getBlock(accept));
|
||||||
|
s.append(getBlock(userAgent));
|
||||||
|
s.append(getBlock(url));
|
||||||
|
s.append(getBlock(params));
|
||||||
|
s.append(getBlock(request.getHeader("Referer")));
|
||||||
|
getAccessLog().info(s.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 记录异常错误 格式 [exception]
|
||||||
|
*
|
||||||
|
* @param message
|
||||||
|
* @param e
|
||||||
|
*/
|
||||||
|
public static void logError(String message, Throwable e)
|
||||||
|
{
|
||||||
|
String username = getUsername();
|
||||||
|
StringBuilder s = new StringBuilder();
|
||||||
|
s.append(getBlock("exception"));
|
||||||
|
s.append(getBlock(username));
|
||||||
|
s.append(getBlock(message));
|
||||||
|
ERROR_LOG.error(s.toString(), e);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 记录页面错误 错误日志记录 [page/eception][username][statusCode][errorMessage][servletName][uri][exceptionName][ip][exception]
|
||||||
|
*
|
||||||
|
* @param request
|
||||||
|
*/
|
||||||
|
public static void logPageError(HttpServletRequest request)
|
||||||
|
{
|
||||||
|
String username = getUsername();
|
||||||
|
|
||||||
|
Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
|
||||||
|
String message = (String) request.getAttribute("javax.servlet.error.message");
|
||||||
|
String uri = (String) request.getAttribute("javax.servlet.error.request_uri");
|
||||||
|
Throwable t = (Throwable) request.getAttribute("javax.servlet.error.exception");
|
||||||
|
|
||||||
|
if (statusCode == null)
|
||||||
|
{
|
||||||
|
statusCode = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder s = new StringBuilder();
|
||||||
|
s.append(getBlock(t == null ? "page" : "exception"));
|
||||||
|
s.append(getBlock(username));
|
||||||
|
s.append(getBlock(statusCode));
|
||||||
|
s.append(getBlock(message));
|
||||||
|
s.append(getBlock(IpUtils.getIpAddr(request)));
|
||||||
|
|
||||||
|
s.append(getBlock(uri));
|
||||||
|
s.append(getBlock(request.getHeader("Referer")));
|
||||||
|
StringWriter sw = new StringWriter();
|
||||||
|
|
||||||
|
while (t != null)
|
||||||
|
{
|
||||||
|
t.printStackTrace(new PrintWriter(sw));
|
||||||
|
t = t.getCause();
|
||||||
|
}
|
||||||
|
s.append(getBlock(sw.toString()));
|
||||||
|
getErrorLog().error(s.toString());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getBlock(Object msg)
|
||||||
|
{
|
||||||
|
if (msg == null)
|
||||||
|
{
|
||||||
|
msg = "";
|
||||||
|
}
|
||||||
|
return "[" + msg.toString() + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static String getParams(HttpServletRequest request)
|
||||||
|
{
|
||||||
|
Map<String, String[]> params = request.getParameterMap();
|
||||||
|
return JSON.toJSONString(params);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static String getUsername()
|
||||||
|
{
|
||||||
|
return (String) SecurityUtils.getSubject().getPrincipal();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Logger getAccessLog()
|
||||||
|
{
|
||||||
|
return ACCESS_LOG;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Logger getErrorLog()
|
||||||
|
{
|
||||||
|
return ERROR_LOG;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
package com.ruoyi.common.utils;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map通用处理方法
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
public class MapDataUtil
|
||||||
|
{
|
||||||
|
public static Map<String, Object> convertDataMap(HttpServletRequest request)
|
||||||
|
{
|
||||||
|
Map<String, String[]> properties = request.getParameterMap();
|
||||||
|
Map<String, Object> returnMap = new HashMap<String, Object>();
|
||||||
|
Iterator<?> entries = properties.entrySet().iterator();
|
||||||
|
Map.Entry<?, ?> entry;
|
||||||
|
String name = "";
|
||||||
|
String value = "";
|
||||||
|
while (entries.hasNext())
|
||||||
|
{
|
||||||
|
entry = (Entry<?, ?>) entries.next();
|
||||||
|
name = (String) entry.getKey();
|
||||||
|
Object valueObj = entry.getValue();
|
||||||
|
if (null == valueObj)
|
||||||
|
{
|
||||||
|
value = "";
|
||||||
|
}
|
||||||
|
else if (valueObj instanceof String[])
|
||||||
|
{
|
||||||
|
String[] values = (String[]) valueObj;
|
||||||
|
value = "";
|
||||||
|
for (int i = 0; i < values.length; i++)
|
||||||
|
{
|
||||||
|
value += values[i] + ",";
|
||||||
|
}
|
||||||
|
if (value.length() > 0)
|
||||||
|
{
|
||||||
|
value = value.substring(0, value.length() - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
value = valueObj.toString();
|
||||||
|
}
|
||||||
|
returnMap.put(name, value);
|
||||||
|
}
|
||||||
|
return returnMap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,67 @@
|
||||||
|
package com.ruoyi.common.utils;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Md5加密方法
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
public class Md5Utils
|
||||||
|
{
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(Md5Utils.class);
|
||||||
|
|
||||||
|
private static byte[] md5(String s)
|
||||||
|
{
|
||||||
|
MessageDigest algorithm;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
algorithm = MessageDigest.getInstance("MD5");
|
||||||
|
algorithm.reset();
|
||||||
|
algorithm.update(s.getBytes("UTF-8"));
|
||||||
|
byte[] messageDigest = algorithm.digest();
|
||||||
|
return messageDigest;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
log.error("MD5 Error...", e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final String toHex(byte hash[])
|
||||||
|
{
|
||||||
|
if (hash == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
StringBuffer buf = new StringBuffer(hash.length * 2);
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < hash.length; i++)
|
||||||
|
{
|
||||||
|
if ((hash[i] & 0xff) < 0x10)
|
||||||
|
{
|
||||||
|
buf.append("0");
|
||||||
|
}
|
||||||
|
buf.append(Long.toString(hash[i] & 0xff, 16));
|
||||||
|
}
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String hash(String s)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return new String(toHex(md5(s)).getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
log.error("not supported charset...{}", e);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
package com.ruoyi.common.utils;
|
||||||
|
|
||||||
|
import org.springframework.context.MessageSource;
|
||||||
|
import org.springframework.context.i18n.LocaleContextHolder;
|
||||||
|
import com.ruoyi.common.utils.spring.SpringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取i18n资源文件
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
public class MessageUtils
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 根据消息键和参数 获取消息 委托给spring messageSource
|
||||||
|
*
|
||||||
|
* @param code 消息键
|
||||||
|
* @param args 参数
|
||||||
|
* @return 获取国际化翻译值
|
||||||
|
*/
|
||||||
|
public static String message(String code, Object... args)
|
||||||
|
{
|
||||||
|
MessageSource messageSource = SpringUtils.getBean(MessageSource.class);
|
||||||
|
return messageSource.getMessage(code, args, LocaleContextHolder.getLocale());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
package com.ruoyi.common.utils;
|
||||||
|
|
||||||
|
import com.github.pagehelper.PageHelper;
|
||||||
|
import com.ruoyi.common.utils.sql.SqlUtil;
|
||||||
|
import com.ruoyi.framework.web.page.PageDomain;
|
||||||
|
import com.ruoyi.framework.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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,216 @@
|
||||||
|
package com.ruoyi.common.utils;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.net.URLDecoder;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import javax.servlet.http.HttpSession;
|
||||||
|
import org.springframework.web.context.request.RequestAttributes;
|
||||||
|
import org.springframework.web.context.request.RequestContextHolder;
|
||||||
|
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||||
|
import com.ruoyi.common.constant.Constants;
|
||||||
|
import com.ruoyi.common.utils.text.Convert;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 客户端工具类
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
public class ServletUtils
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 定义移动端请求的所有可能类型
|
||||||
|
*/
|
||||||
|
private final static String[] agent = { "Android", "iPhone", "iPod", "iPad", "Windows Phone", "MQQBrowser" };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取request
|
||||||
|
*/
|
||||||
|
public static HttpServletRequest getRequest()
|
||||||
|
{
|
||||||
|
return getRequestAttributes().getRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取response
|
||||||
|
*/
|
||||||
|
public static HttpServletResponse getResponse()
|
||||||
|
{
|
||||||
|
return getRequestAttributes().getResponse();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取session
|
||||||
|
*/
|
||||||
|
public static HttpSession getSession()
|
||||||
|
{
|
||||||
|
return getRequest().getSession();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ServletRequestAttributes getRequestAttributes()
|
||||||
|
{
|
||||||
|
RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
|
||||||
|
return (ServletRequestAttributes) attributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将字符串渲染到客户端
|
||||||
|
*
|
||||||
|
* @param response 渲染对象
|
||||||
|
* @param string 待渲染的字符串
|
||||||
|
* @return null
|
||||||
|
*/
|
||||||
|
public static String renderString(HttpServletResponse response, String string)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
response.setContentType("application/json");
|
||||||
|
response.setCharacterEncoding("utf-8");
|
||||||
|
response.getWriter().print(string);
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否是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");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断User-Agent 是不是来自于手机
|
||||||
|
*/
|
||||||
|
public static boolean checkAgentIsMobile(String ua)
|
||||||
|
{
|
||||||
|
boolean flag = false;
|
||||||
|
if (!ua.contains("Windows NT") || (ua.contains("Windows NT") && ua.contains("compatible; MSIE 9.0;")))
|
||||||
|
{
|
||||||
|
// 排除 苹果桌面系统
|
||||||
|
if (!ua.contains("Windows NT") && !ua.contains("Macintosh"))
|
||||||
|
{
|
||||||
|
for (String item : agent)
|
||||||
|
{
|
||||||
|
if (ua.contains(item))
|
||||||
|
{
|
||||||
|
flag = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return flag;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 内容编码
|
||||||
|
*
|
||||||
|
* @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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,676 @@
|
||||||
|
package com.ruoyi.common.utils;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import org.springframework.util.AntPathMatcher;
|
||||||
|
import com.ruoyi.common.constant.Constants;
|
||||||
|
import com.ruoyi.common.utils.text.StrFormatter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字符串工具类
|
||||||
|
*
|
||||||
|
* @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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化文本, {} 表示占位符<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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字符串转set
|
||||||
|
*
|
||||||
|
* @param str 字符串
|
||||||
|
* @param sep 分隔符
|
||||||
|
* @return set集合
|
||||||
|
*/
|
||||||
|
public static final Set<String> str2Set(String str, String sep)
|
||||||
|
{
|
||||||
|
return new HashSet<String>(str2List(str, sep, true, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字符串转list
|
||||||
|
*
|
||||||
|
* @param str 字符串
|
||||||
|
* @param sep 分隔符
|
||||||
|
* @param filterBlank 过滤纯空白
|
||||||
|
* @param trim 去掉首尾空白
|
||||||
|
* @return list集合
|
||||||
|
*/
|
||||||
|
public static final List<String> str2List(String str, String sep, boolean filterBlank, boolean trim)
|
||||||
|
{
|
||||||
|
List<String> list = new ArrayList<String>();
|
||||||
|
if (StringUtils.isEmpty(str))
|
||||||
|
{
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 过滤空白字符串
|
||||||
|
if (filterBlank && StringUtils.isBlank(str))
|
||||||
|
{
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
String[] split = str.split(sep);
|
||||||
|
for (String string : split)
|
||||||
|
{
|
||||||
|
if (filterBlank && StringUtils.isBlank(string))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (trim)
|
||||||
|
{
|
||||||
|
string = string.trim();
|
||||||
|
}
|
||||||
|
list.add(string);
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断给定的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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查找指定字符串是否包含指定字符串列表中的任意一个字符串同时串忽略大小写
|
||||||
|
*
|
||||||
|
* @param cs 指定字符串
|
||||||
|
* @param searchCharSequences 需要检查的字符串数组
|
||||||
|
* @return 是否包含任意一个字符串
|
||||||
|
*/
|
||||||
|
public static boolean containsAnyIgnoreCase(CharSequence cs, CharSequence... searchCharSequences)
|
||||||
|
{
|
||||||
|
if (isEmpty(cs) || isEmpty(searchCharSequences))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (CharSequence testStr : searchCharSequences)
|
||||||
|
{
|
||||||
|
if (containsIgnoreCase(cs, testStr))
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除最后一个字符串
|
||||||
|
*
|
||||||
|
* @param str 输入字符串
|
||||||
|
* @param spit 以什么类型结尾的
|
||||||
|
* @return 截取后的字符串
|
||||||
|
*/
|
||||||
|
public static String lastStringDel(String str, String spit)
|
||||||
|
{
|
||||||
|
if (!StringUtils.isEmpty(str) && str.endsWith(spit))
|
||||||
|
{
|
||||||
|
return str.subSequence(0, str.length() - 1).toString();
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。 例如: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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,99 @@
|
||||||
|
package com.ruoyi.common.utils;
|
||||||
|
|
||||||
|
import java.util.concurrent.CancellationException;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 线程相关工具类.
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
public class Threads
|
||||||
|
{
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(Threads.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sleep等待,单位为毫秒
|
||||||
|
*/
|
||||||
|
public static void sleep(long milliseconds)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Thread.sleep(milliseconds);
|
||||||
|
}
|
||||||
|
catch (InterruptedException e)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 停止线程池
|
||||||
|
* 先使用shutdown, 停止接收新任务并尝试完成所有已存在任务.
|
||||||
|
* 如果超时, 则调用shutdownNow, 取消在workQueue中Pending的任务,并中断所有阻塞函数.
|
||||||
|
* 如果仍人超時,則強制退出.
|
||||||
|
* 另对在shutdown时线程本身被调用中断做了处理.
|
||||||
|
*/
|
||||||
|
public static void shutdownAndAwaitTermination(ExecutorService pool)
|
||||||
|
{
|
||||||
|
if (pool != null && !pool.isShutdown())
|
||||||
|
{
|
||||||
|
pool.shutdown();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!pool.awaitTermination(120, TimeUnit.SECONDS))
|
||||||
|
{
|
||||||
|
pool.shutdownNow();
|
||||||
|
if (!pool.awaitTermination(120, TimeUnit.SECONDS))
|
||||||
|
{
|
||||||
|
logger.info("Pool did not terminate");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (InterruptedException ie)
|
||||||
|
{
|
||||||
|
pool.shutdownNow();
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打印线程异常信息
|
||||||
|
*/
|
||||||
|
public static void printException(Runnable r, Throwable t)
|
||||||
|
{
|
||||||
|
if (t == null && r instanceof Future<?>)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Future<?> future = (Future<?>) r;
|
||||||
|
if (future.isDone())
|
||||||
|
{
|
||||||
|
future.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (CancellationException ce)
|
||||||
|
{
|
||||||
|
t = ce;
|
||||||
|
}
|
||||||
|
catch (ExecutionException ee)
|
||||||
|
{
|
||||||
|
t = ee.getCause();
|
||||||
|
}
|
||||||
|
catch (InterruptedException ie)
|
||||||
|
{
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (t != null)
|
||||||
|
{
|
||||||
|
logger.error(t.getMessage(), t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,137 @@
|
||||||
|
package com.ruoyi.common.utils;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import com.ruoyi.project.system.menu.domain.Menu;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 权限数据处理
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
public class TreeUtils
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 根据父节点的ID获取所有子节点
|
||||||
|
*
|
||||||
|
* @param list 分类表
|
||||||
|
* @param parentId 传入的父节点ID
|
||||||
|
* @return String
|
||||||
|
*/
|
||||||
|
public static List<Menu> getChildPerms(List<Menu> list, int parentId)
|
||||||
|
{
|
||||||
|
List<Menu> returnList = new ArrayList<Menu>();
|
||||||
|
for (Iterator<Menu> iterator = list.iterator(); iterator.hasNext();)
|
||||||
|
{
|
||||||
|
Menu t = (Menu) iterator.next();
|
||||||
|
// 一、根据传入的某个父节点ID,遍历该父节点的所有子节点
|
||||||
|
if (t.getParentId() == parentId)
|
||||||
|
{
|
||||||
|
recursionFn(list, t);
|
||||||
|
returnList.add(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return returnList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 递归列表
|
||||||
|
*
|
||||||
|
* @param list
|
||||||
|
* @param t
|
||||||
|
*/
|
||||||
|
private static void recursionFn(List<Menu> list, Menu t)
|
||||||
|
{
|
||||||
|
// 得到子节点列表
|
||||||
|
List<Menu> childList = getChildList(list, t);
|
||||||
|
t.setChildren(childList);
|
||||||
|
for (Menu tChild : childList)
|
||||||
|
{
|
||||||
|
if (hasChild(list, tChild))
|
||||||
|
{
|
||||||
|
recursionFn(list, tChild);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 得到子节点列表
|
||||||
|
*/
|
||||||
|
private static List<Menu> getChildList(List<Menu> list, Menu t)
|
||||||
|
{
|
||||||
|
|
||||||
|
List<Menu> tlist = new ArrayList<Menu>();
|
||||||
|
Iterator<Menu> it = list.iterator();
|
||||||
|
while (it.hasNext())
|
||||||
|
{
|
||||||
|
Menu n = (Menu) it.next();
|
||||||
|
if (n.getParentId().longValue() == t.getMenuId().longValue())
|
||||||
|
{
|
||||||
|
tlist.add(n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tlist;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Menu> returnList = new ArrayList<Menu>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据父节点的ID获取所有子节点
|
||||||
|
*
|
||||||
|
* @param list 分类表
|
||||||
|
* @param typeId 传入的父节点ID
|
||||||
|
* @param prefix 子节点前缀
|
||||||
|
*/
|
||||||
|
public List<Menu> getChildPerms(List<Menu> list, int typeId, String prefix)
|
||||||
|
{
|
||||||
|
if (list == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
for (Iterator<Menu> iterator = list.iterator(); iterator.hasNext();)
|
||||||
|
{
|
||||||
|
Menu node = (Menu) iterator.next();
|
||||||
|
// 一、根据传入的某个父节点ID,遍历该父节点的所有子节点
|
||||||
|
if (node.getParentId() == typeId)
|
||||||
|
{
|
||||||
|
recursionFn(list, node, prefix);
|
||||||
|
}
|
||||||
|
// 二、遍历所有的父节点下的所有子节点
|
||||||
|
/*
|
||||||
|
* if (node.getParentId()==0) { recursionFn(list, node); }
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
return returnList;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void recursionFn(List<Menu> list, Menu node, String p)
|
||||||
|
{
|
||||||
|
// 得到子节点列表
|
||||||
|
List<Menu> childList = getChildList(list, node);
|
||||||
|
if (hasChild(list, node))
|
||||||
|
{
|
||||||
|
// 判断是否有子节点
|
||||||
|
returnList.add(node);
|
||||||
|
Iterator<Menu> it = childList.iterator();
|
||||||
|
while (it.hasNext())
|
||||||
|
{
|
||||||
|
Menu n = (Menu) it.next();
|
||||||
|
n.setMenuName(p + n.getMenuName());
|
||||||
|
recursionFn(list, n, p + p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
returnList.add(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否有子节点
|
||||||
|
*/
|
||||||
|
private static boolean hasChild(List<Menu> list, Menu t)
|
||||||
|
{
|
||||||
|
return getChildList(list, t).size() > 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,110 @@
|
||||||
|
package com.ruoyi.common.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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
package com.ruoyi.common.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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,76 @@
|
||||||
|
package com.ruoyi.common.utils.file;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件类型工具类
|
||||||
|
*
|
||||||
|
* @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 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,233 @@
|
||||||
|
package com.ruoyi.common.utils.file;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.Objects;
|
||||||
|
import org.apache.commons.io.FilenameUtils;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
import com.ruoyi.common.constant.Constants;
|
||||||
|
import com.ruoyi.common.exception.file.FileNameLengthLimitExceededException;
|
||||||
|
import com.ruoyi.common.exception.file.FileSizeLimitExceededException;
|
||||||
|
import com.ruoyi.common.exception.file.InvalidExtensionException;
|
||||||
|
import com.ruoyi.common.utils.DateUtils;
|
||||||
|
import com.ruoyi.common.utils.StringUtils;
|
||||||
|
import com.ruoyi.common.utils.uuid.Seq;
|
||||||
|
import com.ruoyi.framework.config.RuoYiConfig;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件上传工具类
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
public class FileUploadUtils
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 默认大小 50M
|
||||||
|
*/
|
||||||
|
public static final long DEFAULT_MAX_SIZE = 50 * 1024 * 1024;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认的文件名最大长度 100
|
||||||
|
*/
|
||||||
|
public static final int DEFAULT_FILE_NAME_LENGTH = 100;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认上传的地址
|
||||||
|
*/
|
||||||
|
private static String defaultBaseDir = RuoYiConfig.getProfile();
|
||||||
|
|
||||||
|
public static void setDefaultBaseDir(String defaultBaseDir)
|
||||||
|
{
|
||||||
|
FileUploadUtils.defaultBaseDir = defaultBaseDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getDefaultBaseDir()
|
||||||
|
{
|
||||||
|
return defaultBaseDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 以默认配置进行文件上传
|
||||||
|
*
|
||||||
|
* @param file 上传的文件
|
||||||
|
* @return 文件名称
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public static final String upload(MultipartFile file) throws IOException
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return upload(getDefaultBaseDir(), file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
throw new IOException(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据文件路径上传
|
||||||
|
*
|
||||||
|
* @param baseDir 相对应用的基目录
|
||||||
|
* @param file 上传的文件
|
||||||
|
* @return 文件名称
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public static final String upload(String baseDir, MultipartFile file) throws IOException
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return upload(baseDir, file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
throw new IOException(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件上传
|
||||||
|
*
|
||||||
|
* @param baseDir 相对应用的基目录
|
||||||
|
* @param file 上传的文件
|
||||||
|
* @param allowedExtension 上传文件类型
|
||||||
|
* @return 返回上传成功的文件名
|
||||||
|
* @throws FileSizeLimitExceededException 如果超出最大大小
|
||||||
|
* @throws FileNameLengthLimitExceededException 文件名太长
|
||||||
|
* @throws IOException 比如读写文件出错时
|
||||||
|
* @throws InvalidExtensionException 文件校验异常
|
||||||
|
*/
|
||||||
|
public static final String upload(String baseDir, MultipartFile file, String[] allowedExtension)
|
||||||
|
throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException,
|
||||||
|
InvalidExtensionException
|
||||||
|
{
|
||||||
|
int fileNameLength = Objects.requireNonNull(file.getOriginalFilename()).length();
|
||||||
|
if (fileNameLength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH)
|
||||||
|
{
|
||||||
|
throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
assertAllowed(file, allowedExtension);
|
||||||
|
|
||||||
|
String fileName = extractFilename(file);
|
||||||
|
|
||||||
|
String absPath = getAbsoluteFile(baseDir, fileName).getAbsolutePath();
|
||||||
|
file.transferTo(Paths.get(absPath));
|
||||||
|
return getPathFileName(baseDir, fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 编码文件名
|
||||||
|
*/
|
||||||
|
public static final String extractFilename(MultipartFile file)
|
||||||
|
{
|
||||||
|
return StringUtils.format("{}/{}_{}.{}", DateUtils.datePath(),
|
||||||
|
FilenameUtils.getBaseName(file.getOriginalFilename()), Seq.getId(Seq.uploadSeqType), getExtension(file));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final File getAbsoluteFile(String uploadDir, String fileName) throws IOException
|
||||||
|
{
|
||||||
|
File desc = new File(uploadDir + File.separator + fileName);
|
||||||
|
|
||||||
|
if (!desc.exists())
|
||||||
|
{
|
||||||
|
if (!desc.getParentFile().exists())
|
||||||
|
{
|
||||||
|
desc.getParentFile().mkdirs();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final String getPathFileName(String uploadDir, String fileName) throws IOException
|
||||||
|
{
|
||||||
|
int dirLastIndex = RuoYiConfig.getProfile().length() + 1;
|
||||||
|
String currentDir = StringUtils.substring(uploadDir, dirLastIndex);
|
||||||
|
return Constants.RESOURCE_PREFIX + "/" + currentDir + "/" + fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件大小校验
|
||||||
|
*
|
||||||
|
* @param file 上传的文件
|
||||||
|
* @return
|
||||||
|
* @throws FileSizeLimitExceededException 如果超出最大大小
|
||||||
|
* @throws InvalidExtensionException
|
||||||
|
*/
|
||||||
|
public static final void assertAllowed(MultipartFile file, String[] allowedExtension)
|
||||||
|
throws FileSizeLimitExceededException, InvalidExtensionException
|
||||||
|
{
|
||||||
|
long size = file.getSize();
|
||||||
|
if (size > DEFAULT_MAX_SIZE)
|
||||||
|
{
|
||||||
|
throw new FileSizeLimitExceededException(DEFAULT_MAX_SIZE / 1024 / 1024);
|
||||||
|
}
|
||||||
|
|
||||||
|
String fileName = file.getOriginalFilename();
|
||||||
|
String extension = getExtension(file);
|
||||||
|
if (allowedExtension != null && !isAllowedExtension(extension, allowedExtension))
|
||||||
|
{
|
||||||
|
if (allowedExtension == MimeTypeUtils.IMAGE_EXTENSION)
|
||||||
|
{
|
||||||
|
throw new InvalidExtensionException.InvalidImageExtensionException(allowedExtension, extension,
|
||||||
|
fileName);
|
||||||
|
}
|
||||||
|
else if (allowedExtension == MimeTypeUtils.FLASH_EXTENSION)
|
||||||
|
{
|
||||||
|
throw new InvalidExtensionException.InvalidFlashExtensionException(allowedExtension, extension,
|
||||||
|
fileName);
|
||||||
|
}
|
||||||
|
else if (allowedExtension == MimeTypeUtils.MEDIA_EXTENSION)
|
||||||
|
{
|
||||||
|
throw new InvalidExtensionException.InvalidMediaExtensionException(allowedExtension, extension,
|
||||||
|
fileName);
|
||||||
|
}
|
||||||
|
else if (allowedExtension == MimeTypeUtils.VIDEO_EXTENSION)
|
||||||
|
{
|
||||||
|
throw new InvalidExtensionException.InvalidVideoExtensionException(allowedExtension, extension,
|
||||||
|
fileName);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new InvalidExtensionException(allowedExtension, extension, fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断MIME类型是否是允许的MIME类型
|
||||||
|
*
|
||||||
|
* @param extension
|
||||||
|
* @param allowedExtension
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static final boolean isAllowedExtension(String extension, String[] allowedExtension)
|
||||||
|
{
|
||||||
|
for (String str : allowedExtension)
|
||||||
|
{
|
||||||
|
if (str.equalsIgnoreCase(extension))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取文件名的后缀
|
||||||
|
*
|
||||||
|
* @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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,290 @@
|
||||||
|
package com.ruoyi.common.utils.file;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
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.io.FilenameUtils;
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.commons.lang3.ArrayUtils;
|
||||||
|
import com.ruoyi.common.utils.DateUtils;
|
||||||
|
import com.ruoyi.common.utils.StringUtils;
|
||||||
|
import com.ruoyi.common.utils.uuid.IdUtils;
|
||||||
|
import com.ruoyi.framework.config.RuoYiConfig;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件处理工具类
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
public class FileUtils
|
||||||
|
{
|
||||||
|
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
|
||||||
|
{
|
||||||
|
IOUtils.close(os);
|
||||||
|
IOUtils.close(fis);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 写数据到文件中
|
||||||
|
*
|
||||||
|
* @param data 数据
|
||||||
|
* @return 目标文件
|
||||||
|
* @throws IOException IO异常
|
||||||
|
*/
|
||||||
|
public static String writeImportBytes(byte[] data) throws IOException
|
||||||
|
{
|
||||||
|
return writeBytes(data, RuoYiConfig.getImportPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 写数据到文件中
|
||||||
|
*
|
||||||
|
* @param data 数据
|
||||||
|
* @param uploadDir 目标文件
|
||||||
|
* @return 目标文件
|
||||||
|
* @throws IOException IO异常
|
||||||
|
*/
|
||||||
|
public static String writeBytes(byte[] data, String uploadDir) throws IOException
|
||||||
|
{
|
||||||
|
FileOutputStream fos = null;
|
||||||
|
String pathName = "";
|
||||||
|
try
|
||||||
|
{
|
||||||
|
String extension = getFileExtendName(data);
|
||||||
|
pathName = DateUtils.datePath() + "/" + IdUtils.fastUUID() + "." + extension;
|
||||||
|
File file = FileUploadUtils.getAbsoluteFile(uploadDir, pathName);
|
||||||
|
fos = new FileOutputStream(file);
|
||||||
|
fos.write(data);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
IOUtils.close(fos);
|
||||||
|
}
|
||||||
|
return FileUploadUtils.getPathFileName(uploadDir, pathName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除文件
|
||||||
|
*
|
||||||
|
* @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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查允许下载的文件规则
|
||||||
|
if (ArrayUtils.contains(MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION, FileTypeUtils.getFileType(resource)))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 不在允许下载的文件规则
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载文件名重新编码
|
||||||
|
*
|
||||||
|
* @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 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());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 百分号编码工具方法
|
||||||
|
*
|
||||||
|
* @param s 需要百分号编码的字符串
|
||||||
|
* @return 百分号编码后的字符串
|
||||||
|
*/
|
||||||
|
public static String percentEncode(String s) throws UnsupportedEncodingException
|
||||||
|
{
|
||||||
|
String encode = URLEncoder.encode(s, StandardCharsets.UTF_8.toString());
|
||||||
|
return encode.replaceAll("\\+", "%20");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取图像后缀
|
||||||
|
*
|
||||||
|
* @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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取文件名称 /profile/upload/2022/04/16/ruoyi.png -- ruoyi.png
|
||||||
|
*
|
||||||
|
* @param fileName 路径名称
|
||||||
|
* @return 没有文件路径的名称
|
||||||
|
*/
|
||||||
|
public static String getName(String fileName)
|
||||||
|
{
|
||||||
|
if (fileName == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
int lastUnixPos = fileName.lastIndexOf('/');
|
||||||
|
int lastWindowsPos = fileName.lastIndexOf('\\');
|
||||||
|
int index = Math.max(lastUnixPos, lastWindowsPos);
|
||||||
|
return fileName.substring(index + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取不带后缀文件名称 /profile/upload/2022/04/16/ruoyi.png -- ruoyi
|
||||||
|
*
|
||||||
|
* @param fileName 路径名称
|
||||||
|
* @return 没有文件路径和后缀的名称
|
||||||
|
*/
|
||||||
|
public static String getNameNotSuffix(String fileName)
|
||||||
|
{
|
||||||
|
if (fileName == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String baseName = FilenameUtils.getBaseName(fileName);
|
||||||
|
return baseName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,98 @@
|
||||||
|
package com.ruoyi.common.utils.file;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
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;
|
||||||
|
import com.ruoyi.common.constant.Constants;
|
||||||
|
import com.ruoyi.common.utils.StringUtils;
|
||||||
|
import com.ruoyi.framework.config.RuoYiConfig;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 图片处理工具类
|
||||||
|
*
|
||||||
|
* @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
|
||||||
|
{
|
||||||
|
if (url.startsWith("http"))
|
||||||
|
{
|
||||||
|
// 网络地址
|
||||||
|
URL urlObj = new URL(url);
|
||||||
|
URLConnection urlConnection = urlObj.openConnection();
|
||||||
|
urlConnection.setConnectTimeout(30 * 1000);
|
||||||
|
urlConnection.setReadTimeout(60 * 1000);
|
||||||
|
urlConnection.setDoInput(true);
|
||||||
|
in = urlConnection.getInputStream();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 本机地址
|
||||||
|
String localPath = RuoYiConfig.getProfile();
|
||||||
|
String downloadPath = localPath + StringUtils.substringAfter(url, Constants.RESOURCE_PREFIX);
|
||||||
|
in = new FileInputStream(downloadPath);
|
||||||
|
}
|
||||||
|
return IOUtils.toByteArray(in);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
log.error("获取文件路径异常 {}", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
IOUtils.closeQuietly(in);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
package com.ruoyi.common.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 "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,167 @@
|
||||||
|
package com.ruoyi.common.utils.html;
|
||||||
|
|
||||||
|
import com.ruoyi.common.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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,570 @@
|
||||||
|
package com.ruoyi.common.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 (false == 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,274 @@
|
||||||
|
package com.ruoyi.common.utils.http;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.net.ConnectException;
|
||||||
|
import java.net.SocketTimeoutException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLConnection;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
|
import javax.net.ssl.HostnameVerifier;
|
||||||
|
import javax.net.ssl.HttpsURLConnection;
|
||||||
|
import javax.net.ssl.SSLContext;
|
||||||
|
import javax.net.ssl.SSLSession;
|
||||||
|
import javax.net.ssl.TrustManager;
|
||||||
|
import javax.net.ssl.X509TrustManager;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import com.ruoyi.common.constant.Constants;
|
||||||
|
import com.ruoyi.common.utils.StringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通用http发送方法
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
public class HttpUtils
|
||||||
|
{
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(HttpUtils.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 向指定 URL 发送GET方法的请求
|
||||||
|
*
|
||||||
|
* @param url 发送请求的 URL
|
||||||
|
* @return 所代表远程资源的响应结果
|
||||||
|
*/
|
||||||
|
public static String sendGet(String url)
|
||||||
|
{
|
||||||
|
return sendGet(url, StringUtils.EMPTY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 向指定 URL 发送GET方法的请求
|
||||||
|
*
|
||||||
|
* @param url 发送请求的 URL
|
||||||
|
* @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
|
||||||
|
* @return 所代表远程资源的响应结果
|
||||||
|
*/
|
||||||
|
public static String sendGet(String url, String param)
|
||||||
|
{
|
||||||
|
return sendGet(url, param, Constants.UTF8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 向指定 URL 发送GET方法的请求
|
||||||
|
*
|
||||||
|
* @param url 发送请求的 URL
|
||||||
|
* @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
|
||||||
|
* @param contentType 编码类型
|
||||||
|
* @return 所代表远程资源的响应结果
|
||||||
|
*/
|
||||||
|
public static String sendGet(String url, String param, String contentType)
|
||||||
|
{
|
||||||
|
StringBuilder result = new StringBuilder();
|
||||||
|
BufferedReader in = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
String urlNameString = StringUtils.isNotBlank(param) ? url + "?" + param : url;
|
||||||
|
log.info("sendGet - {}", urlNameString);
|
||||||
|
URL realUrl = new URL(urlNameString);
|
||||||
|
URLConnection connection = realUrl.openConnection();
|
||||||
|
connection.setRequestProperty("accept", "*/*");
|
||||||
|
connection.setRequestProperty("connection", "Keep-Alive");
|
||||||
|
connection.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)");
|
||||||
|
connection.connect();
|
||||||
|
in = new BufferedReader(new InputStreamReader(connection.getInputStream(), contentType));
|
||||||
|
String line;
|
||||||
|
while ((line = in.readLine()) != null)
|
||||||
|
{
|
||||||
|
result.append(line);
|
||||||
|
}
|
||||||
|
log.info("recv - {}", result);
|
||||||
|
}
|
||||||
|
catch (ConnectException e)
|
||||||
|
{
|
||||||
|
log.error("调用HttpUtils.sendGet ConnectException, url=" + url + ",param=" + param, e);
|
||||||
|
}
|
||||||
|
catch (SocketTimeoutException e)
|
||||||
|
{
|
||||||
|
log.error("调用HttpUtils.sendGet SocketTimeoutException, url=" + url + ",param=" + param, e);
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
log.error("调用HttpUtils.sendGet IOException, url=" + url + ",param=" + param, e);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
log.error("调用HttpsUtil.sendGet Exception, url=" + url + ",param=" + param, e);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (in != null)
|
||||||
|
{
|
||||||
|
in.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
log.error("调用in.close Exception, url=" + url + ",param=" + param, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 向指定 URL 发送POST方法的请求
|
||||||
|
*
|
||||||
|
* @param url 发送请求的 URL
|
||||||
|
* @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
|
||||||
|
* @return 所代表远程资源的响应结果
|
||||||
|
*/
|
||||||
|
public static String sendPost(String url, String param)
|
||||||
|
{
|
||||||
|
PrintWriter out = null;
|
||||||
|
BufferedReader in = null;
|
||||||
|
StringBuilder result = new StringBuilder();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
log.info("sendPost - {}", url);
|
||||||
|
URL realUrl = new URL(url);
|
||||||
|
URLConnection conn = realUrl.openConnection();
|
||||||
|
conn.setRequestProperty("accept", "*/*");
|
||||||
|
conn.setRequestProperty("connection", "Keep-Alive");
|
||||||
|
conn.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)");
|
||||||
|
conn.setRequestProperty("Accept-Charset", "utf-8");
|
||||||
|
conn.setRequestProperty("contentType", "utf-8");
|
||||||
|
conn.setDoOutput(true);
|
||||||
|
conn.setDoInput(true);
|
||||||
|
out = new PrintWriter(conn.getOutputStream());
|
||||||
|
out.print(param);
|
||||||
|
out.flush();
|
||||||
|
in = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8));
|
||||||
|
String line;
|
||||||
|
while ((line = in.readLine()) != null)
|
||||||
|
{
|
||||||
|
result.append(line);
|
||||||
|
}
|
||||||
|
log.info("recv - {}", result);
|
||||||
|
}
|
||||||
|
catch (ConnectException e)
|
||||||
|
{
|
||||||
|
log.error("调用HttpUtils.sendPost ConnectException, url=" + url + ",param=" + param, e);
|
||||||
|
}
|
||||||
|
catch (SocketTimeoutException e)
|
||||||
|
{
|
||||||
|
log.error("调用HttpUtils.sendPost SocketTimeoutException, url=" + url + ",param=" + param, e);
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
log.error("调用HttpUtils.sendPost IOException, url=" + url + ",param=" + param, e);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
log.error("调用HttpsUtil.sendPost Exception, url=" + url + ",param=" + param, e);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (out != null)
|
||||||
|
{
|
||||||
|
out.close();
|
||||||
|
}
|
||||||
|
if (in != null)
|
||||||
|
{
|
||||||
|
in.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (IOException ex)
|
||||||
|
{
|
||||||
|
log.error("调用in.close Exception, url=" + url + ",param=" + param, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String sendSSLPost(String url, String param)
|
||||||
|
{
|
||||||
|
StringBuilder result = new StringBuilder();
|
||||||
|
String urlNameString = url + "?" + param;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
log.info("sendSSLPost - {}", urlNameString);
|
||||||
|
SSLContext sc = SSLContext.getInstance("SSL");
|
||||||
|
sc.init(null, new TrustManager[] { new TrustAnyTrustManager() }, new java.security.SecureRandom());
|
||||||
|
URL console = new URL(urlNameString);
|
||||||
|
HttpsURLConnection conn = (HttpsURLConnection) console.openConnection();
|
||||||
|
conn.setRequestProperty("accept", "*/*");
|
||||||
|
conn.setRequestProperty("connection", "Keep-Alive");
|
||||||
|
conn.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)");
|
||||||
|
conn.setRequestProperty("Accept-Charset", "utf-8");
|
||||||
|
conn.setRequestProperty("contentType", "utf-8");
|
||||||
|
conn.setDoOutput(true);
|
||||||
|
conn.setDoInput(true);
|
||||||
|
|
||||||
|
conn.setSSLSocketFactory(sc.getSocketFactory());
|
||||||
|
conn.setHostnameVerifier(new TrustAnyHostnameVerifier());
|
||||||
|
conn.connect();
|
||||||
|
InputStream is = conn.getInputStream();
|
||||||
|
BufferedReader br = new BufferedReader(new InputStreamReader(is));
|
||||||
|
String ret = "";
|
||||||
|
while ((ret = br.readLine()) != null)
|
||||||
|
{
|
||||||
|
if (ret != null && !ret.trim().equals(""))
|
||||||
|
{
|
||||||
|
result.append(new String(ret.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.info("recv - {}", result);
|
||||||
|
conn.disconnect();
|
||||||
|
br.close();
|
||||||
|
}
|
||||||
|
catch (ConnectException e)
|
||||||
|
{
|
||||||
|
log.error("调用HttpUtils.sendSSLPost ConnectException, url=" + url + ",param=" + param, e);
|
||||||
|
}
|
||||||
|
catch (SocketTimeoutException e)
|
||||||
|
{
|
||||||
|
log.error("调用HttpUtils.sendSSLPost SocketTimeoutException, url=" + url + ",param=" + param, e);
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
log.error("调用HttpUtils.sendSSLPost IOException, url=" + url + ",param=" + param, e);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
log.error("调用HttpsUtil.sendSSLPost Exception, url=" + url + ",param=" + param, e);
|
||||||
|
}
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class TrustAnyTrustManager implements X509TrustManager
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void checkClientTrusted(X509Certificate[] chain, String authType)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void checkServerTrusted(X509Certificate[] chain, String authType)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public X509Certificate[] getAcceptedIssuers()
|
||||||
|
{
|
||||||
|
return new X509Certificate[] {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class TrustAnyHostnameVerifier implements HostnameVerifier
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public boolean verify(String hostname, SSLSession session)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
package com.ruoyi.common.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);
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,410 @@
|
||||||
|
package com.ruoyi.common.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.utils.DateUtils;
|
||||||
|
import com.ruoyi.common.utils.text.Convert;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 反射工具类. 提供调用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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
package com.ruoyi.common.utils.security;
|
||||||
|
|
||||||
|
import org.apache.shiro.SecurityUtils;
|
||||||
|
import org.apache.shiro.mgt.RealmSecurityManager;
|
||||||
|
import com.ruoyi.framework.shiro.realm.UserRealm;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户授权信息
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
public class AuthorizationUtils
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 清理所有用户授权信息缓存
|
||||||
|
*/
|
||||||
|
public static void clearAllCachedAuthorizationInfo()
|
||||||
|
{
|
||||||
|
getUserRealm().clearAllCachedAuthorizationInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取自定义Realm
|
||||||
|
*/
|
||||||
|
public static UserRealm getUserRealm()
|
||||||
|
{
|
||||||
|
RealmSecurityManager rsm = (RealmSecurityManager) SecurityUtils.getSecurityManager();
|
||||||
|
return (UserRealm) rsm.getRealms().iterator().next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
package com.ruoyi.common.utils.security;
|
||||||
|
|
||||||
|
import java.security.Key;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import javax.crypto.KeyGenerator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对称密钥密码算法工具类
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
public class CipherUtils
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 生成随机秘钥
|
||||||
|
*
|
||||||
|
* @param keyBitSize 字节大小
|
||||||
|
* @param algorithmName 算法名称
|
||||||
|
* @return 创建密匙
|
||||||
|
*/
|
||||||
|
public static Key generateNewKey(int keyBitSize, String algorithmName)
|
||||||
|
{
|
||||||
|
KeyGenerator kg;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
kg = KeyGenerator.getInstance(algorithmName);
|
||||||
|
}
|
||||||
|
catch (NoSuchAlgorithmException e)
|
||||||
|
{
|
||||||
|
String msg = "Unable to acquire " + algorithmName + " algorithm. This is required to function.";
|
||||||
|
throw new IllegalStateException(msg, e);
|
||||||
|
}
|
||||||
|
kg.init(keyBitSize);
|
||||||
|
return kg.generateKey();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,77 @@
|
||||||
|
package com.ruoyi.common.utils.security;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import com.ruoyi.common.constant.PermissionConstants;
|
||||||
|
import com.ruoyi.common.utils.MessageUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* permission 工具类
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
public class PermissionUtils
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 查看数据的权限
|
||||||
|
*/
|
||||||
|
public static final String VIEW_PERMISSION = "no.view.permission";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建数据的权限
|
||||||
|
*/
|
||||||
|
public static final String CREATE_PERMISSION = "no.create.permission";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改数据的权限
|
||||||
|
*/
|
||||||
|
public static final String UPDATE_PERMISSION = "no.update.permission";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除数据的权限
|
||||||
|
*/
|
||||||
|
public static final String DELETE_PERMISSION = "no.delete.permission";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出数据的权限
|
||||||
|
*/
|
||||||
|
public static final String EXPORT_PERMISSION = "no.export.permission";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 其他数据的权限
|
||||||
|
*/
|
||||||
|
public static final String PERMISSION = "no.permission";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 权限错误消息提醒
|
||||||
|
*
|
||||||
|
* @param permissionsStr 错误信息
|
||||||
|
* @return 提示信息
|
||||||
|
*/
|
||||||
|
public static String getMsg(String permissionsStr)
|
||||||
|
{
|
||||||
|
String permission = StringUtils.substringBetween(permissionsStr, "[", "]");
|
||||||
|
String msg = MessageUtils.message(PERMISSION, permission);
|
||||||
|
if (StringUtils.endsWithIgnoreCase(permission, PermissionConstants.ADD_PERMISSION))
|
||||||
|
{
|
||||||
|
msg = MessageUtils.message(CREATE_PERMISSION, permission);
|
||||||
|
}
|
||||||
|
else if (StringUtils.endsWithIgnoreCase(permission, PermissionConstants.EDIT_PERMISSION))
|
||||||
|
{
|
||||||
|
msg = MessageUtils.message(UPDATE_PERMISSION, permission);
|
||||||
|
}
|
||||||
|
else if (StringUtils.endsWithIgnoreCase(permission, PermissionConstants.REMOVE_PERMISSION))
|
||||||
|
{
|
||||||
|
msg = MessageUtils.message(DELETE_PERMISSION, permission);
|
||||||
|
}
|
||||||
|
else if (StringUtils.endsWithIgnoreCase(permission, PermissionConstants.EXPORT_PERMISSION))
|
||||||
|
{
|
||||||
|
msg = MessageUtils.message(EXPORT_PERMISSION, permission);
|
||||||
|
}
|
||||||
|
else if (StringUtils.endsWithAny(permission,
|
||||||
|
new String[] { PermissionConstants.VIEW_PERMISSION, PermissionConstants.LIST_PERMISSION }))
|
||||||
|
{
|
||||||
|
msg = MessageUtils.message(VIEW_PERMISSION, permission);
|
||||||
|
}
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,75 @@
|
||||||
|
package com.ruoyi.common.utils.security;
|
||||||
|
|
||||||
|
import org.apache.shiro.SecurityUtils;
|
||||||
|
import org.apache.shiro.session.Session;
|
||||||
|
import org.apache.shiro.subject.Subject;
|
||||||
|
import org.apache.shiro.subject.PrincipalCollection;
|
||||||
|
import org.apache.shiro.subject.SimplePrincipalCollection;
|
||||||
|
import com.ruoyi.common.utils.StringUtils;
|
||||||
|
import com.ruoyi.common.utils.bean.BeanUtils;
|
||||||
|
import com.ruoyi.project.system.user.domain.User;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* shiro 工具类
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
public class ShiroUtils
|
||||||
|
{
|
||||||
|
public static Subject getSubject()
|
||||||
|
{
|
||||||
|
return SecurityUtils.getSubject();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Session getSession()
|
||||||
|
{
|
||||||
|
return SecurityUtils.getSubject().getSession();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void logout()
|
||||||
|
{
|
||||||
|
getSubject().logout();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static User getSysUser()
|
||||||
|
{
|
||||||
|
User user = null;
|
||||||
|
Object obj = getSubject().getPrincipal();
|
||||||
|
if (StringUtils.isNotNull(obj))
|
||||||
|
{
|
||||||
|
user = new User();
|
||||||
|
BeanUtils.copyBeanProp(user, obj);
|
||||||
|
}
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setSysUser(User user)
|
||||||
|
{
|
||||||
|
Subject subject = getSubject();
|
||||||
|
PrincipalCollection principalCollection = subject.getPrincipals();
|
||||||
|
String realmName = principalCollection.getRealmNames().iterator().next();
|
||||||
|
PrincipalCollection newPrincipalCollection = new SimplePrincipalCollection(user, realmName);
|
||||||
|
// 重新加载Principal
|
||||||
|
subject.runAs(newPrincipalCollection);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Long getUserId()
|
||||||
|
{
|
||||||
|
return getSysUser().getUserId().longValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getLoginName()
|
||||||
|
{
|
||||||
|
return getSysUser().getLoginName();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getIp()
|
||||||
|
{
|
||||||
|
return StringUtils.substring(getSubject().getSession().getHost(), 0, 128);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getSessionId()
|
||||||
|
{
|
||||||
|
return String.valueOf(getSubject().getSession().getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,159 @@
|
||||||
|
package com.ruoyi.common.utils.spring;
|
||||||
|
|
||||||
|
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.context.ApplicationContext;
|
||||||
|
import org.springframework.context.ApplicationContextAware;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import com.ruoyi.common.utils.StringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* spring工具类 方便在非spring管理环境中获取bean
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationContextAware
|
||||||
|
{
|
||||||
|
/** Spring应用上下文环境 */
|
||||||
|
private static ConfigurableListableBeanFactory beanFactory;
|
||||||
|
|
||||||
|
private static ApplicationContext applicationContext;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException
|
||||||
|
{
|
||||||
|
SpringUtils.beanFactory = beanFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
|
||||||
|
{
|
||||||
|
SpringUtils.applicationContext = applicationContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取对象
|
||||||
|
*
|
||||||
|
* @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();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前的环境配置,无配置返回null
|
||||||
|
*
|
||||||
|
* @return 当前的环境配置
|
||||||
|
*/
|
||||||
|
public static String[] getActiveProfiles()
|
||||||
|
{
|
||||||
|
return applicationContext.getEnvironment().getActiveProfiles();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前的环境配置,当有多个环境配置时,只获取第一个
|
||||||
|
*
|
||||||
|
* @return 当前的环境配置
|
||||||
|
*/
|
||||||
|
public static String getActiveProfile()
|
||||||
|
{
|
||||||
|
final String[] activeProfiles = getActiveProfiles();
|
||||||
|
return StringUtils.isNotEmpty(activeProfiles) ? activeProfiles[0] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取配置文件中的值
|
||||||
|
*
|
||||||
|
* @param key 配置文件的key
|
||||||
|
* @return 当前的配置文件的值
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public static String getRequiredProperty(String key)
|
||||||
|
{
|
||||||
|
return applicationContext.getEnvironment().getRequiredProperty(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,70 @@
|
||||||
|
package com.ruoyi.common.utils.sql;
|
||||||
|
|
||||||
|
import com.ruoyi.common.exception.UtilException;
|
||||||
|
import com.ruoyi.common.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注入风险");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,87 @@
|
||||||
|
package com.ruoyi.common.utils.text;
|
||||||
|
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import com.ruoyi.common.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();
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,93 @@
|
||||||
|
package com.ruoyi.common.utils.text;
|
||||||
|
|
||||||
|
import com.ruoyi.common.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// append the characters following the last {} pair.
|
||||||
|
// 加入最后一个占位符后所有的字符
|
||||||
|
sbuf.append(strPattern, handledPosition, strPattern.length());
|
||||||
|
|
||||||
|
return sbuf.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
package com.ruoyi.common.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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,86 @@
|
||||||
|
package com.ruoyi.common.utils.uuid;
|
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import com.ruoyi.common.utils.DateUtils;
|
||||||
|
import com.ruoyi.common.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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,484 @@
|
||||||
|
package com.ruoyi.common.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.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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
package com.ruoyi.common.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 {};
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,74 @@
|
||||||
|
package com.ruoyi.common.xss;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import javax.servlet.Filter;
|
||||||
|
import javax.servlet.FilterChain;
|
||||||
|
import javax.servlet.FilterConfig;
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.ServletRequest;
|
||||||
|
import javax.servlet.ServletResponse;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import com.ruoyi.common.utils.StringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 防止XSS攻击的过滤器
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
public class XssFilter implements Filter
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 排除链接
|
||||||
|
*/
|
||||||
|
public List<String> excludes = new ArrayList<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(FilterConfig filterConfig) throws ServletException
|
||||||
|
{
|
||||||
|
String tempExcludes = filterConfig.getInitParameter("excludes");
|
||||||
|
if (StringUtils.isNotEmpty(tempExcludes))
|
||||||
|
{
|
||||||
|
String[] urls = tempExcludes.split(",");
|
||||||
|
for (String url : urls)
|
||||||
|
{
|
||||||
|
excludes.add(url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
|
||||||
|
throws IOException, ServletException
|
||||||
|
{
|
||||||
|
HttpServletRequest req = (HttpServletRequest) request;
|
||||||
|
HttpServletResponse resp = (HttpServletResponse) response;
|
||||||
|
if (handleExcludeURL(req, resp))
|
||||||
|
{
|
||||||
|
chain.doFilter(request, response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper((HttpServletRequest) request);
|
||||||
|
chain.doFilter(xssRequest, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean handleExcludeURL(HttpServletRequest request, HttpServletResponse response)
|
||||||
|
{
|
||||||
|
String url = request.getServletPath();
|
||||||
|
String method = request.getMethod();
|
||||||
|
// GET DELETE 不过滤
|
||||||
|
if (method == null || method.matches("GET") || method.matches("DELETE"))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return StringUtils.matches(url, excludes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void destroy()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
package com.ruoyi.common.xss;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletRequestWrapper;
|
||||||
|
import com.ruoyi.common.utils.html.EscapeUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* XSS过滤处理
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param request
|
||||||
|
*/
|
||||||
|
public XssHttpServletRequestWrapper(HttpServletRequest request)
|
||||||
|
{
|
||||||
|
super(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] getParameterValues(String name)
|
||||||
|
{
|
||||||
|
String[] values = super.getParameterValues(name);
|
||||||
|
if (values != null)
|
||||||
|
{
|
||||||
|
int length = values.length;
|
||||||
|
String[] escapseValues = new String[length];
|
||||||
|
for (int i = 0; i < length; i++)
|
||||||
|
{
|
||||||
|
// 防xss攻击和过滤前后空格
|
||||||
|
escapseValues[i] = EscapeUtil.clean(values[i]).trim();
|
||||||
|
}
|
||||||
|
return escapseValues;
|
||||||
|
}
|
||||||
|
return super.getParameterValues(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
package com.ruoyi.common.xss;
|
||||||
|
|
||||||
|
import com.ruoyi.common.utils.StringUtils;
|
||||||
|
import javax.validation.ConstraintValidator;
|
||||||
|
import javax.validation.ConstraintValidatorContext;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,182 @@
|
||||||
|
package com.ruoyi.framework.aspectj;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import org.aspectj.lang.JoinPoint;
|
||||||
|
import org.aspectj.lang.annotation.Aspect;
|
||||||
|
import org.aspectj.lang.annotation.Before;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import com.ruoyi.common.utils.StringUtils;
|
||||||
|
import com.ruoyi.common.utils.security.ShiroUtils;
|
||||||
|
import com.ruoyi.common.utils.text.Convert;
|
||||||
|
import com.ruoyi.framework.aspectj.lang.annotation.DataScope;
|
||||||
|
import com.ruoyi.framework.context.PermissionContextHolder;
|
||||||
|
import com.ruoyi.framework.web.domain.BaseEntity;
|
||||||
|
import com.ruoyi.project.system.role.domain.Role;
|
||||||
|
import com.ruoyi.project.system.user.domain.User;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据过滤处理
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
@Aspect
|
||||||
|
@Component
|
||||||
|
public class DataScopeAspect
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 全部数据权限
|
||||||
|
*/
|
||||||
|
public static final String DATA_SCOPE_ALL = "1";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定数据权限
|
||||||
|
*/
|
||||||
|
public static final String DATA_SCOPE_CUSTOM = "2";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 部门数据权限
|
||||||
|
*/
|
||||||
|
public static final String DATA_SCOPE_DEPT = "3";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 部门及以下数据权限
|
||||||
|
*/
|
||||||
|
public static final String DATA_SCOPE_DEPT_AND_CHILD = "4";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 仅本人数据权限
|
||||||
|
*/
|
||||||
|
public static final String DATA_SCOPE_SELF = "5";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据权限过滤关键字
|
||||||
|
*/
|
||||||
|
public static final String DATA_SCOPE = "dataScope";
|
||||||
|
|
||||||
|
@Before("@annotation(controllerDataScope)")
|
||||||
|
public void doBefore(JoinPoint point, DataScope controllerDataScope) throws Throwable
|
||||||
|
{
|
||||||
|
clearDataScope(point);
|
||||||
|
handleDataScope(point, controllerDataScope);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void handleDataScope(final JoinPoint joinPoint, DataScope controllerDataScope)
|
||||||
|
{
|
||||||
|
// 获取当前的用户
|
||||||
|
User currentUser = ShiroUtils.getSysUser();
|
||||||
|
if (currentUser != null)
|
||||||
|
{
|
||||||
|
// 如果是超级管理员,则不过滤数据
|
||||||
|
if (!currentUser.isAdmin())
|
||||||
|
{
|
||||||
|
String permission = StringUtils.defaultIfEmpty(controllerDataScope.permission(), PermissionContextHolder.getContext());
|
||||||
|
dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptAlias(),
|
||||||
|
controllerDataScope.userAlias(), permission);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据范围过滤
|
||||||
|
*
|
||||||
|
* @param joinPoint 切点
|
||||||
|
* @param user 用户
|
||||||
|
* @param deptAlias 部门别名
|
||||||
|
* @param userAlias 用户别名
|
||||||
|
* @param permission 权限字符
|
||||||
|
*/
|
||||||
|
public static void dataScopeFilter(JoinPoint joinPoint, User user, String deptAlias, String userAlias, String permission)
|
||||||
|
{
|
||||||
|
StringBuilder sqlString = new StringBuilder();
|
||||||
|
List<String> conditions = new ArrayList<String>();
|
||||||
|
List<String> scopeCustomIds = new ArrayList<String>();
|
||||||
|
user.getRoles().forEach(role -> {
|
||||||
|
if (DATA_SCOPE_CUSTOM.equals(role.getDataScope()) && StringUtils.containsAny(role.getPermissions(), Convert.toStrArray(permission)))
|
||||||
|
{
|
||||||
|
scopeCustomIds.add(Convert.toStr(role.getRoleId()));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
for (Role role : user.getRoles())
|
||||||
|
{
|
||||||
|
String dataScope = role.getDataScope();
|
||||||
|
if (conditions.contains(dataScope))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!StringUtils.containsAny(role.getPermissions(), Convert.toStrArray(permission)))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (DATA_SCOPE_ALL.equals(dataScope))
|
||||||
|
{
|
||||||
|
sqlString = new StringBuilder();
|
||||||
|
conditions.add(dataScope);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (DATA_SCOPE_CUSTOM.equals(dataScope))
|
||||||
|
{
|
||||||
|
if (scopeCustomIds.size() > 1)
|
||||||
|
{
|
||||||
|
// 多个自定数据权限使用in查询,避免多次拼接。
|
||||||
|
sqlString.append(StringUtils.format(" OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id in ({}) ) ", deptAlias, String.join(",", scopeCustomIds)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sqlString.append(StringUtils.format(" OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ", deptAlias, role.getRoleId()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (DATA_SCOPE_DEPT.equals(dataScope))
|
||||||
|
{
|
||||||
|
sqlString.append(StringUtils.format(" OR {}.dept_id = {} ", deptAlias, user.getDeptId()));
|
||||||
|
}
|
||||||
|
else if (DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope))
|
||||||
|
{
|
||||||
|
sqlString.append(StringUtils.format(" OR {}.dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} or find_in_set( {} , ancestors ) )", deptAlias, user.getDeptId(), user.getDeptId()));
|
||||||
|
}
|
||||||
|
else if (DATA_SCOPE_SELF.equals(dataScope))
|
||||||
|
{
|
||||||
|
if (StringUtils.isNotBlank(userAlias))
|
||||||
|
{
|
||||||
|
sqlString.append(StringUtils.format(" OR {}.user_id = {} ", userAlias, user.getUserId()));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 数据权限为仅本人且没有userAlias别名不查询任何数据
|
||||||
|
sqlString.append(StringUtils.format(" OR {}.dept_id = 0 ", deptAlias));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
conditions.add(dataScope);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 多角色情况下,所有角色都不包含传递过来的权限字符,这个时候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, "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
package com.ruoyi.framework.aspectj;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
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.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.core.annotation.AnnotationUtils;
|
||||||
|
import org.springframework.core.annotation.Order;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import com.ruoyi.common.utils.StringUtils;
|
||||||
|
import com.ruoyi.framework.aspectj.lang.annotation.DataSource;
|
||||||
|
import com.ruoyi.framework.datasource.DynamicDataSourceContextHolder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 多数据源处理
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
@Aspect
|
||||||
|
@Order(1)
|
||||||
|
@Component
|
||||||
|
public class DataSourceAspect
|
||||||
|
{
|
||||||
|
protected Logger logger = LoggerFactory.getLogger(getClass());
|
||||||
|
|
||||||
|
@Pointcut("@annotation(com.ruoyi.framework.aspectj.lang.annotation.DataSource)"
|
||||||
|
+ "|| @within(com.ruoyi.framework.aspectj.lang.annotation.DataSource)")
|
||||||
|
public void dsPointCut()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Around("dsPointCut()")
|
||||||
|
public Object around(ProceedingJoinPoint point) throws Throwable
|
||||||
|
{
|
||||||
|
DataSource dataSource = getDataSource(point);
|
||||||
|
|
||||||
|
if (StringUtils.isNotNull(dataSource))
|
||||||
|
{
|
||||||
|
DynamicDataSourceContextHolder.setDataSourceType(dataSource.value().name());
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return point.proceed();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
// 销毁数据源 在执行方法之后
|
||||||
|
DynamicDataSourceContextHolder.clearDataSourceType();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取需要切换的数据源
|
||||||
|
*/
|
||||||
|
public DataSource getDataSource(ProceedingJoinPoint point)
|
||||||
|
{
|
||||||
|
MethodSignature signature = (MethodSignature) point.getSignature();
|
||||||
|
DataSource dataSource = AnnotationUtils.findAnnotation(signature.getMethod(), DataSource.class);
|
||||||
|
if (Objects.nonNull(dataSource))
|
||||||
|
{
|
||||||
|
return dataSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
return AnnotationUtils.findAnnotation(signature.getDeclaringType(), DataSource.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,255 @@
|
||||||
|
package com.ruoyi.framework.aspectj;
|
||||||
|
|
||||||
|
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.core.NamedThreadLocal;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.validation.BindingResult;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import com.alibaba.fastjson.support.spring.PropertyPreFilters;
|
||||||
|
import com.ruoyi.common.utils.ServletUtils;
|
||||||
|
import com.ruoyi.common.utils.StringUtils;
|
||||||
|
import com.ruoyi.common.utils.security.ShiroUtils;
|
||||||
|
import com.ruoyi.framework.aspectj.lang.annotation.Log;
|
||||||
|
import com.ruoyi.framework.aspectj.lang.enums.BusinessStatus;
|
||||||
|
import com.ruoyi.framework.manager.AsyncManager;
|
||||||
|
import com.ruoyi.framework.manager.factory.AsyncFactory;
|
||||||
|
import com.ruoyi.project.monitor.operlog.domain.OperLog;
|
||||||
|
import com.ruoyi.project.system.user.domain.User;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 操作日志记录处理
|
||||||
|
*
|
||||||
|
* @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");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理请求前执行
|
||||||
|
*/
|
||||||
|
@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
|
||||||
|
{
|
||||||
|
// 获取当前的用户
|
||||||
|
User currentUser = ShiroUtils.getSysUser();
|
||||||
|
|
||||||
|
// *========数据库日志=========*//
|
||||||
|
OperLog operLog = new OperLog();
|
||||||
|
operLog.setStatus(BusinessStatus.SUCCESS.ordinal());
|
||||||
|
// 请求的地址
|
||||||
|
String ip = ShiroUtils.getIp();
|
||||||
|
operLog.setOperIp(ip);
|
||||||
|
operLog.setOperUrl(StringUtils.substring(ServletUtils.getRequest().getRequestURI(), 0, 255));
|
||||||
|
if (currentUser != null)
|
||||||
|
{
|
||||||
|
operLog.setOperName(currentUser.getLoginName());
|
||||||
|
if (StringUtils.isNotNull(currentUser.getDept())
|
||||||
|
&& StringUtils.isNotEmpty(currentUser.getDept().getDeptName()))
|
||||||
|
{
|
||||||
|
operLog.setDeptName(currentUser.getDept().getDeptName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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());
|
||||||
|
// 保存数据库
|
||||||
|
AsyncManager.me().execute(AsyncFactory.recordOper(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, OperLog 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(JSONObject.toJSONString(jsonResult), 0, 2000));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取请求的参数,放到log中
|
||||||
|
*
|
||||||
|
* @param operLog
|
||||||
|
* @param request
|
||||||
|
*/
|
||||||
|
private void setRequestValue(JoinPoint joinPoint, OperLog operLog, String[] excludeParamNames)
|
||||||
|
{
|
||||||
|
Map<String, String[]> map = ServletUtils.getRequest().getParameterMap();
|
||||||
|
if (StringUtils.isNotEmpty(map))
|
||||||
|
{
|
||||||
|
String params = JSONObject.toJSONString(map, excludePropertyPreFilter(excludeParamNames));
|
||||||
|
operLog.setOperParam(StringUtils.substring(params, 0, 2000));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Object args = joinPoint.getArgs();
|
||||||
|
if (StringUtils.isNotNull(args))
|
||||||
|
{
|
||||||
|
String params = argsArrayToString(joinPoint.getArgs(), excludeParamNames);
|
||||||
|
operLog.setOperParam(StringUtils.substring(params, 0, 2000));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 忽略敏感属性
|
||||||
|
*/
|
||||||
|
public PropertyPreFilters.MySimplePropertyPreFilter excludePropertyPreFilter(String[] excludeParamNames)
|
||||||
|
{
|
||||||
|
return new PropertyPreFilters().addFilter().addExcludes(ArrayUtils.addAll(EXCLUDE_PROPERTIES, excludeParamNames));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 参数拼装
|
||||||
|
*/
|
||||||
|
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
|
||||||
|
{
|
||||||
|
Object jsonObj = JSONObject.toJSONString(o, excludePropertyPreFilter(excludeParamNames));
|
||||||
|
params += jsonObj.toString() + " ";
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return params.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否需要过滤的对象。
|
||||||
|
*
|
||||||
|
* @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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
package com.ruoyi.framework.aspectj;
|
||||||
|
|
||||||
|
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||||
|
import org.aspectj.lang.JoinPoint;
|
||||||
|
import org.aspectj.lang.annotation.Aspect;
|
||||||
|
import org.aspectj.lang.annotation.Before;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import com.ruoyi.common.utils.StringUtils;
|
||||||
|
import com.ruoyi.framework.context.PermissionContextHolder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义权限拦截器,将权限字符串放到当前请求中以便用于多个角色匹配符合要求的权限
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
@Aspect
|
||||||
|
@Component
|
||||||
|
public class PermissionsAspect
|
||||||
|
{
|
||||||
|
@Before("@annotation(controllerRequiresPermissions)")
|
||||||
|
public void doBefore(JoinPoint point, RequiresPermissions controllerRequiresPermissions) throws Throwable
|
||||||
|
{
|
||||||
|
handleRequiresPermissions(point, controllerRequiresPermissions);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void handleRequiresPermissions(final JoinPoint joinPoint, RequiresPermissions requiresPermissions)
|
||||||
|
{
|
||||||
|
PermissionContextHolder.setContext(StringUtils.join(requiresPermissions.value(), ","));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
package com.ruoyi.framework.aspectj.lang.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 匿名访问不鉴权注解
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
@Target({ ElementType.METHOD, ElementType.TYPE })
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Documented
|
||||||
|
public @interface Anonymous
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
package com.ruoyi.framework.aspectj.lang.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 "";
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
package com.ruoyi.framework.aspectj.lang.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Inherited;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
import com.ruoyi.framework.aspectj.lang.enums.DataSourceType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义多数据源切换注解
|
||||||
|
*
|
||||||
|
* 优先级:先方法,后类,如果方法覆盖了类上的数据源类型,以方法的为准,否则以类上的为准
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
@Target({ ElementType.METHOD, ElementType.TYPE })
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Documented
|
||||||
|
@Inherited
|
||||||
|
public @interface DataSource
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 切换数据源名称
|
||||||
|
*/
|
||||||
|
public DataSourceType value() default DataSourceType.MASTER;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,192 @@
|
||||||
|
package com.ruoyi.framework.aspectj.lang.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.utils.poi.ExcelHandlerAdapter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义导出Excel数据注解
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target(ElementType.FIELD)
|
||||||
|
public @interface Excel
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 导出时在excel中排序
|
||||||
|
*/
|
||||||
|
public int sort() default Integer.MAX_VALUE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出到Excel中的名字.
|
||||||
|
*/
|
||||||
|
public String name() default "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 日期格式, 如: yyyy-MM-dd
|
||||||
|
*/
|
||||||
|
public String dateFormat() default "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 如果是字典类型,请设置字典的type值 (如: sys_user_sex)
|
||||||
|
*/
|
||||||
|
public String dictType() default "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 读取内容转表达式 (如: 0=男,1=女,2=未知)
|
||||||
|
*/
|
||||||
|
public String readConverterExp() default "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分隔符,读取字符串组内容
|
||||||
|
*/
|
||||||
|
public String separator() default ",";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BigDecimal 精度 默认:-1(默认不开启BigDecimal格式化)
|
||||||
|
*/
|
||||||
|
public int scale() default -1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BigDecimal 舍入规则 默认:BigDecimal.ROUND_HALF_EVEN
|
||||||
|
*/
|
||||||
|
public 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 {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否从字典读数据到combo,默认不读取,如读取需要设置dictType注解.
|
||||||
|
*/
|
||||||
|
public boolean comboReadDict() default false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否需要纵向合并单元格,应对需求:含有list集合单元格)
|
||||||
|
*/
|
||||||
|
public boolean needMerge() default false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否导出数据,应对需求:有时我们需要导出一份模板,这是标题需要但内容需要用户手工填写.
|
||||||
|
*/
|
||||||
|
public boolean isExport() default true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 另一个类中的属性名称,支持多级获取,以小数点隔开
|
||||||
|
*/
|
||||||
|
public String targetAttr() default "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否自动统计数据,在最后追加一行统计数据总和
|
||||||
|
*/
|
||||||
|
public boolean isStatistics() default false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出类型(0数字 1字符串 2图片)
|
||||||
|
*/
|
||||||
|
public ColumnType cellType() default ColumnType.STRING;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出列头背景颜色
|
||||||
|
*/
|
||||||
|
public IndexedColors headerBackgroundColor() default IndexedColors.GREY_50_PERCENT;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出列头字体颜色
|
||||||
|
*/
|
||||||
|
public IndexedColors headerColor() default IndexedColors.WHITE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出单元格背景颜色
|
||||||
|
*/
|
||||||
|
public IndexedColors backgroundColor() default IndexedColors.WHITE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出单元格字体颜色
|
||||||
|
*/
|
||||||
|
public IndexedColors color() default IndexedColors.BLACK;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出字段对齐方式
|
||||||
|
*/
|
||||||
|
public HorizontalAlignment align() default HorizontalAlignment.CENTER;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义数据处理器
|
||||||
|
*/
|
||||||
|
public Class<?> handler() default ExcelHandlerAdapter.class;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义数据处理器参数
|
||||||
|
*/
|
||||||
|
public String[] args() default {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字段类型(0:导出导入;1:仅导出;2:仅导入)
|
||||||
|
*/
|
||||||
|
Type type() default Type.ALL;
|
||||||
|
|
||||||
|
public enum Type
|
||||||
|
{
|
||||||
|
ALL(0), EXPORT(1), IMPORT(2);
|
||||||
|
private final int value;
|
||||||
|
|
||||||
|
Type(int value)
|
||||||
|
{
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int value()
|
||||||
|
{
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ColumnType
|
||||||
|
{
|
||||||
|
NUMERIC(0), STRING(1), IMAGE(2), TEXT(3);
|
||||||
|
private final int value;
|
||||||
|
|
||||||
|
ColumnType(int value)
|
||||||
|
{
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int value()
|
||||||
|
{
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
package com.ruoyi.framework.aspectj.lang.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();
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue