若依 2.1

This commit is contained in:
RuoYi 2018-07-09 08:44:52 +08:00
parent a66f603437
commit 57773e6eff
458 changed files with 103815 additions and 0 deletions

20
LICENSE Normal file
View File

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

92
README.md Normal file
View File

@ -0,0 +1,92 @@
## 平台简介
一直想做一款后台管理系统看了很多优秀的开源项目但是发现没有合适的。于是利用空闲休息时间开始自己写了一套后台系统。如此有了若依。她可以用于所有的Web应用程序如网站管理后台网站会员中心CMSCRMOA。所有前端后台代码封装过后十分精简易上手出错概率低。同时支持移动客户端访问。系统会陆续更新一些实用功能。
性别男,若依是女儿的名字。
若依基于hplus和inspinia两套后台系统摸版开发。有需要可自行到群内下载。
http://www.zi-han.net/theme/hplus
http://webapplayers.com/inspinia_admin-v2.7.1
## 内置功能
1. 用户管理:用户是系统操作者,该功能主要完成系统用户配置。
2. 部门管理:配置系统组织机构(公司、部门、小组),树结构展现。
3. 岗位管理:配置系统用户所属担任职务。
4. 菜单管理:配置系统菜单,操作权限,按钮权限标识等。
5. 角色管理:角色菜单权限分配、设置角色按机构进行数据范围权限划分。
6. 字典管理:对系统中经常使用的一些较为固定的数据进行维护。
7. 参数管理:对系统动态配置常用参数。
8. 通知公告:系统通知公告信息发布维护。
9. 操作日志:系统正常操作日志记录和查询;系统异常信息日志记录和查询。
10. 登录日志:系统登录日志记录查询包含登录异常。
11. 在线用户:当前系统中活跃用户状态监控。
12. 定时任务:在线(添加、修改、删除)任务调度包含执行结果日志。
13. 代码生成前后端代码的生成java、html、xml、sql)支持CRUD下载 。
14. 系统接口根据业务代码自动生成相关的api接口文档。
15. 在线构建器拖动表单元素生成相应的HTML代码。
16. 连接池监视监视当期系统数据库连接池状态可进行分析SQL找出系统性能瓶颈。
## 在线体验
> admin/admin123
地址http://www.ruoyi.club
## 演示图
<table>
<tr>
<td><img src="https://ruoyi-1256174062.cos.ap-guangzhou.myqcloud.com/%E8%8B%A5%E4%BE%9D2.0/%E7%99%BB%E5%BD%95%E7%95%8C%E9%9D%A2.png"/></td>
<td><img src="https://ruoyi-1256174062.cos.ap-guangzhou.myqcloud.com/%E8%8B%A5%E4%BE%9D2.0/%E9%A6%96%E9%A1%B5.png"/></td>
</tr>
<tr>
<td><img src="https://ruoyi-1256174062.cos.ap-guangzhou.myqcloud.com/%E8%8B%A5%E4%BE%9D2.0/%E7%94%A8%E6%88%B7%E7%AE%A1%E7%90%86.png"/></td>
<td><img src="https://ruoyi-1256174062.cos.ap-guangzhou.myqcloud.com/%E8%8B%A5%E4%BE%9D2.0/%E7%94%A8%E6%88%B7%E4%BF%AE%E6%94%B9.png"/></td>
</tr>
<tr>
<td><img src="https://ruoyi-1256174062.cos.ap-guangzhou.myqcloud.com/%E8%8B%A5%E4%BE%9D2.0/%E8%A7%92%E8%89%B2%E7%AE%A1%E7%90%86.png"/></td>
<td><img src="https://ruoyi-1256174062.cos.ap-guangzhou.myqcloud.com/%E8%8B%A5%E4%BE%9D2.0/%E8%A7%92%E8%89%B2%E4%BF%AE%E6%94%B9.png"/></td>
</tr>
<tr>
<td><img src="https://ruoyi-1256174062.cos.ap-guangzhou.myqcloud.com/%E8%8B%A5%E4%BE%9D2.0/%E8%8F%9C%E5%8D%95%E7%AE%A1%E7%90%86.png"/></td>
<td><img src="https://ruoyi-1256174062.cos.ap-guangzhou.myqcloud.com/%E8%8B%A5%E4%BE%9D2.0/%E4%BF%AE%E6%94%B9%E8%8F%9C%E5%8D%95.png"/></td>
</tr>
<tr>
<td><img src="https://ruoyi-1256174062.cos.ap-guangzhou.myqcloud.com/%E8%8B%A5%E4%BE%9D2.0/%E9%83%A8%E9%97%A8%E7%AE%A1%E7%90%86.png"/></td>
<td><img src="https://ruoyi-1256174062.cos.ap-guangzhou.myqcloud.com/%E8%8B%A5%E4%BE%9D2.0/%E4%BF%AE%E6%94%B9%E9%83%A8%E9%97%A8.png"/></td>
</tr>
<tr>
<td><img src="https://ruoyi-1256174062.cos.ap-guangzhou.myqcloud.com/%E8%8B%A5%E4%BE%9D2.0/%E5%B2%97%E4%BD%8D%E7%AE%A1%E7%90%86.png"/></td>
<td><img src="https://ruoyi-1256174062.cos.ap-guangzhou.myqcloud.com/%E8%8B%A5%E4%BE%9D2.0/%E5%B2%97%E4%BD%8D%E4%BF%AE%E6%94%B9.png"/></td>
</tr>
<tr>
<td><img src="https://ruoyi-1256174062.cos.ap-guangzhou.myqcloud.com/%E8%8B%A5%E4%BE%9D2.0/%E5%AD%97%E5%85%B8%E7%B1%BB%E5%9E%8B.png"/></td>
<td><img src="https://ruoyi-1256174062.cos.ap-guangzhou.myqcloud.com/%E8%8B%A5%E4%BE%9D2.0/%E5%AD%97%E5%85%B8%E6%95%B0%E6%8D%AE.png"/></td>
</tr>
<tr>
<td><img src="https://ruoyi-1256174062.cos.ap-guangzhou.myqcloud.com/%E8%8B%A5%E4%BE%9D2.0/%E5%8F%82%E6%95%B0%E8%AE%BE%E7%BD%AE.png"/></td>
<td><img src="https://ruoyi-1256174062.cos.ap-guangzhou.myqcloud.com/%E8%8B%A5%E4%BE%9D2.0/%E9%80%9A%E7%9F%A5%E7%AE%A1%E7%90%86.png"/></td>
</tr>
<tr>
<td><img src="https://ruoyi-1256174062.cos.ap-guangzhou.myqcloud.com/%E8%8B%A5%E4%BE%9D2.0/%E6%93%8D%E4%BD%9C%E6%97%A5%E5%BF%97.png"/></td>
<td><img src="https://ruoyi-1256174062.cos.ap-guangzhou.myqcloud.com/%E8%8B%A5%E4%BE%9D2.0/%E7%99%BB%E5%BD%95%E6%97%A5%E5%BF%97.png"/></td>
</tr>
<tr>
<td><img src="https://ruoyi-1256174062.cos.ap-guangzhou.myqcloud.com/%E8%8B%A5%E4%BE%9D2.0/%E5%AE%9A%E6%97%B6%E4%BB%BB%E5%8A%A1.png"/></td>
<td><img src="https://ruoyi-1256174062.cos.ap-guangzhou.myqcloud.com/%E8%8B%A5%E4%BE%9D2.0/%E5%AE%9A%E6%97%B6%E4%BB%BB%E5%8A%A1%E6%97%A5%E5%BF%97.png"/></td>
</tr>
<tr>
<td><img src="https://ruoyi-1256174062.cos.ap-guangzhou.myqcloud.com/%E8%8B%A5%E4%BE%9D2.0/%E5%9C%A8%E7%BA%BF%E7%94%A8%E6%88%B7.png"/></td>
<td><img src="https://ruoyi-1256174062.cos.ap-guangzhou.myqcloud.com/%E8%8B%A5%E4%BE%9D2.0/%E4%BB%A3%E7%A0%81%E7%94%9F%E6%88%90.png"/></td>
</tr>
<tr>
<td><img src="https://ruoyi-1256174062.cos.ap-guangzhou.myqcloud.com/%E8%8B%A5%E4%BE%9D2.0/%E6%95%B0%E6%8D%AE%E7%9B%91%E6%8E%A7.png"/></td>
<td><img src="https://ruoyi-1256174062.cos.ap-guangzhou.myqcloud.com/%E8%8B%A5%E4%BE%9D2.0/%E7%B3%BB%E7%BB%9F%E6%8E%A5%E5%8F%A3.png"/></td>
</tr>
</table>
## 若依交流群
QQ群 [![加入QQ群](https://img.shields.io/badge/QQ群-1389287-blue.svg)](http://shang.qq.com/wpa/qunwpa?idkey=4a9a52f5d9d9c65a8ea67859170ba835d95fc50ec74a2a722293e60e036b5016) 或 [![加入QQ群](https://img.shields.io/badge/QQ群-1389287-blue.svg)](https://jq.qq.com/?_wv=1027&k=5HBAaYN),推荐点击按钮入群,当然如果无法成功操作,请自行搜索群号`1389287`进行添加

Binary file not shown.

290
pom.xml Normal file
View File

@ -0,0 +1,290 @@
<?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>2.1.0</version>
<packaging>jar</packaging>
<name>RuoYi</name>
<description>若依管理系统</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.3.RELEASE</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>
<shiro.version>1.4.0</shiro.version>
<thymeleaf.extras.shiro.version>2.0.0</thymeleaf.extras.shiro.version>
<mybatis.spring.boot.starter.version>1.3.2</mybatis.spring.boot.starter.version>
<pagehelper.spring.boot.starter.version>1.2.5</pagehelper.spring.boot.starter.version>
<fastjson.version>1.2.47</fastjson.version>
<druid.version>1.1.10</druid.version>
<commons.io.version>2.5</commons.io.version>
<commons.fileupload.version>1.3.3</commons.fileupload.version>
<bitwalker.version>1.19</bitwalker.version>
<lombok.version>1.16.18</lombok.version>
<velocity.version>1.7</velocity.version>
<kaptcha.version>2.3.2</kaptcha.version>
<swagger.version>2.7.0</swagger.version>
<jsoup.version>1.11.3</jsoup.version>
</properties>
<dependencies>
<!-- SpringBoot 核心包 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<!--
<exclusions>
<exclusion>
<artifactId>spring-boot-starter-tomcat</artifactId>
<groupId>org.springframework.boot</groupId>
</exclusion>
</exclusions>
-->
</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>
<!-- thymeleaf网页解析 -->
<dependency>
<groupId>net.sourceforge.nekohtml</groupId>
<artifactId>nekohtml</artifactId>
</dependency>
<!-- Mysql驱动包 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- SpringBoot集成mybatis框架 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis.spring.boot.starter.version}</version>
</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</artifactId>
<version>${druid.version}</version>
</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>
<!--文件上传工具类 -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>${commons.fileupload.version}</version>
</dependency>
<!--Shiro核心框架 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>${shiro.version}</version>
</dependency>
<!-- Shiro使用Srping框架 -->
<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</artifactId>
<version>${velocity.version}</version>
</dependency>
<!--验证码 -->
<dependency>
<groupId>com.github.penggle</groupId>
<artifactId>kaptcha</artifactId>
<version>${kaptcha.version}</version>
<exclusions>
<exclusion>
<artifactId>javax.servlet-api</artifactId>
<groupId>javax.servlet</groupId>
</exclusion>
</exclusions>
</dependency>
<!-- swagger2-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${swagger.version}</version>
</dependency>
<!-- swagger2-UI-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>${swagger.version}</version>
</dependency>
<!-- HTML解析器 -->
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>${jsoup.version}</version>
</dependency>
<!-- excel工具 -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.9</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>
</plugins>
</build>
<repositories>
<repository>
<id>public</id>
<name>aliyun nexus</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<releases>
<enabled>true</enabled>
</releases>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>public</id>
<name>aliyun nexus</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
</project>

86
ry.sh Normal file
View File

@ -0,0 +1,86 @@
#!/bin/bash
AppName=RuoYi.jar
#JVM参数
JVM_OPTS="-Dname=$AppName -Duser.timezone=Asia/Shanghai -Xms512M -Xmx512M -XX:PermSize=256M -XX:MaxPermSize=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 -jar $JVM_OPTS target/$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

170
sql/quartz.sql Normal file
View File

@ -0,0 +1,170 @@
-- ----------------------------
-- 1、存储每一个已配置的 jobDetail 的详细信息
-- ----------------------------
drop table if exists QRTZ_JOB_DETAILS;
create table QRTZ_JOB_DETAILS (
sched_name varchar(120) not null,
job_name varchar(200) not null,
job_group varchar(200) not null,
description varchar(250) null,
job_class_name varchar(250) not null,
is_durable varchar(1) not null,
is_nonconcurrent varchar(1) not null,
is_update_data varchar(1) not null,
requests_recovery varchar(1) not null,
job_data blob null,
primary key (sched_name,job_name,job_group)
) engine=innodb default charset=utf8;
-- ----------------------------
-- 2、 存储已配置的 Trigger 的信息
-- ----------------------------
drop table if exists QRTZ_TRIGGERS;
create table QRTZ_TRIGGERS (
sched_name varchar(120) not null,
trigger_name varchar(200) not null,
trigger_group varchar(200) not null,
job_name varchar(200) not null,
job_group varchar(200) not null,
description varchar(250) null,
next_fire_time bigint(13) null,
prev_fire_time bigint(13) null,
priority integer null,
trigger_state varchar(16) not null,
trigger_type varchar(8) not null,
start_time bigint(13) not null,
end_time bigint(13) null,
calendar_name varchar(200) null,
misfire_instr smallint(2) null,
job_data blob null,
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 default charset=utf8;
-- ----------------------------
-- 3、 存储简单的 Trigger包括重复次数间隔以及已触发的次数
-- ----------------------------
drop table if exists QRTZ_SIMPLE_TRIGGERS;
create table QRTZ_SIMPLE_TRIGGERS (
sched_name varchar(120) not null,
trigger_name varchar(200) not null,
trigger_group varchar(200) not null,
repeat_count bigint(7) not null,
repeat_interval bigint(12) not null,
times_triggered bigint(10) not null,
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 default charset=utf8;
-- ----------------------------
-- 4、 存储 Cron Trigger包括 Cron 表达式和时区信息
-- ----------------------------
drop table if exists QRTZ_CRON_TRIGGERS;
create table QRTZ_CRON_TRIGGERS (
sched_name varchar(120) not null,
trigger_name varchar(200) not null,
trigger_group varchar(200) not null,
cron_expression varchar(200) not null,
time_zone_id varchar(80),
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 default charset=utf8;
-- ----------------------------
-- 5、 Trigger 作为 Blob 类型存储(用于 Quartz 用户用 JDBC 创建他们自己定制的 Trigger 类型JobStore 并不知道如何存储实例的时候)
-- ----------------------------
drop table if exists QRTZ_BLOB_TRIGGERS;
create table QRTZ_BLOB_TRIGGERS (
sched_name varchar(120) not null,
trigger_name varchar(200) not null,
trigger_group varchar(200) not null,
blob_data blob null,
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 default charset=utf8;
-- ----------------------------
-- 6、 以 Blob 类型存储存放日历信息, quartz可配置一个日历来指定一个时间范围
-- ----------------------------
drop table if exists QRTZ_CALENDARS;
create table QRTZ_CALENDARS (
sched_name varchar(120) not null,
calendar_name varchar(200) not null,
calendar blob not null,
primary key (sched_name,calendar_name)
) engine=innodb default charset=utf8;
-- ----------------------------
-- 7、 存储已暂停的 Trigger 组的信息
-- ----------------------------
drop table if exists QRTZ_PAUSED_TRIGGER_GRPS;
create table QRTZ_PAUSED_TRIGGER_GRPS (
sched_name varchar(120) not null,
trigger_group varchar(200) not null,
primary key (sched_name,trigger_group)
) engine=innodb default charset=utf8;
-- ----------------------------
-- 8、 存储与已触发的 Trigger 相关的状态信息,以及相联 Job 的执行信息
-- ----------------------------
drop table if exists QRTZ_FIRED_TRIGGERS;
create table QRTZ_FIRED_TRIGGERS (
sched_name varchar(120) not null,
entry_id varchar(95) not null,
trigger_name varchar(200) not null,
trigger_group varchar(200) not null,
instance_name varchar(200) not null,
fired_time bigint(13) not null,
sched_time bigint(13) not null,
priority integer not null,
state varchar(16) not null,
job_name varchar(200) null,
job_group varchar(200) null,
is_nonconcurrent varchar(1) null,
requests_recovery varchar(1) null,
primary key (sched_name,entry_id)
) engine=innodb default charset=utf8;
-- ----------------------------
-- 9、 存储少量的有关 Scheduler 的状态信息,假如是用于集群中,可以看到其他的 Scheduler 实例
-- ----------------------------
drop table if exists QRTZ_SCHEDULER_STATE;
create table QRTZ_SCHEDULER_STATE (
sched_name varchar(120) not null,
instance_name varchar(200) not null,
last_checkin_time bigint(13) not null,
checkin_interval bigint(13) not null,
primary key (sched_name,instance_name)
) engine=innodb default charset=utf8;
-- ----------------------------
-- 10、 存储程序的悲观锁的信息(假如使用了悲观锁)
-- ----------------------------
drop table if exists QRTZ_LOCKS;
create table QRTZ_LOCKS (
sched_name varchar(120) not null,
lock_name varchar(40) not null,
primary key (sched_name,lock_name)
) engine=innodb default charset=utf8;
drop table if exists QRTZ_SIMPROP_TRIGGERS;
create table QRTZ_SIMPROP_TRIGGERS (
sched_name varchar(120) not null,
trigger_name varchar(200) not null,
trigger_group varchar(200) not null,
str_prop_1 varchar(512) null,
str_prop_2 varchar(512) null,
str_prop_3 varchar(512) null,
int_prop_1 int null,
int_prop_2 int null,
long_prop_1 bigint null,
long_prop_2 bigint null,
dec_prop_1 numeric(13,4) null,
dec_prop_2 numeric(13,4) null,
bool_prop_1 varchar(1) null,
bool_prop_2 varchar(1) null,
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 default charset=utf8;
commit;

2890
sql/ruoyi.html Normal file

File diff suppressed because it is too large Load Diff

4068
sql/ruoyi.pdm Normal file

File diff suppressed because it is too large Load Diff

597
sql/ry_20180709.sql Normal file
View File

@ -0,0 +1,597 @@
-- ----------------------------
-- 1、部门表
-- ----------------------------
drop table if exists sys_dept;
create table sys_dept (
dept_id int(11) not null auto_increment comment '部门id',
parent_id int(11) default 0 comment '父部门id',
dept_name varchar(30) default '' comment '部门名称',
order_num int(4) default 0 comment '显示顺序',
leader varchar(20) default '' comment '负责人',
phone varchar(11) default '' comment '联系电话',
email varchar(50) 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 '更新时间',
primary key (dept_id)
) engine=innodb auto_increment=200 default charset=utf8 comment = '部门表';
-- ----------------------------
-- 初始化-部门表数据
-- ----------------------------
insert into sys_dept values(100, 0, '若依集团', 0, '若依', '15888888888', 'ry@qq.com', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00');
insert into sys_dept values(101, 100, '研发部门', 1, '若依', '15888888888', 'ry@qq.com', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00');
insert into sys_dept values(102, 100, '市场部门', 2, '若依', '15888888888', 'ry@qq.com', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00');
insert into sys_dept values(103, 100, '测试部门', 3, '若依', '15888888888', 'ry@qq.com', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00');
insert into sys_dept values(104, 100, '财务部门', 4, '若依', '15888888888', 'ry@qq.com', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00');
insert into sys_dept values(105, 100, '运维部门', 5, '若依', '15888888888', 'ry@qq.com', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00');
insert into sys_dept values(106, 101, '研发一部', 1, '若依', '15888888888', 'ry@qq.com', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00');
insert into sys_dept values(107, 101, '研发二部', 2, '若依', '15888888888', 'ry@qq.com', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00');
insert into sys_dept values(108, 102, '市场一部', 1, '若依', '15888888888', 'ry@qq.com', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00');
insert into sys_dept values(109, 102, '市场二部', 2, '若依', '15888888888', 'ry@qq.com', '1', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00');
-- ----------------------------
-- 2、用户信息表
-- ----------------------------
drop table if exists sys_user;
create table sys_user (
user_id int(11) not null auto_increment comment '用户ID',
dept_id int(11) default null comment '部门ID',
login_name varchar(30) not null comment '登录账号',
user_name varchar(30) not null comment '用户昵称',
user_type varchar(2) default '00' comment '用户类型00系统用户',
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(20) default '' comment '最后登陆IP',
login_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 '' comment '备注',
primary key (user_id)
) engine=innodb auto_increment=100 default charset=utf8 comment = '用户信息表';
-- ----------------------------
-- 初始化-用户信息表数据
-- ----------------------------
insert into sys_user values(1, 106, 'admin', '若依', '00', 'ry@163.com', '15888888888', '1', '', '29c67a30398638269fe600f73a054934', '111111', '0', '0', '127.0.0.1', '2018-03-16 11-33-00', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '管理员');
insert into sys_user values(2, 108, 'ry', '若依', '00', 'ry@qq.com', '15666666666', '1', '', '8e6d98b90472783cc73c17047ddccf36', '222222', '0', '0', '127.0.0.1', '2018-03-16 11-33-00', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '测试员');
-- ----------------------------
-- 3、岗位信息表
-- ----------------------------
drop table if exists sys_post;
create table sys_post
(
post_id int(11) 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 '' comment '备注',
primary key (post_id)
) engine=innodb default charset=utf8 comment = '岗位信息表';
-- ----------------------------
-- 初始化-岗位信息表数据
-- ----------------------------
insert into sys_post values(1, 'ceo', '董事长', 1, '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '');
insert into sys_post values(2, 'se', '项目经理', 2, '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '');
insert into sys_post values(3, 'hr', '人力资源', 3, '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '');
insert into sys_post values(4, 'user', '普通员工', 4, '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '');
-- ----------------------------
-- 4、角色信息表
-- ----------------------------
drop table if exists sys_role;
create table sys_role (
role_id int(11) 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 '显示顺序',
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 '' comment '备注',
primary key (role_id)
) engine=innodb auto_increment=100 default charset=utf8 comment = '角色信息表';
-- ----------------------------
-- 初始化-角色信息表数据
-- ----------------------------
insert into sys_role values('1', '管理员', 'admin', 1, '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '管理员');
insert into sys_role values('2', '普通角色', 'common', 2, '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '普通角色');
-- ----------------------------
-- 5、菜单权限表
-- ----------------------------
drop table if exists sys_menu;
create table sys_menu (
menu_id int(11) not null auto_increment comment '菜单ID',
menu_name varchar(50) not null comment '菜单名称',
parent_id int(11) default 0 comment '父菜单ID',
order_num int(4) default null comment '显示顺序',
url varchar(200) default '' comment '请求地址',
menu_type char(1) default '' comment '菜单类型M目录 C菜单 F按钮',
visible char(1) default 0 comment '菜单状态0显示 1隐藏',
perms varchar(100) default '' 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 default charset=utf8 comment = '菜单权限表';
-- ----------------------------
-- 初始化-菜单信息表数据
-- ----------------------------
-- 一级菜单
insert into sys_menu values('1', '系统管理', '0', '1', '#', 'M', '0', '', 'fa fa-gear', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '系统管理目录');
insert into sys_menu values('2', '系统监控', '0', '2', '#', 'M', '0', '', 'fa fa-video-camera', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '系统监控目录');
insert into sys_menu values('3', '系统工具', '0', '3', '#', 'M', '0', '', 'fa fa-bars', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '系统工具目录');
-- 二级菜单
insert into sys_menu values('100', '用户管理', '1', '1', '/system/user', 'C', '0', 'system:user:view', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '用户管理菜单');
insert into sys_menu values('101', '角色管理', '1', '2', '/system/role', 'C', '0', 'system:role:view', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '角色管理菜单');
insert into sys_menu values('102', '菜单管理', '1', '3', '/system/menu', 'C', '0', 'system:menu:view', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '菜单管理菜单');
insert into sys_menu values('103', '部门管理', '1', '4', '/system/dept', 'C', '0', 'system:dept:view', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '部门管理菜单');
insert into sys_menu values('104', '岗位管理', '1', '5', '/system/post', 'C', '0', 'system:post:view', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '岗位管理菜单');
insert into sys_menu values('105', '字典管理', '1', '6', '/system/dict', 'C', '0', 'system:dict:view', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '字典管理菜单');
insert into sys_menu values('106', '参数设置', '1', '7', '/system/config', 'C', '0', 'system:config:view', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '参数设置菜单');
insert into sys_menu values('107', '通知公告', '1', '8', '/system/notice', 'C', '0', 'system:notice:view', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '通知公告菜单');
insert into sys_menu values('108', '日志管理', '1', '9', '#', 'M', '0', '', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '日志管理菜单');
insert into sys_menu values('109', '在线用户', '2', '1', '/monitor/online', 'C', '0', 'monitor:online:view', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '在线用户菜单');
insert into sys_menu values('110', '定时任务', '2', '2', '/monitor/job', 'C', '0', 'monitor:job:view', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '定时任务菜单');
insert into sys_menu values('111', '数据监控', '2', '3', '/monitor/data', 'C', '0', 'monitor:data:view', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '数据监控菜单');
insert into sys_menu values('112', '表单构建', '3', '1', '/tool/build', 'C', '0', 'tool:build:view', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '表单构建菜单');
insert into sys_menu values('113', '代码生成', '3', '2', '/tool/gen', 'C', '0', 'tool:gen:view', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '代码生成菜单');
insert into sys_menu values('114', '系统接口', '3', '3', '/tool/swagger', 'C', '0', 'tool:swagger:view', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '系统接口菜单');
-- 三级菜单
insert into sys_menu values('500', '操作日志', '108', '1', '/monitor/operlog', 'C', '0', 'monitor:operlog:view', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '操作日志菜单');
insert into sys_menu values('501', '登录日志', '108', '2', '/monitor/logininfor', 'C', '0', 'monitor:logininfor:view', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '登录日志菜单');
-- 用户管理按钮
insert into sys_menu values('1000', '用户查询', '100', '1', '#', 'F', '0', 'system:user:list', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '');
insert into sys_menu values('1001', '用户新增', '100', '2', '#', 'F', '0', 'system:user:add', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '');
insert into sys_menu values('1002', '用户修改', '100', '3', '#', 'F', '0', 'system:user:edit', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '');
insert into sys_menu values('1003', '用户删除', '100', '4', '#', 'F', '0', 'system:user:remove', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '');
insert into sys_menu values('1004', '用户保存', '100', '5', '#', 'F', '0', 'system:user:save', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '');
insert into sys_menu values('1005', '重置密码', '100', '6', '#', 'F', '0', 'system:user:resetPwd', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '');
-- 角色管理按钮
insert into sys_menu values('1006', '角色查询', '101', '1', '#', 'F', '0', 'system:role:list', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '');
insert into sys_menu values('1007', '角色新增', '101', '2', '#', 'F', '0', 'system:role:add', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '');
insert into sys_menu values('1008', '角色修改', '101', '3', '#', 'F', '0', 'system:role:edit', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '');
insert into sys_menu values('1009', '角色删除', '101', '4', '#', 'F', '0', 'system:role:remove', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '');
insert into sys_menu values('1010', '角色保存', '101', '5', '#', 'F', '0', 'system:role:save', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '');
-- 菜单管理按钮
insert into sys_menu values('1011', '菜单查询', '102', '1', '#', 'F', '0', 'system:menu:list', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '');
insert into sys_menu values('1012', '菜单新增', '102', '2', '#', 'F', '0', 'system:menu:add', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '');
insert into sys_menu values('1013', '菜单修改', '102', '3', '#', 'F', '0', 'system:menu:edit', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '');
insert into sys_menu values('1014', '菜单删除', '102', '4', '#', 'F', '0', 'system:menu:remove', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '');
insert into sys_menu values('1015', '菜单保存', '102', '5', '#', 'F', '0', 'system:menu:save', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '');
-- 部门管理按钮
insert into sys_menu values('1016', '部门查询', '103', '1', '#', 'F', '0', 'system:dept:list', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '');
insert into sys_menu values('1017', '部门新增', '103', '2', '#', 'F', '0', 'system:dept:add', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '');
insert into sys_menu values('1018', '部门修改', '103', '3', '#', 'F', '0', 'system:dept:edit', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '');
insert into sys_menu values('1019', '部门删除', '103', '4', '#', 'F', '0', 'system:dept:remove', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '');
insert into sys_menu values('1020', '部门保存', '103', '5', '#', 'F', '0', 'system:dept:save', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '');
-- 岗位管理按钮
insert into sys_menu values('1021', '岗位查询', '104', '1', '#', 'F', '0', 'system:post:list', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '');
insert into sys_menu values('1022', '岗位新增', '104', '2', '#', 'F', '0', 'system:post:add', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '');
insert into sys_menu values('1023', '岗位修改', '104', '3', '#', 'F', '0', 'system:post:edit', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '');
insert into sys_menu values('1024', '岗位删除', '104', '4', '#', 'F', '0', 'system:post:remove', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '');
insert into sys_menu values('1025', '岗位保存', '104', '5', '#', 'F', '0', 'system:post:save', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '');
-- 字典管理按钮
insert into sys_menu values('1026', '字典查询', '105', '1', '#', 'F', '0', 'system:dict:list', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '');
insert into sys_menu values('1027', '字典新增', '105', '2', '#', 'F', '0', 'system:dict:add', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '');
insert into sys_menu values('1028', '字典修改', '105', '3', '#', 'F', '0', 'system:dict:edit', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '');
insert into sys_menu values('1029', '字典删除', '105', '4', '#', 'F', '0', 'system:dict:remove', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '');
insert into sys_menu values('1030', '字典保存', '105', '5', '#', 'F', '0', 'system:dict:save', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '');
-- 参数设置按钮
insert into sys_menu values('1031', '参数查询', '106', '1', '#', 'F', '0', 'system:config:list', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '');
insert into sys_menu values('1032', '参数新增', '106', '2', '#', 'F', '0', 'system:config:add', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '');
insert into sys_menu values('1033', '参数修改', '106', '3', '#', 'F', '0', 'system:config:edit', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '');
insert into sys_menu values('1034', '参数删除', '106', '4', '#', 'F', '0', 'system:config:remove', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '');
insert into sys_menu values('1035', '参数保存', '106', '5', '#', 'F', '0', 'system:config:save', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '');
-- 通知公告按钮
insert into sys_menu values('1036', '公告查询', '107', '1', '#', 'F', '0', 'system:notice:list', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '');
insert into sys_menu values('1037', '公告新增', '107', '2', '#', 'F', '0', 'system:notice:add', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '');
insert into sys_menu values('1038', '公告修改', '107', '3', '#', 'F', '0', 'system:notice:edit', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '');
insert into sys_menu values('1039', '公告删除', '107', '4', '#', 'F', '0', 'system:notice:remove', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '');
insert into sys_menu values('1040', '公告保存', '107', '5', '#', 'F', '0', 'system:notice:save', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '');
-- 操作日志按钮
insert into sys_menu values('1041', '操作查询', '500', '1', '#', 'F', '0', 'monitor:operlog:list', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '');
insert into sys_menu values('1042', '操作删除', '500', '2', '#', 'F', '0', 'monitor:operlog:remove', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '');
insert into sys_menu values('1043', '详细信息', '500', '3', '#', 'F', '0', 'monitor:operlog:detail', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '');
-- 登录日志按钮
insert into sys_menu values('1044', '登录查询', '501', '1', '#', 'F', '0', 'monitor:logininfor:list', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '');
insert into sys_menu values('1045', '登录删除', '501', '2', '#', 'F', '0', 'monitor:logininfor:remove', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '');
-- 在线用户按钮
insert into sys_menu values('1046', '在线查询', '109', '1', '#', 'F', '0', 'monitor:online:list', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '');
insert into sys_menu values('1047', '批量强退', '109', '2', '#', 'F', '0', 'monitor:online:batchForceLogout', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '');
insert into sys_menu values('1048', '单条强退', '109', '3', '#', 'F', '0', 'monitor:online:forceLogout', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '');
-- 定时任务按钮
insert into sys_menu values('1049', '任务查询', '110', '1', '#', 'F', '0', 'monitor:job:list', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '');
insert into sys_menu values('1050', '任务新增', '110', '2', '#', 'F', '0', 'monitor:job:add', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '');
insert into sys_menu values('1051', '任务修改', '110', '3', '#', 'F', '0', 'monitor:job:edit', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '');
insert into sys_menu values('1052', '任务删除', '110', '4', '#', 'F', '0', 'monitor:job:remove', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '');
insert into sys_menu values('1053', '任务保存', '110', '5', '#', 'F', '0', 'monitor:job:save', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '');
insert into sys_menu values('1054', '状态修改', '110', '6', '#', 'F', '0', 'monitor:job:changeStatus', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '');
-- 代码生成按钮
insert into sys_menu values('1055', '生成查询', '113', '1', '#', 'F', '0', 'tool:gen:list', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '');
insert into sys_menu values('1056', '生成代码', '113', '2', '#', 'F', '0', 'tool:gen:code', '#', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '');
-- ----------------------------
-- 6、用户和角色关联表 用户N-1角色
-- ----------------------------
drop table if exists sys_user_role;
create table sys_user_role (
user_id int(11) not null comment '用户ID',
role_id int(11) not null comment '角色ID',
primary key(user_id, role_id)
) engine=innodb default charset=utf8 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 int(11) not null comment '角色ID',
menu_id int(11) not null comment '菜单ID',
primary key(role_id, menu_id)
) engine=innodb default charset=utf8 comment = '角色和菜单关联表';
-- ----------------------------
-- 初始化-角色和菜单关联表数据
-- ----------------------------
insert into sys_role_menu values ('1', '1');
insert into sys_role_menu values ('1', '2');
insert into sys_role_menu values ('1', '3');
insert into sys_role_menu values ('1', '100');
insert into sys_role_menu values ('1', '101');
insert into sys_role_menu values ('1', '102');
insert into sys_role_menu values ('1', '103');
insert into sys_role_menu values ('1', '104');
insert into sys_role_menu values ('1', '105');
insert into sys_role_menu values ('1', '106');
insert into sys_role_menu values ('1', '107');
insert into sys_role_menu values ('1', '108');
insert into sys_role_menu values ('1', '109');
insert into sys_role_menu values ('1', '110');
insert into sys_role_menu values ('1', '111');
insert into sys_role_menu values ('1', '112');
insert into sys_role_menu values ('1', '113');
insert into sys_role_menu values ('1', '114');
insert into sys_role_menu values ('1', '500');
insert into sys_role_menu values ('1', '501');
insert into sys_role_menu values ('1', '1000');
insert into sys_role_menu values ('1', '1001');
insert into sys_role_menu values ('1', '1002');
insert into sys_role_menu values ('1', '1003');
insert into sys_role_menu values ('1', '1004');
insert into sys_role_menu values ('1', '1005');
insert into sys_role_menu values ('1', '1006');
insert into sys_role_menu values ('1', '1007');
insert into sys_role_menu values ('1', '1008');
insert into sys_role_menu values ('1', '1009');
insert into sys_role_menu values ('1', '1010');
insert into sys_role_menu values ('1', '1011');
insert into sys_role_menu values ('1', '1012');
insert into sys_role_menu values ('1', '1013');
insert into sys_role_menu values ('1', '1014');
insert into sys_role_menu values ('1', '1015');
insert into sys_role_menu values ('1', '1016');
insert into sys_role_menu values ('1', '1017');
insert into sys_role_menu values ('1', '1018');
insert into sys_role_menu values ('1', '1019');
insert into sys_role_menu values ('1', '1020');
insert into sys_role_menu values ('1', '1021');
insert into sys_role_menu values ('1', '1022');
insert into sys_role_menu values ('1', '1023');
insert into sys_role_menu values ('1', '1024');
insert into sys_role_menu values ('1', '1025');
insert into sys_role_menu values ('1', '1026');
insert into sys_role_menu values ('1', '1027');
insert into sys_role_menu values ('1', '1028');
insert into sys_role_menu values ('1', '1029');
insert into sys_role_menu values ('1', '1030');
insert into sys_role_menu values ('1', '1031');
insert into sys_role_menu values ('1', '1032');
insert into sys_role_menu values ('1', '1033');
insert into sys_role_menu values ('1', '1034');
insert into sys_role_menu values ('1', '1035');
insert into sys_role_menu values ('1', '1036');
insert into sys_role_menu values ('1', '1037');
insert into sys_role_menu values ('1', '1038');
insert into sys_role_menu values ('1', '1039');
insert into sys_role_menu values ('1', '1040');
insert into sys_role_menu values ('1', '1041');
insert into sys_role_menu values ('1', '1042');
insert into sys_role_menu values ('1', '1043');
insert into sys_role_menu values ('1', '1044');
insert into sys_role_menu values ('1', '1045');
insert into sys_role_menu values ('1', '1046');
insert into sys_role_menu values ('1', '1047');
insert into sys_role_menu values ('1', '1048');
insert into sys_role_menu values ('1', '1049');
insert into sys_role_menu values ('1', '1050');
insert into sys_role_menu values ('1', '1051');
insert into sys_role_menu values ('1', '1052');
insert into sys_role_menu values ('1', '1053');
insert into sys_role_menu values ('1', '1054');
insert into sys_role_menu values ('1', '1055');
insert into sys_role_menu values ('1', '1056');
-- ----------------------------
-- 8、用户与岗位关联表 用户1-N岗位
-- ----------------------------
drop table if exists sys_user_post;
create table sys_user_post
(
user_id varchar(64) not null comment '用户ID',
post_id varchar(64) not null comment '岗位ID',
primary key (user_id, post_id)
) engine=innodb default charset=utf8 comment = '用户与岗位关联表';
-- ----------------------------
-- 初始化-用户与岗位关联表数据
-- ----------------------------
insert into sys_user_post values ('1', '1');
insert into sys_user_post values ('2', '2');
-- ----------------------------
-- 9、操作日志记录
-- ----------------------------
drop table if exists sys_oper_log;
create table sys_oper_log (
oper_id int(11) not null auto_increment comment '日志主键',
title varchar(50) default '' comment '模块标题',
action varchar(100) default '' comment '功能请求',
method varchar(100) default '' comment '方法名称',
channel varchar(20) default '' comment '来源渠道manage后台用户 mobile手机端用户 other其它',
oper_name varchar(50) default '' comment '操作人员',
dept_name varchar(50) default '' comment '部门名称',
oper_url varchar(255) default '' comment '请求URL',
oper_ip varchar(30) default '' comment '主机地址',
oper_location varchar(255) default '' comment '操作地点',
oper_param varchar(255) default '' comment '请求参数',
status char(1) default '0' comment '操作状态0正常 1异常',
error_msg varchar(2000) default '' comment '错误消息',
oper_time datetime comment '操作时间',
primary key (oper_id)
) engine=innodb auto_increment=100 default charset=utf8 comment = '操作日志记录';
-- ----------------------------
-- 10、字典类型表
-- ----------------------------
drop table if exists sys_dict_type;
create table sys_dict_type
(
dict_id int(11) 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 '' comment '备注',
primary key (dict_id),
unique (dict_type)
) engine=innodb auto_increment=100 default charset=utf8 comment = '字典类型表';
insert into sys_dict_type values(1, '用户性别', 'sys_user_sex', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '用户性别列表');
insert into sys_dict_type values(2, '菜单状态', 'sys_show_hide', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '菜单状态列表');
insert into sys_dict_type values(3, '系统开关', 'sys_normal_disable', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '系统开关列表');
insert into sys_dict_type values(4, '任务状态', 'sys_job_status', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '任务状态列表');
insert into sys_dict_type values(5, '系统是否', 'sys_yes_no', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '系统是否列表');
insert into sys_dict_type values(6, '通知类型', 'sys_notice_type', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '通知类型列表');
insert into sys_dict_type values(7, '通知状态', 'sys_notice_status', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '通知状态列表');
insert into sys_dict_type values(8, '操作类型', 'sys_oper_type', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '操作类型列表');
insert into sys_dict_type values(9, '系统状态', 'sys_common_status', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '登录状态列表');
-- ----------------------------
-- 11、字典数据表
-- ----------------------------
drop table if exists sys_dict_data;
create table sys_dict_data
(
dict_code int(11) 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(500) default '' 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 '' comment '备注',
primary key (dict_code)
) engine=innodb auto_increment=100 default charset=utf8 comment = '字典数据表';
insert into sys_dict_data values(1, 1, '', '0', 'sys_user_sex', '', 'Y', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '性别男');
insert into sys_dict_data values(2, 2, '', '1', 'sys_user_sex', '', 'N', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '性别女');
insert into sys_dict_data values(3, 3, '未知', '2', 'sys_user_sex', '', 'N', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '性别未知');
insert into sys_dict_data values(4, 1, '显示', '0', 'sys_show_hide', 'radio radio-info radio-inline', 'Y', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '显示菜单');
insert into sys_dict_data values(5, 2, '隐藏', '1', 'sys_show_hide', 'radio radio-danger radio-inline', 'N', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '隐藏菜单');
insert into sys_dict_data values(6, 1, '正常', '0', 'sys_normal_disable', 'radio radio-info radio-inline', 'Y', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '正常状态');
insert into sys_dict_data values(7, 2, '停用', '1', 'sys_normal_disable', 'radio radio-danger radio-inline', 'N', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '停用状态');
insert into sys_dict_data values(8, 1, '正常', '0', 'sys_job_status', 'radio radio-info radio-inline', 'Y', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '正常状态');
insert into sys_dict_data values(9, 2, '暂停', '1', 'sys_job_status', 'radio radio-danger radio-inline', 'N', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '停用状态');
insert into sys_dict_data values(10, 1, '', 'Y', 'sys_yes_no', 'radio radio-info radio-inline', 'Y', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '系统默认是');
insert into sys_dict_data values(11, 2, '', 'N', 'sys_yes_no', 'radio radio-danger radio-inline', 'N', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '系统默认否');
insert into sys_dict_data values(12, 1, '通知', '1', 'sys_notice_type', '', 'Y', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '通知');
insert into sys_dict_data values(13, 2, '公告', '2', 'sys_notice_type', '', 'N', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '公告');
insert into sys_dict_data values(14, 1, '正常', '0', 'sys_notice_status', 'radio radio-info radio-inline', 'Y', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '正常状态');
insert into sys_dict_data values(15, 2, '关闭', '1', 'sys_notice_status', 'radio radio-danger radio-inline', 'N', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '关闭状态');
insert into sys_dict_data values(16, 1, '新增', '1', 'sys_oper_type', '', 'N', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '新增操作');
insert into sys_dict_data values(17, 2, '修改', '2', 'sys_oper_type', '', 'N', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '新增操作');
insert into sys_dict_data values(18, 3, '保存', '3', 'sys_oper_type', '', 'N', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '新增操作');
insert into sys_dict_data values(19, 4, '删除', '4', 'sys_oper_type', '', 'N', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '新增操作');
insert into sys_dict_data values(20, 5, '授权', '5', 'sys_oper_type', '', 'N', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '新增操作');
insert into sys_dict_data values(21, 6, '导出', '6', 'sys_oper_type', '', 'N', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '新增操作');
insert into sys_dict_data values(22, 7, '导入', '7', 'sys_oper_type', '', 'N', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '新增操作');
insert into sys_dict_data values(23, 8, '强退', '8', 'sys_oper_type', '', 'N', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '新增操作');
insert into sys_dict_data values(24, 9, '禁止访问', '9', 'sys_oper_type', '', 'N', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '新增操作');
insert into sys_dict_data values(25, 10, '生成代码', '10', 'sys_oper_type', '', 'N', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '新增操作');
insert into sys_dict_data values(26, 1, '成功', '0', 'sys_common_status', '', 'N', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '正常状态');
insert into sys_dict_data values(27, 2, '失败', '1', 'sys_common_status', '', 'N', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '停用状态');
-- ----------------------------
-- 12、参数配置表
-- ----------------------------
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(100) 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 '' comment '备注',
primary key (config_id)
) engine=innodb auto_increment=100 default charset=utf8 comment = '参数配置表';
insert into sys_config values(1, '主框架页-默认皮肤样式名称', 'sys.index.skinName', 'skin-default', 'Y', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '默认 skin-default、蓝色 skin-blue、黄色 skin-yellow' );
insert into sys_config values(2, '用户管理-账号初始密码', 'sys.user.initPassword', '123456', 'Y', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '初始化密码 123456' );
-- ----------------------------
-- 13、系统访问记录
-- ----------------------------
drop table if exists sys_logininfor;
create table sys_logininfor (
info_id int(11) not null auto_increment comment '访问ID',
login_name varchar(50) default '' comment '登录账号',
ipaddr varchar(50) 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)
) engine=innodb auto_increment=100 default charset=utf8 comment = '系统访问记录';
-- ----------------------------
-- 14、在线用户记录
-- ----------------------------
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(50) 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_timestsamp datetime comment 'session创建时间',
last_access_time datetime comment 'session最后访问时间',
expire_time int(5) default 0 comment '超时时间,单位为分钟',
primary key (sessionId)
) engine=innodb default charset=utf8 comment = '在线用户记录';
-- ----------------------------
-- 15、定时任务调度表
-- ----------------------------
drop table if exists sys_job;
create table sys_job (
job_id int(11) not null auto_increment comment '任务ID',
job_name varchar(64) default '' comment '任务名称',
job_group varchar(64) default '' comment '任务组名',
method_name varchar(500) default '' comment '任务方法',
params varchar(200) default '' comment '方法参数',
cron_expression varchar(255) default '' comment 'cron执行表达式',
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 default charset=utf8 comment = '定时任务调度表';
insert into sys_job values(1, 'ryTask', '系统默认(无参)', 'ryNoParams', '', '0/10 * * * * ?', '1', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '');
insert into sys_job values(2, 'ryTask', '系统默认(有参)', 'ryParams', 'ry', '0/20 * * * * ?', '1', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '');
-- ----------------------------
-- 16、定时任务调度日志表
-- ----------------------------
drop table if exists sys_job_log;
create table sys_job_log (
job_log_id int(11) not null auto_increment comment '任务日志ID',
job_name varchar(64) not null comment '任务名称',
job_group varchar(64) not null comment '任务组名',
method_name varchar(500) comment '任务方法',
params varchar(200) default '' comment '方法参数',
job_message varchar(500) comment '日志信息',
status char(1) default '0' comment '执行状态0正常 1失败',
exception_info text comment '异常信息',
create_time datetime comment '创建时间',
primary key (job_log_id)
) engine=innodb default charset=utf8 comment = '定时任务调度日志表';
-- ----------------------------
-- 17、通知公告表
-- ----------------------------
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(2) not null comment '公告类型1通知 2公告',
notice_content varchar(500) not 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 '' comment '备注',
primary key (notice_id)
) engine=innodb auto_increment=10 default charset=utf8 comment = '通知公告表';
-- ----------------------------
-- 初始化-公告信息表数据
-- ----------------------------
insert into sys_notice values('1', '温馨提醒2018-07-01 若依新版本发布啦', '2', '新版本内容', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '管理员');
insert into sys_notice values('2', '维护通知2018-07-01 若依系统凌晨维护', '1', '维护内容', '0', 'admin', '2018-03-16 11-33-00', 'ry', '2018-03-16 11-33-00', '管理员');

View File

@ -0,0 +1,33 @@
package com.ruoyi;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.transaction.annotation.EnableTransactionManagement;
/**
* 启动程序
*
* @author ruoyi
*/
@SpringBootApplication
@EnableTransactionManagement
@MapperScan("com.ruoyi.project.*.*.mapper")
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" +
" ''-' `'-' `-..-' ");
}
}

View File

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

View File

@ -0,0 +1,46 @@
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("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("tinytext", "String");
javaTypeMap.put("text", "String");
javaTypeMap.put("mediumtext", "String");
javaTypeMap.put("longtext", "String");
javaTypeMap.put("date", "Date");
javaTypeMap.put("datetime", "Date");
javaTypeMap.put("timestamp", "Date");
}
}

View File

@ -0,0 +1,65 @@
package com.ruoyi.common.constant;
/**
* 通用常量信息
*
* @author ruoyi
*/
public class Constants
{
/**
* UTF-8 字符集
*/
public static final String UTF8 = "UTF-8";
/**
* 通用成功标识
*/
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 LOGIN_FAIL = "Error";
/**
* 自动去除表前缀
*/
public static String AUTO_REOMVE_PRE = "true";
/**
* 当前记录起始索引
*/
public static String PAGENUM = "pageNum";
/**
* 每页显示记录数
*/
public static String PAGESIZE = "pageSize";
/**
* 排序列
*/
public static String ORDERBYCOLUMN = "orderByColumn";
/**
* 排序的方向 "desc" 或者 "asc".
*/
public static String ISASC = "isAsc";
}

View File

@ -0,0 +1,39 @@
package com.ruoyi.common.constant;
/**
* 任务调度通用常量
*
* @author ruoyi
*/
public interface ScheduleConstants
{
/**
* 任务调度参数key
*/
public static final String JOB_PARAM_KEY = "JOB_PARAM_KEY";
public enum Status
{
/**
* 正常
*/
NORMAL("0"),
/**
* 暂停
*/
PAUSE("1");
private String value;
private Status(String value)
{
this.value = value;
}
public String getValue()
{
return value;
}
}
}

View File

@ -0,0 +1,65 @@
package com.ruoyi.common.constant;
/**
* Shiro通用常量
*
* @author ruoyi
*/
public interface ShiroConstants
{
/**
* 当前登录的用户
*/
public static final String CURRENT_USER = "currentUser";
/**
* 用户名
*/
public static final String CURRENT_USERNAME = "username";
/**
* 消息key
*/
public static String MESSAGE = "message";
/**
* 错误key
*/
public static String ERROR = "errorMsg";
/**
* 编码格式
*/
public static String ENCODING = "UTF-8";
/**
* 当前在线会话
*/
public String ONLINE_SESSION = "online_session";
/**
* 验证码key
*/
public static final String CURRENT_CAPTCHA = "captcha";
/**
* 验证码开关
*/
public static final String CURRENT_EBABLED = "captchaEbabled";
/**
* 验证码开关
*/
public static final String CURRENT_TYPE = "captchaType";
/**
* 验证码
*/
public static final String CURRENT_VALIDATECODE = "validateCode";
/**
* 验证码错误
*/
public static final String CAPTCHA_ERROR = "captchaError";
}

View File

@ -0,0 +1,80 @@
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_BLOCKED = "1";
/** 角色封禁状态 */
public static final String ROLE_BLOCKED = "1";
/** 部门正常状态 */
public static final String DEPT_NORMAL = "0";
/**
* 用户名长度限制
*/
public static final int USERNAME_MIN_LENGTH = 2;
public static final int USERNAME_MAX_LENGTH = 20;
/** 登录名称是否唯一的返回结果码 */
public final static String USER_NAME_UNIQUE = "0";
public final static String USER_NAME_NOT_UNIQUE = "1";
/** 手机号码是否唯一的返回结果 */
public final static String USER_PHONE_UNIQUE = "0";
public final static String USER_PHONE_NOT_UNIQUE = "1";
/** e-mail 是否唯一的返回结果 */
public final static String USER_EMAIL_UNIQUE = "0";
public final static String USER_EMAIL_NOT_UNIQUE = "1";
/** 部门名称是否唯一的返回结果码 */
public final static String DEPT_NAME_UNIQUE = "0";
public final static String DEPT_NAME_NOT_UNIQUE = "1";
/** 角色名称是否唯一的返回结果码 */
public final static String ROLE_NAME_UNIQUE = "0";
public final static String ROLE_NAME_NOT_UNIQUE = "1";
/** 菜单名称是否唯一的返回结果码 */
public final static String MENU_NAME_UNIQUE = "0";
public final static String MENU_NAME_NOT_UNIQUE = "1";
/** 字典类型是否唯一的返回结果码 */
public final static String DICT_TYPE_UNIQUE = "0";
public final static String DICT_TYPE_NOT_UNIQUE = "1";
/** 参数键名是否唯一的返回结果码 */
public final static String CONFIG_KEY_UNIQUE = "0";
public final static String CONFIG_KEY_NOT_UNIQUE = "1";
/**
* 密码长度限制
*/
public static final int PASSWORD_MIN_LENGTH = 5;
public static final int PASSWORD_MAX_LENGTH = 20;
/**
* 手机号码格式限制
*/
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])))\\.?";
}

View File

@ -0,0 +1,15 @@
package com.ruoyi.common.exception;
/**
* 演示模式异常
*
* @author ruoyi
*/
public class DemoModeException extends RuntimeException
{
private static final long serialVersionUID = 1L;
public DemoModeException()
{
}
}

View File

@ -0,0 +1,105 @@
package com.ruoyi.common.exception.base;
import org.springframework.util.StringUtils;
import com.ruoyi.common.utils.MessageUtils;
/**
* 基础异常
*
* @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() + '\'' + '}';
}
}

View File

@ -0,0 +1,41 @@
package com.ruoyi.common.exception.file;
import org.apache.commons.fileupload.FileUploadException;
/**
* 文件名超长 误异常类
*
* @author ruoyi
*/
public class FileNameLengthLimitExceededException extends FileUploadException
{
private static final long serialVersionUID = 1L;
private int length;
private int maxLength;
private String filename;
public FileNameLengthLimitExceededException(String filename, int length, int maxLength)
{
super("file name : [" + filename + "], length : [" + length + "], max length : [" + maxLength + "]");
this.length = length;
this.maxLength = maxLength;
this.filename = filename;
}
public String getFilename()
{
return filename;
}
public int getLength()
{
return length;
}
public int getMaxLength()
{
return maxLength;
}
}

View File

@ -0,0 +1,74 @@
package com.ruoyi.common.exception.file;
import java.util.Arrays;
import org.apache.commons.fileupload.FileUploadException;
/**
* 文件上传 误异常类
*
* @author ruoyi
*/
public class InvalidExtensionException extends FileUploadException
{
private static final long serialVersionUID = 1L;
private String[] allowedExtension;
private String extension;
private String filename;
public InvalidExtensionException(String[] allowedExtension, String extension, String filename)
{
super("filename : [" + filename + "], extension : [" + extension + "], allowed extension : ["
+ Arrays.toString(allowedExtension) + "]");
this.allowedExtension = allowedExtension;
this.extension = extension;
this.filename = filename;
}
public String[] getAllowedExtension()
{
return allowedExtension;
}
public String getExtension()
{
return extension;
}
public String getFilename()
{
return filename;
}
public static class InvalidImageExtensionException extends InvalidExtensionException
{
private static final long serialVersionUID = 1L;
public InvalidImageExtensionException(String[] allowedExtension, String extension, String filename)
{
super(allowedExtension, extension, filename);
}
}
public static class InvalidFlashExtensionException extends InvalidExtensionException
{
private static final long serialVersionUID = 1L;
public InvalidFlashExtensionException(String[] allowedExtension, String extension, String filename)
{
super(allowedExtension, extension, filename);
}
}
public static class InvalidMediaExtensionException extends InvalidExtensionException
{
private static final long serialVersionUID = 1L;
public InvalidMediaExtensionException(String[] allowedExtension, String extension, String filename)
{
super(allowedExtension, extension, filename);
}
}
}

View File

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

View File

@ -0,0 +1,18 @@
package com.ruoyi.common.exception.user;
/**
* 角色锁定异常类
*
* @author ruoyi
*/
public class RoleBlockedException extends UserException
{
private static final long serialVersionUID = 1L;
public RoleBlockedException(String reason)
{
super("role.blocked", new Object[] { reason });
}
}

View File

@ -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(String reason)
{
super("user.blocked", new Object[] { reason });
}
}

View File

@ -0,0 +1,20 @@
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);
}
}

View File

@ -0,0 +1,17 @@
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);
}
}

View File

@ -0,0 +1,17 @@
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);
}
}

View File

@ -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, String password)
{
super("user.password.retry.limit.count", new Object[] { retryLimitCount, password });
}
}

View File

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

View File

@ -0,0 +1,88 @@
package com.ruoyi.common.support;
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)
{
srcCharset = 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

View File

@ -0,0 +1,95 @@
package com.ruoyi.common.support;
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();
}
}

View File

@ -0,0 +1,96 @@
package com.ruoyi.common.utils;
import com.alibaba.fastjson.JSONObject;
import com.ruoyi.common.constant.Constants;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 获取地址类
*
* @author ruoyi
*/
public class AddressUtils
{
private static final Logger log = LoggerFactory.getLogger(AddressUtils.class);
public static final String IP_URL = "http://ip.taobao.com/service/getIpInfo.php";
/**
* 获取查询结果
*
* @param urlStr
* @param content
* @param encoding
* @return
*/
private static String sendPost(String content, String encoding)
{
URL url = null;
HttpURLConnection connection = null;
try
{
url = new URL(IP_URL);
connection = (HttpURLConnection) url.openConnection();
connection.setConnectTimeout(2000);
connection.setReadTimeout(2000);
connection.setDoOutput(true);
connection.setDoInput(true);
connection.setRequestMethod("POST");
connection.setUseCaches(false);
connection.connect();
DataOutputStream out = new DataOutputStream(connection.getOutputStream());
out.writeBytes(content);
out.flush();
out.close();
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream(), encoding));
StringBuffer buffer = new StringBuffer();
String line = "";
while ((line = reader.readLine()) != null)
{
buffer.append(line);
}
reader.close();
return buffer.toString();
}
catch (IOException e)
{
log.error("温馨提醒:您的主机已经断网,请您检查主机的网络连接");
log.error("根据IP获取所在位置----------错误消息:" + e.getMessage());
}
finally
{
if (connection != null)
{
connection.disconnect();
}
}
return null;
}
public static String getRealAddressByIP(String ip)
{
String address = "";
try
{
address = sendPost("ip=" + ip, Constants.UTF8);
JSONObject json = JSONObject.parseObject(address);
JSONObject object = json.getObject("data", JSONObject.class);
String region = object.getString("region");
String city = object.getString("city");
address = region + " " + city;
}
catch (Exception e)
{
log.error("根据IP获取所在位置----------错误消息:" + e.getMessage());
}
return address;
}
}

View File

@ -0,0 +1,100 @@
package com.ruoyi.common.utils;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.apache.commons.lang3.time.DateFormatUtils;
/**
* 时间工具类
*
* @author ruoyi
*/
public class 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";
/**
* 获取当前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");
}
}

View File

@ -0,0 +1,155 @@
package com.ruoyi.common.utils;
import java.io.File;
import java.io.IOException;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.tomcat.util.http.fileupload.FileUploadBase.FileSizeLimitExceededException;
import org.springframework.web.multipart.MultipartFile;
import com.ruoyi.common.exception.file.FileNameLengthLimitExceededException;
import com.ruoyi.framework.config.RuoYiConfig;
/**
* 文件上传工具类
*
* @author ruoyi
*/
public class FileUploadUtils
{
// 默认大小 50M
public static final long DEFAULT_MAX_SIZE = 52428800;
// 默认上传的地址
private static String defaultBaseDir = RuoYiConfig.getProfile();
// 默认的文件名最大长度
public static final int DEFAULT_FILE_NAME_LENGTH = 200;
// 默认文件类型jpg
public static final String IMAGE_JPG_EXTENSION = ".jpg";
private static int counter = 0;
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, FileUploadUtils.IMAGE_JPG_EXTENSION);
}
catch (Exception e)
{
throw new IOException(e);
}
}
/**
* 根据文件路径上传
*
* @param baseDir 相对应用的基目录
* @param file 上传的文件
* @return 文件名称
* @throws IOException
*/
public static final String upload(String baseDir, MultipartFile file) throws IOException
{
try
{
return upload(baseDir, file, FileUploadUtils.IMAGE_JPG_EXTENSION);
}
catch (Exception e)
{
throw new IOException(e);
}
}
/**
* 文件上传
*
* @param baseDir 相对应用的基目录
* @param file 上传的文件
* @param needDatePathAndRandomName 是否需要日期目录和随机文件名前缀
* @param extension 上传文件类型
* @return 返回上传成功的文件名
* @throws FileSizeLimitExceededException 如果超出最大大小
* @throws FileNameLengthLimitExceededException 文件名太长
* @throws IOException 比如读写文件出错时
*/
public static final String upload(String baseDir, MultipartFile file, String extension)
throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException
{
int fileNamelength = file.getOriginalFilename().length();
if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH)
{
throw new FileNameLengthLimitExceededException(file.getOriginalFilename(), fileNamelength,
FileUploadUtils.DEFAULT_FILE_NAME_LENGTH);
}
assertAllowed(file);
String fileName = encodingFilename(file.getOriginalFilename(), extension);
File desc = getAbsoluteFile(baseDir, baseDir + fileName);
file.transferTo(desc);
return fileName;
}
private static final File getAbsoluteFile(String uploadDir, String filename) throws IOException
{
File desc = new File(File.separator + filename);
if (!desc.getParentFile().exists())
{
desc.getParentFile().mkdirs();
}
if (!desc.exists())
{
desc.createNewFile();
}
return desc;
}
/**
* 编码文件名
*/
private static final String encodingFilename(String filename, String extension)
{
filename = filename.replace("_", " ");
filename = new Md5Hash(filename + System.nanoTime() + counter++).toHex().toString() + extension;
return filename;
}
/**
* 文件大小校验
*
* @param file 上传的文件
* @return
* @throws FileSizeLimitExceededException 如果超出最大大小
*/
public static final void assertAllowed(MultipartFile file) throws FileSizeLimitExceededException
{
long size = file.getSize();
if (DEFAULT_MAX_SIZE != -1 && size > DEFAULT_MAX_SIZE)
{
throw new FileSizeLimitExceededException("not allowed upload upload", size, DEFAULT_MAX_SIZE);
}
}
}

View File

@ -0,0 +1,43 @@
package com.ruoyi.common.utils;
import javax.servlet.http.HttpServletRequest;
/**
* 获取IP方法
*
* @author ruoyi
*/
public class IpUtils
{
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" : ip;
}
}

View File

@ -0,0 +1,136 @@
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;
}
}

View File

@ -0,0 +1,51 @@
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;
for (int i = 0; i < values.length; i++)
{
value = values[i] + ",";
}
value = value.substring(0, value.length() - 1);
}
else
{
value = valueObj.toString();
}
returnMap.put(name, value);
}
return returnMap;
}
}

View File

@ -0,0 +1,27 @@
package com.ruoyi.common.utils;
import org.springframework.context.MessageSource;
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, null);
}
}

View File

@ -0,0 +1,137 @@
package com.ruoyi.common.utils;
import java.io.IOException;
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.support.Convert;
/**
* 客户端工具类
*
* @author ruoyi
*/
public class ServletUtils
{
/**
* 获取String参数
*/
public static String getParameter(String name)
{
return getRequest().getParameter(name);
}
/**
* 获取String参数
*/
public static String getParameter(String name, String defaultValue)
{
return Convert.toStr(getRequest().getParameter(name), defaultValue);
}
/**
* 获取Integer参数
*/
public static Integer getParameterToInt(String name)
{
return Convert.toInt(getRequest().getParameter(name));
}
/**
* 获取Integer参数
*/
public static Integer getParameterToInt(String name, Integer defaultValue)
{
return Convert.toInt(getRequest().getParameter(name), defaultValue);
}
/**
* 获取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.indexOf("application/json") != -1)
{
return true;
}
String xRequestedWith = request.getHeader("X-Requested-With");
if (xRequestedWith != null && xRequestedWith.indexOf("XMLHttpRequest") != -1)
{
return true;
}
String uri = request.getRequestURI();
if (StringUtils.inStringIgnoreCase(uri, ".json", ".xml"))
{
return true;
}
String ajax = request.getParameter("__ajax");
if (StringUtils.inStringIgnoreCase(ajax, "json", "xml"))
{
return true;
}
return false;
}
}

View File

@ -0,0 +1,373 @@
package com.ruoyi.common.utils;
import java.util.Collection;
import java.util.Map;
import org.apache.commons.lang.text.StrBuilder;
import com.ruoyi.common.support.StrFormatter;
/**
* 字符串工具类
*
* @author ruoyi
*/
public class StringUtils
{
/** 空字符串 */
private static final String NULLSTR = "";
/** 下划线 */
private static final char SEPARATOR = '_';
/**
* 获取参数不为空值
*
* @param value defaultValue 要判断的value
* @return value 返回值
*/
public static <T> T nvl(T value, T defaultValue)
{
return value != null ? value : defaultValue;
}
/**
* * 判断一个Collection是否为空 包含ListSetQueue
*
* @param coll 要判断的Collection
* @return true为空 false非空
*/
public static boolean isEmpty(Collection<?> coll)
{
return isNull(coll) || coll.isEmpty();
}
/**
* * 判断一个Collection是否非空包含ListSetQueue
*
* @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 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);
}
/**
* 驼峰首字符小写
*/
public static String uncapitalize(String str)
{
int strLen;
if (str == null || (strLen = str.length()) == 0)
{
return str;
}
return new StrBuilder(strLen).append(Character.toLowerCase(str.charAt(0))).append(str.substring(1)).toString();
}
/**
* 下划线转驼峰命名
*/
public static String toUnderScoreCase(String s)
{
if (s == null)
{
return null;
}
StringBuilder sb = new StringBuilder();
boolean upperCase = false;
for (int i = 0; i < s.length(); i++)
{
char c = s.charAt(i);
boolean nextUpperCase = true;
if (i < (s.length() - 1))
{
nextUpperCase = Character.isUpperCase(s.charAt(i + 1));
}
if ((i > 0) && Character.isUpperCase(c))
{
if (!upperCase || !nextUpperCase)
{
sb.append(SEPARATOR);
}
upperCase = true;
}
else
{
upperCase = false;
}
sb.append(Character.toLowerCase(c));
}
return sb.toString();
}
/**
* 是否包含字符串
*
* @param str 验证字符串
* @param strs 字符串组
* @return 包含返回true
*/
public static boolean inStringIgnoreCase(String str, String... strs)
{
if (str != null && strs != null)
{
for (String s : strs)
{
if (str.equalsIgnoreCase(trim(s)))
{
return true;
}
}
}
return false;
}
/**
* 将下划线大写方式命名的字符串转换为驼峰式如果转换前的下划线大写方式命名的字符串为空则返回空字符串 例如HELLO_WORLD->HelloWorld
*
* @param name 转换前的下划线大写方式命名的字符串
* @return 转换后的驼峰式命名的字符串
*/
public static String convertToCamelCase(String name)
{
StringBuilder result = new StringBuilder();
// 快速检查
if (name == null || name.isEmpty())
{
// 没必要转换
return "";
}
else if (!name.contains("_"))
{
// 不含下划线仅将首字母大写
return name.substring(0, 1).toUpperCase() + name.substring(1);
}
// 用下划线将原始字符串分割
String[] camels = name.split("_");
for (String camel : camels)
{
// 跳过原始字符串中开头结尾的下换线或双重下划线
if (camel.isEmpty())
{
continue;
}
// 首字母大写
result.append(camel.substring(0, 1).toUpperCase());
result.append(camel.substring(1).toLowerCase());
}
return result.toString();
}
}

View File

@ -0,0 +1,71 @@
package com.ruoyi.common.utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.utils.security.ShiroUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.project.monitor.logininfor.domain.Logininfor;
import com.ruoyi.project.monitor.logininfor.service.LogininforServiceImpl;
import eu.bitwalker.useragentutils.UserAgent;
/**
* 记录用户日志信息
*
* @author ruoyi
*/
public class SystemLogUtils
{
private static final Logger sys_user_logger = LoggerFactory.getLogger("sys-user");
/**
* 记录格式 [ip][用户名][操作][错误消息]
* <p/>
* 注意操作如下 loginError 登录失败 loginSuccess 登录成功 passwordError 密码错误 changePassword 修改密码 changeStatus 修改状态
*
* @param username
* @param op
* @param msg
* @param args
*/
public static void log(String username, String status, String msg, Object... args)
{
StringBuilder s = new StringBuilder();
s.append(LogUtils.getBlock(ShiroUtils.getIp()));
s.append(AddressUtils.getRealAddressByIP(ShiroUtils.getIp()));
s.append(LogUtils.getBlock(username));
s.append(LogUtils.getBlock(status));
s.append(LogUtils.getBlock(msg));
sys_user_logger.info(s.toString(), args);
if (Constants.LOGIN_SUCCESS.equals(status) || Constants.LOGOUT.equals(status))
{
saveOpLog(username, msg, Constants.SUCCESS);
}
else if (Constants.LOGIN_FAIL.equals(status))
{
saveOpLog(username, msg, Constants.FAIL);
}
}
public static void saveOpLog(String username, String message, String status)
{
UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent"));
// 获取客户端操作系统
String os = userAgent.getOperatingSystem().getName();
// 获取客户端浏览器
String browser = userAgent.getBrowser().getName();
LogininforServiceImpl logininforService = SpringUtils.getBean(LogininforServiceImpl.class);
Logininfor logininfor = new Logininfor();
logininfor.setLoginName(username);
logininfor.setStatus(status);
logininfor.setIpaddr(ShiroUtils.getIp());
logininfor.setLoginLocation(AddressUtils.getRealAddressByIP(ShiroUtils.getIp()));
logininfor.setBrowser(browser);
logininfor.setOs(os);
logininfor.setMsg(message);
logininforService.insertLogininfor(logininfor);
}
}

View File

@ -0,0 +1,145 @@
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 typeId 传入的父节点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 Menu
*/
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))
{
// 判断是否有子节点
Iterator<Menu> it = childList.iterator();
while (it.hasNext())
{
Menu n = (Menu) it.next();
recursionFn(list, n);
}
}
}
}
/**
* 得到子节点列表
*/
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 ? true : false;
}
}

View File

@ -0,0 +1,409 @@
package com.ruoyi.common.utils.poi;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.apache.poi.hssf.usermodel.DVConstraint;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFCellStyle;
import org.apache.poi.hssf.usermodel.HSSFDataValidation;
import org.apache.poi.hssf.usermodel.HSSFFont;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.util.HSSFColor;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.apache.poi.ss.util.CellRangeAddressList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ResourceUtils;
import com.ruoyi.framework.aspectj.lang.annotation.Excel;
import com.ruoyi.framework.shiro.web.session.OnlineWebSessionManager;
import com.ruoyi.framework.web.domain.AjaxResult;
/**
* Excel相关处理
*
* @author ruoyi
*/
public class ExcelUtil<T>
{
private static final Logger log = LoggerFactory.getLogger(OnlineWebSessionManager.class);
public Class<T> clazz;
public ExcelUtil(Class<T> clazz)
{
this.clazz = clazz;
}
public List<T> importExcel(String sheetName, InputStream input) throws Exception
{
List<T> list = new ArrayList<T>();
Workbook workbook = WorkbookFactory.create(input);
Sheet sheet = workbook.getSheet(sheetName);
if (!sheetName.trim().equals(""))
{
sheet = workbook.getSheet(sheetName); // 如果指定sheet名,则取指定sheet中的内容.
}
if (sheet == null)
{
sheet = workbook.getSheetAt(0); // 如果传入的sheet名不存在则默认指向第1个sheet.
}
int rows = sheet.getPhysicalNumberOfRows();
if (rows > 0)
{
// 有数据时才处理
Field[] allFields = clazz.getDeclaredFields(); // 得到类的所有field.
Map<Integer, Field> fieldsMap = new HashMap<Integer, Field>(); // 定义一个map用于存放列的序号和field.
for (Field field : allFields)
{
// 将有注解的field存放到map中.
if (field.isAnnotationPresent(Excel.class))
{
Excel attr = field.getAnnotation(Excel.class);
int col = getExcelCol(attr.column());// 获得列号
field.setAccessible(true);// 设置类的私有字段属性可访问.
fieldsMap.put(col, field);
}
}
for (int i = 1; i < rows; i++)
{
// 从第2行开始取数据,默认第一行是表头.
Row row = sheet.getRow(i);
int cellNum = sheet.getRow(0).getPhysicalNumberOfCells();
T entity = null;
for (int j = 0; j < cellNum; j++)
{
Cell cell = row.getCell(j);
if (cell == null)
{
continue;
}
else
{
// 先设置Cell的类型然后就可以把纯数字作为String类型读进来了 by zhuyangyong 20171228
row.getCell(j).setCellType(Cell.CELL_TYPE_STRING);
cell = row.getCell(j);
}
String c = cell.getStringCellValue();
if (c.equals(""))
{
continue;
}
entity = (entity == null ? clazz.newInstance() : entity);// 如果不存在实例则新建.
Field field = fieldsMap.get(j);// 从map中得到对应列的field.
// 取得类型,并根据对象类型设置值.
Class<?> fieldType = field.getType();
if (String.class == fieldType)
{
field.set(entity, String.valueOf(c));
}
else if ((Integer.TYPE == fieldType) || (Integer.class == fieldType))
{
field.set(entity, Integer.parseInt(c));
}
else if ((Long.TYPE == fieldType) || (Long.class == fieldType))
{
field.set(entity, Long.valueOf(c));
}
else if ((Float.TYPE == fieldType) || (Float.class == fieldType))
{
field.set(entity, Float.valueOf(c));
}
else if ((Short.TYPE == fieldType) || (Short.class == fieldType))
{
field.set(entity, Short.valueOf(c));
}
else if ((Double.TYPE == fieldType) || (Double.class == fieldType))
{
field.set(entity, Double.valueOf(c));
}
else if (Character.TYPE == fieldType)
{
if ((c != null) && (c.length() > 0))
{
field.set(entity, Character.valueOf(c.charAt(0)));
}
}
else if (java.util.Date.class == fieldType)
{
if (cell.getCellType() == Cell.CELL_TYPE_NUMERIC)
{
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
cell.setCellValue(sdf.format(cell.getNumericCellValue()));
c = sdf.format(cell.getNumericCellValue());
}
else
{
c = cell.getStringCellValue();
}
}
else if (java.math.BigDecimal.class == fieldType)
{
c = cell.getStringCellValue();
}
}
if (entity != null)
{
list.add(entity);
}
}
}
return list;
}
/**
* 对list数据源将其里面的数据导入到excel表单
*
* @param sheetName 工作表的名称
*/
public AjaxResult exportExcel(List<T> list, String sheetName)
{
Field[] allFields = clazz.getDeclaredFields();// 得到所有定义字段
List<Field> fields = new ArrayList<Field>();
// 得到所有field并存放到一个list中.
for (Field field : allFields)
{
if (field.isAnnotationPresent(Excel.class))
{
fields.add(field);
}
}
HSSFWorkbook workbook = new HSSFWorkbook();// 产生工作薄对象
// excel2003中每个sheet中最多有65536行
int sheetSize = 65536;
double sheetNo = Math.ceil(list.size() / sheetSize);// 取出一共有多少个sheet.
for (int index = 0; index <= sheetNo; index++)
{
HSSFSheet sheet = workbook.createSheet();// 产生工作表对象
if (sheetNo == 0)
{
workbook.setSheetName(index, sheetName);
}
else
{
workbook.setSheetName(index, sheetName + index);// 设置工作表的名称.
}
HSSFRow row;
HSSFCell cell; // 产生单元格
row = sheet.createRow(0); // 产生一行
// 写入各个字段的列头名称
for (int i = 0; i < fields.size(); i++)
{
Field field = fields.get(i);
Excel attr = field.getAnnotation(Excel.class);
int col = getExcelCol(attr.column()); // 获得列号
cell = row.createCell(col); // 创建列
cell.setCellType(HSSFCell.CELL_TYPE_STRING); // 设置列中写入内容为String类型
HSSFCellStyle cellStyle = workbook.createCellStyle();
cellStyle.setAlignment(HSSFCellStyle.ALIGN_CENTER);
cellStyle.setVerticalAlignment(CellStyle.VERTICAL_CENTER);
if (attr.name().indexOf("注:") >= 0)
{
HSSFFont font = workbook.createFont();
font.setColor(HSSFFont.COLOR_RED);
cellStyle.setFont(font);
cellStyle.setFillForegroundColor(HSSFColor.LIGHT_YELLOW.index);
sheet.setColumnWidth(i, 6000);
}
else
{
HSSFFont font = workbook.createFont();
font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD); // 粗体显示
cellStyle.setFont(font); // 选择需要用到的字体格式
cellStyle.setFillForegroundColor(HSSFColor.LIGHT_YELLOW.index);
// 设置列宽
sheet.setColumnWidth(i, 3766);
}
cellStyle.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);
cellStyle.setWrapText(true);
cell.setCellStyle(cellStyle);
cell.setCellValue(attr.name());// 写入列名
// 如果设置了提示信息则鼠标放上去提示.
if (!attr.prompt().trim().equals(""))
{
setHSSFPrompt(sheet, "", attr.prompt(), 1, 100, col, col); // 这里默认设了2-101列提示.
}
// 如果设置了combo属性则本列只能选择不能输入
if (attr.combo().length > 0)
{
setHSSFValidation(sheet, attr.combo(), 1, 100, col, col); // 这里默认设了2-101列只能选择不能输入.
}
}
int startNo = index * sheetSize;
int endNo = Math.min(startNo + sheetSize, list.size());
// 写入各条记录,每条记录对应excel表中的一行
HSSFCellStyle cs = workbook.createCellStyle();
cs.setAlignment(HSSFCellStyle.ALIGN_CENTER);
cs.setVerticalAlignment(CellStyle.VERTICAL_CENTER);
for (int i = startNo; i < endNo; i++)
{
row = sheet.createRow(i + 1 - startNo);
T vo = (T) list.get(i); // 得到导出对象.
for (int j = 0; j < fields.size(); j++)
{
Field field = fields.get(j); // 获得field.
field.setAccessible(true); // 设置实体类私有属性可访问
Excel attr = field.getAnnotation(Excel.class);
try
{
// 根据Excel中设置情况决定是否导出,有些情况需要保持为空,希望用户填写这一列.
if (attr.isExport())
{
cell = row.createCell(getExcelCol(attr.column()));// 创建cell
cell.setCellStyle(cs);
try
{
if (String.valueOf(field.get(vo)).length() > 10)
throw new Exception("长度超过10位就不用转数字了");
// 如果可以转成数字则导出为数字类型
BigDecimal bc = new BigDecimal(String.valueOf(field.get(vo)));
cell.setCellType(HSSFCell.CELL_TYPE_NUMERIC);
cell.setCellValue(bc.doubleValue());
}
catch (Exception e)
{
cell.setCellType(HSSFCell.CELL_TYPE_STRING);
if (vo == null)
{
cell.setCellValue(""); // 如果数据存在就填入,不存在填入空格.
}
else
{
cell.setCellValue(field.get(vo) == null ? "" : String.valueOf(field.get(vo)));// 如果数据存在就填入,不存在填入空格.
}
}
}
}
catch (Exception e)
{
log.error("导出Excel失败{}", e);
}
}
}
}
try
{
String filename = encodingFilename(sheetName);
OutputStream out = new FileOutputStream(getfile() + filename);
workbook.write(out);
out.close();
return AjaxResult.success(filename);
}
catch (Exception e)
{
log.error("关闭flush失败{}", e);
return AjaxResult.error("导出Excel失败请联系网站管理员");
}
}
/**
* 将EXCEL中A,B,C,D,E列映射成0,1,2,3
*
* @param col
*/
public static int getExcelCol(String col)
{
col = col.toUpperCase();
// -1开始计算,字母重1开始运算这种总数下来算数正好相同
int count = -1;
char[] cs = col.toCharArray();
for (int i = 0; i < cs.length; i++)
{
count += (cs[i] - 64) * Math.pow(26, cs.length - 1 - i);
}
return count;
}
/**
* 设置单元格上提示
*
* @param sheet 要设置的sheet.
* @param promptTitle 标题
* @param promptContent 内容
* @param firstRow 开始行
* @param endRow 结束行
* @param firstCol 开始列
* @param endCol 结束列
* @return 设置好的sheet.
*/
public static HSSFSheet setHSSFPrompt(HSSFSheet sheet, String promptTitle, String promptContent, int firstRow,
int endRow, int firstCol, int endCol)
{
// 构造constraint对象
DVConstraint constraint = DVConstraint.createCustomFormulaConstraint("DD1");
// 四个参数分别是起始行终止行起始列终止列
CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol);
// 数据有效性对象
HSSFDataValidation data_validation_view = new HSSFDataValidation(regions, constraint);
data_validation_view.createPromptBox(promptTitle, promptContent);
sheet.addValidationData(data_validation_view);
return sheet;
}
/**
* 设置某些列的值只能输入预制的数据,显示下拉框.
*
* @param sheet 要设置的sheet.
* @param textlist 下拉框显示的内容
* @param firstRow 开始行
* @param endRow 结束行
* @param firstCol 开始列
* @param endCol 结束列
* @return 设置好的sheet.
*/
public static HSSFSheet setHSSFValidation(HSSFSheet sheet, String[] textlist, int firstRow, int endRow,
int firstCol, int endCol)
{
// 加载下拉列表内容
DVConstraint constraint = DVConstraint.createExplicitListConstraint(textlist);
// 设置数据有效性加载在哪个单元格上,四个参数分别是起始行终止行起始列终止列
CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol);
// 数据有效性对象
HSSFDataValidation data_validation_list = new HSSFDataValidation(regions, constraint);
sheet.addValidationData(data_validation_list);
return sheet;
}
/**
* 编码文件名
*/
public String encodingFilename(String filename)
{
filename = UUID.randomUUID().toString() + "_" + filename + ".xls";
return filename;
}
public String getfile() throws FileNotFoundException
{
return ResourceUtils.getURL("classpath:").getPath() + "static/file/";
}
}

View File

@ -0,0 +1,76 @@
package com.ruoyi.common.utils.security;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.mgt.RealmSecurityManager;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.subject.Subject;
import com.ruoyi.framework.shiro.realm.UserRealm;
import com.ruoyi.project.system.user.domain.User;
/**
* shiro 工具类
*
* @author ruoyi
*/
public class ShiroUtils
{
public static Subject getSubjct()
{
return SecurityUtils.getSubject();
}
public static Session getSession()
{
return SecurityUtils.getSubject().getSession();
}
public static void logout()
{
getSubjct().logout();
}
public static User getUser()
{
return (User) getSubjct().getPrincipal();
}
public static void setUser(User user)
{
Subject subject = getSubjct();
PrincipalCollection principalCollection = subject.getPrincipals();
String realmName = principalCollection.getRealmNames().iterator().next();
PrincipalCollection newPrincipalCollection = new SimplePrincipalCollection(user, realmName);
// 重新加载Principal
subject.runAs(newPrincipalCollection);
}
public static void clearCachedAuthorizationInfo()
{
RealmSecurityManager rsm = (RealmSecurityManager) SecurityUtils.getSecurityManager();
UserRealm realm = (UserRealm) rsm.getRealms().iterator().next();
realm.clearCachedAuthorizationInfo();
}
public static Long getUserId()
{
return getUser().getUserId().longValue();
}
public static String getLoginName()
{
return getUser().getLoginName();
}
public static String getIp()
{
return getSubjct().getSession().getHost();
}
public static String getSessionId()
{
return String.valueOf(getSubjct().getSession().getId());
}
}

View File

@ -0,0 +1,102 @@
package com.ruoyi.common.utils.spring;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.stereotype.Component;
/**
* spring工具类 方便在非spring管理环境中获取bean
*
* @author ruoyi
*/
@Component
public final class SpringUtils implements BeanFactoryPostProcessor
{
/** Spring应用上下文环境 */
private static ConfigurableListableBeanFactory beanFactory;
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException
{
SpringUtils.beanFactory = beanFactory;
}
/**
* 获取对象
*
* @param name
* @return Object 一个以所给名字注册的bean的实例
* @throws org.springframework.beans.BeansException
*
*/
@SuppressWarnings("unchecked")
public static <T> T getBean(String name) throws BeansException
{
return (T) beanFactory.getBean(name);
}
/**
* 获取类型为requiredType的对象
*
* @param clz
* @return
* @throws org.springframework.beans.BeansException
*
*/
public static <T> T getBean(Class<T> clz) throws BeansException
{
T result = (T) beanFactory.getBean(clz);
return result;
}
/**
* 如果BeanFactory包含一个与所给名称匹配的bean定义则返回true
*
* @param name
* @return boolean
*/
public static boolean containsBean(String name)
{
return beanFactory.containsBean(name);
}
/**
* 判断以给定名字注册的bean定义是一个singleton还是一个prototype 如果与给定名字相应的bean定义没有被找到将会抛出一个异常NoSuchBeanDefinitionException
*
* @param name
* @return boolean
* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
*
*/
public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException
{
return beanFactory.isSingleton(name);
}
/**
* @param name
* @return Class 注册对象的类型
* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
*
*/
public static Class<?> getType(String name) throws NoSuchBeanDefinitionException
{
return beanFactory.getType(name);
}
/**
* 如果给定的bean名字在bean定义中有别名则返回这些别名
*
* @param name
* @return
* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
*
*/
public static String[] getAliases(String name) throws NoSuchBeanDefinitionException
{
return beanFactory.getAliases(name);
}
}

View File

@ -0,0 +1,86 @@
package com.ruoyi.common.xss;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
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.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 防止XSS攻击的过滤器
*
* @author ruoyi
*/
@WebFilter(filterName = "xssFilter", urlPatterns = "/system/*")
public class XssFilter implements Filter
{
/**
* 排除链接
*/
public List<String> excludes = new ArrayList<>();
@Override
public void init(FilterConfig filterConfig) throws ServletException
{
String temp = filterConfig.getInitParameter("excludes");
if (temp != null)
{
String[] url = temp.split(",");
for (int i = 0; url != null && i < url.length; i++)
{
excludes.add(url[i]);
}
}
}
@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)
{
if (excludes == null || excludes.isEmpty())
{
return false;
}
String url = request.getServletPath();
for (String pattern : excludes)
{
Pattern p = Pattern.compile("^" + pattern);
Matcher m = p.matcher(url);
if (m.find())
{
return true;
}
}
return false;
}
@Override
public void destroy()
{
}
}

View File

@ -0,0 +1,41 @@
package com.ruoyi.common.xss;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import org.jsoup.Jsoup;
import org.jsoup.safety.Whitelist;
/**
* 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] = Jsoup.clean(values[i], Whitelist.relaxed()).trim();
}
return escapseValues;
}
return super.getParameterValues(name);
}
}

View File

@ -0,0 +1,178 @@
package com.ruoyi.framework.aspectj;
import java.lang.reflect.Method;
import java.util.Map;
import com.ruoyi.common.utils.AddressUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
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.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.stereotype.Component;
import com.alibaba.fastjson.JSONObject;
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.constant.BusinessStatus;
import com.ruoyi.project.monitor.operlog.domain.OperLog;
import com.ruoyi.project.monitor.operlog.service.IOperLogService;
import com.ruoyi.project.system.user.domain.User;
/**
* 操作日志记录处理
*
* @author ruoyi
*/
@Aspect
@Component
@EnableAsync
public class LogAspect
{
private static final Logger log = LoggerFactory.getLogger(LogAspect.class);
@Autowired
private IOperLogService operLogService;
// 配置织入点
@Pointcut("@annotation(com.ruoyi.framework.aspectj.lang.annotation.Log)")
public void logPointCut()
{
}
/**
* 前置通知 用于拦截操作
*
* @param joinPoint 切点
*/
@AfterReturning(pointcut = "logPointCut()")
public void doBefore(JoinPoint joinPoint)
{
handleLog(joinPoint, null);
}
/**
* 拦截异常操作
*
* @param joinPoint
* @param e
*/
@AfterThrowing(value = "logPointCut()", throwing = "e")
public void doAfter(JoinPoint joinPoint, Exception e)
{
handleLog(joinPoint, e);
}
@Async
protected void handleLog(final JoinPoint joinPoint, final Exception e)
{
try
{
// 获得注解
Log controllerLog = getAnnotationLog(joinPoint);
if (controllerLog == null)
{
return;
}
// 获取当前的用户
User currentUser = ShiroUtils.getUser();
// *========数据库日志=========*//
OperLog operLog = new OperLog();
operLog.setStatus(BusinessStatus.SUCCESS);
// 请求的地址
String ip = ShiroUtils.getIp();
operLog.setOperIp(ip);
// 操作地点
operLog.setOperLocation(AddressUtils.getRealAddressByIP(ip));
operLog.setOperUrl(ServletUtils.getRequest().getRequestURI());
if (currentUser != null)
{
operLog.setOperName(currentUser.getLoginName());
operLog.setDeptName(currentUser.getDept().getDeptName());
}
if (e != null)
{
operLog.setStatus(BusinessStatus.FAIL);
operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000));
}
// 设置方法名称
String className = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName();
operLog.setMethod(className + "." + methodName + "()");
// 处理设置注解上的参数
getControllerMethodDescription(controllerLog, operLog);
// 保存数据库
operLogService.insertOperlog(operLog);
}
catch (Exception exp)
{
// 记录本地异常日志
log.error("==前置通知异常==");
log.error("异常信息:{}", exp.getMessage());
exp.printStackTrace();
}
}
/**
* 获取注解中对方法的描述信息 用于Controller层注解
*
* @param joinPoint 切点
* @return 方法描述
* @throws Exception
*/
public void getControllerMethodDescription(Log log, OperLog operLog) throws Exception
{
// 设置action动作
operLog.setAction(log.action());
// 设置标题
operLog.setTitle(log.title());
// 设置channel
operLog.setChannel(log.channel());
// 是否需要保存request参数和值
if (log.isSaveRequestData())
{
// 获取参数的信息传入到数据库中
setRequestValue(operLog);
}
}
/**
* 获取请求的参数放到log中
*
* @param operLog
* @param request
*/
private void setRequestValue(OperLog operLog)
{
Map<String, String[]> map = ServletUtils.getRequest().getParameterMap();
String params = JSONObject.toJSONString(map);
operLog.setOperParam(StringUtils.substring(params, 0, 255));
}
/**
* 是否存在注解如果存在就获取
*/
private Log getAnnotationLog(JoinPoint joinPoint) throws Exception
{
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
if (method != null)
{
return method.getAnnotation(Log.class);
}
return null;
}
}

View File

@ -0,0 +1,41 @@
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;
/**
* 自定义注解
*
* @author ruoyi
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Excel
{
/**
* 导出到Excel中的名字.
*/
public abstract String name();
/**
* 配置列的名称
*/
public abstract String column();
/**
* 提示信息
*/
public abstract String prompt() default "";
/**
* 设置只能选择不能输入的列内容.
*/
public abstract String[] combo() default {};
/**
* 是否导出数据,应对需求:有时我们需要导出一份模板,这是标题需要但内容需要用户手工填写.
*/
public abstract boolean isExport() default true;
}

View File

@ -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;
import com.ruoyi.framework.aspectj.lang.constant.OperatorType;
/**
* 自定义操作日志记录注解
*
* @author ruoyi
*
*/
@Target({ ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log
{
/** 模块 */
String title() default "";
/** 功能 */
String action() default "";
/** 渠道 */
String channel() default OperatorType.MANAGE;
/** 是否保存请求的参数 */
boolean isSaveRequestData() default true;
}

View File

@ -0,0 +1,19 @@
package com.ruoyi.framework.aspectj.lang.constant;
/**
* 操作状态
*
* @author ruoyi
*
*/
public class BusinessStatus
{
/** 其它 */
public static final String OTHER = "-1";
/** 成功 */
public static final String SUCCESS = "0";
/** 失败 */
public static final String FAIL = "1";
}

View File

@ -0,0 +1,33 @@
package com.ruoyi.framework.aspectj.lang.constant;
/**
* 业务操作类型
*
* @author ruoyi
*
*/
public class BusinessType
{
/** 其它 */
public static final String OTHER = "0";
/** 新增 */
public static final String INSERT = "1";
/** 修改 */
public static final String UPDATE = "2";
/** 保存 */
public static final String SAVE = "3";
/** 删除 */
public static final String DELETE = "4";
/** 授权 */
public static final String GRANT = "5";
/** 导出 */
public static final String EXPORT = "6";
/** 导入 */
public static final String IMPORT = "7";
/** 强退 */
public static final String FORCE = "8";
/** 禁止访问 */
public static final String FORBID = "9";
/** 生成代码 */
public static final String GENCODE = "10";
}

View File

@ -0,0 +1,22 @@
package com.ruoyi.framework.aspectj.lang.constant;
/**
* 操作人类别
*
* @author ruoyi
*
*/
public class OperatorType
{
/** 其它 */
public static final String OTHER = "0";
/** 后台用户 */
public static final String MANAGE = "1";
/** 渠道用户 */
public static final String CHANNEL = "2";
/** 手机端用户 */
public static final String MOBILE = "3";
}

View File

@ -0,0 +1,61 @@
package com.ruoyi.framework.config;
import java.util.Properties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.google.code.kaptcha.util.Config;
/**
* 验证码配置
*
* @author ruoyi
*/
@Configuration
public class CaptchaConfig
{
@Bean(name = "captchaProducer")
public DefaultKaptcha getKaptchaBean()
{
DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
Properties properties = new Properties();
properties.setProperty("kaptcha.border", "yes");
properties.setProperty("kaptcha.border.color", "105,179,90");
properties.setProperty("kaptcha.textproducer.font.color", "blue");
properties.setProperty("kaptcha.image.width", "160");
properties.setProperty("kaptcha.image.height", "60");
properties.setProperty("kaptcha.textproducer.font.size", "28");
properties.setProperty("kaptcha.session.key", "kaptchaCode");
properties.setProperty("kaptcha.textproducer.char.spac", "35");
properties.setProperty("kaptcha.textproducer.char.length", "5");
properties.setProperty("kaptcha.textproducer.font.names", "Arial,Courier");
properties.setProperty("kaptcha.noise.color", "white");
Config config = new Config(properties);
defaultKaptcha.setConfig(config);
return defaultKaptcha;
}
@Bean(name = "captchaProducerMath")
public DefaultKaptcha getKaptchaBeanMath()
{
DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
Properties properties = new Properties();
properties.setProperty("kaptcha.border", "yes");
properties.setProperty("kaptcha.border.color", "105,179,90");
properties.setProperty("kaptcha.textproducer.font.color", "blue");
properties.setProperty("kaptcha.image.width", "160");
properties.setProperty("kaptcha.image.height", "60");
properties.setProperty("kaptcha.textproducer.font.size", "38");
properties.setProperty("kaptcha.session.key", "kaptchaCodeMath");
properties.setProperty("kaptcha.textproducer.impl", "com.ruoyi.framework.config.KaptchaTextCreator");
properties.setProperty("kaptcha.textproducer.char.spac", "5");
properties.setProperty("kaptcha.textproducer.char.length", "6");
properties.setProperty("kaptcha.textproducer.font.names", "Arial,Courier");
properties.setProperty("kaptcha.noise.color", "white");
properties.setProperty("kaptcha.noise.impl", "com.google.code.kaptcha.impl.NoNoise");
properties.setProperty("kaptcha.obscurificator.impl", "com.google.code.kaptcha.impl.ShadowGimpy");
Config config = new Config(properties);
defaultKaptcha.setConfig(config);
return defaultKaptcha;
}
}

View File

@ -0,0 +1,161 @@
package com.ruoyi.framework.config;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
/**
* Druid数据库信息配置加载
*
* @author ruoyi
*/
@Configuration
public class DruidConfig
{
private static final Logger log = LoggerFactory.getLogger(DruidConfig.class);
@Value("${spring.datasource.url}")
private String dbUrl;
@Value("${spring.datasource.username}")
private String username;
@Value("${spring.datasource.password}")
private String password;
@Value("${spring.datasource.driverClassName}")
private String driverClassName;
@Value("${spring.datasource.initialSize}")
private int initialSize;
@Value("${spring.datasource.minIdle}")
private int minIdle;
@Value("${spring.datasource.maxActive}")
private int maxActive;
@Value("${spring.datasource.maxWait}")
private int maxWait;
@Value("${spring.datasource.timeBetweenEvictionRunsMillis}")
private int timeBetweenEvictionRunsMillis;
@Value("${spring.datasource.minEvictableIdleTimeMillis}")
private int minEvictableIdleTimeMillis;
@Value("${spring.datasource.validationQuery}")
private String validationQuery;
@Value("${spring.datasource.testWhileIdle}")
private boolean testWhileIdle;
@Value("${spring.datasource.testOnBorrow}")
private boolean testOnBorrow;
@Value("${spring.datasource.testOnReturn}")
private boolean testOnReturn;
@Value("${spring.datasource.poolPreparedStatements}")
private boolean poolPreparedStatements;
@Value("${spring.datasource.maxPoolPreparedStatementPerConnectionSize}")
private int maxPoolPreparedStatementPerConnectionSize;
@Value("${spring.datasource.filters}")
private String filters;
@Value("{spring.datasource.connectionProperties}")
private String connectionProperties;
@Bean(initMethod = "init", destroyMethod = "close") /** 声明其为Bean实例 */
@Primary /** 在同样的DataSource中首先使用被标注的DataSource */
public DataSource dataSource()
{
DruidDataSource datasource = new DruidDataSource();
datasource.setUrl(this.dbUrl);
datasource.setUsername(username);
datasource.setPassword(password);
datasource.setDriverClassName(driverClassName);
/** configuration */
datasource.setInitialSize(initialSize);
datasource.setMinIdle(minIdle);
datasource.setMaxActive(maxActive);
datasource.setMaxWait(maxWait);
datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
datasource.setValidationQuery(validationQuery);
datasource.setTestWhileIdle(testWhileIdle);
datasource.setTestOnBorrow(testOnBorrow);
datasource.setTestOnReturn(testOnReturn);
datasource.setPoolPreparedStatements(poolPreparedStatements);
datasource.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize);
try
{
datasource.setFilters(filters);
}
catch (SQLException e)
{
log.error("druid configuration initialization filter", e);
}
datasource.setConnectionProperties(connectionProperties);
return datasource;
}
/**
* 注册一个StatViewServlet 相当于在web.xml中声明了一个servlet
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
@Bean
public ServletRegistrationBean druidServlet()
{
ServletRegistrationBean reg = new ServletRegistrationBean();
reg.setServlet(new StatViewServlet());
reg.addUrlMappings("/monitor/druid/*");
/** 白名单 */
// reg.addInitParameter("allow", "10.211.61.45,127.0.0.1,123.207.20.136");
/** IP黑名单(共同存在时deny优先于allow) */
// reg.addInitParameter("deny", "10.211.61.4");
/** 是否能够重置数据 禁用HTML页面上的“Reset All”功能 */
reg.addInitParameter("resetEnable", "false");
return reg;
}
/**
* 注册一个filterRegistrationBean 相当于在web.xml中声明了一个Filter
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
@Bean
public FilterRegistrationBean filterRegistrationBean()
{
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(new WebStatFilter());
/** 添加过滤规则. */
filterRegistrationBean.addUrlPatterns("/*");
/** 监控选项滤器 */
filterRegistrationBean.addInitParameter("DruidWebStatFilter", "/*");
/** 添加不需要忽略的格式信息. */
filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/monitor/druid/*");
/** 配置profileEnable能够监控单个url调用的sql列表 */
filterRegistrationBean.addInitParameter("profileEnable", "true");
/** 当前的cookie的用户 */
filterRegistrationBean.addInitParameter("principalCookieName", "USER_COOKIE");
/** 当前的session的用户 */
filterRegistrationBean.addInitParameter("principalSessionName", "USER_SESSION");
return filterRegistrationBean;
}
}

View File

@ -0,0 +1,34 @@
package com.ruoyi.framework.config;
import java.util.Map;
import javax.servlet.DispatcherType;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.google.common.collect.Maps;
import com.ruoyi.common.xss.XssFilter;
/**
* Filter配置
*
* @author ruoyi
*/
@Configuration
public class FilterConfig
{
@SuppressWarnings({ "rawtypes", "unchecked" })
@Bean
public FilterRegistrationBean xssFilterRegistration()
{
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setDispatcherTypes(DispatcherType.REQUEST);
registration.setFilter(new XssFilter());
registration.addUrlPatterns("/*");
registration.setName("xssFilter");
registration.setOrder(Integer.MAX_VALUE);
Map<String, String> initParameters = Maps.newHashMap();
initParameters.put("excludes", "/system/notice/*");
registration.setInitParameters(initParameters);
return registration;
}
}

View File

@ -0,0 +1,71 @@
package com.ruoyi.framework.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* 读取代码生成相关配置
*
* @author ruoyi
*/
@Component
@ConfigurationProperties(prefix = "gen")
public class GenConfig
{
/** 作者 */
public static String author;
/** 生成包路径 */
public static String packageName;
/** 自动去除表前缀默认是true */
public static String autoRemovePre;
/** 表前缀(类名不会包含表前缀) */
public static String tablePrefix;
public static String getAuthor()
{
return author;
}
public void setAuthor(String author)
{
GenConfig.author = author;
}
public static String getPackageName()
{
return packageName;
}
public void setPackageName(String packageName)
{
GenConfig.packageName = packageName;
}
public static String getAutoRemovePre()
{
return autoRemovePre;
}
public void setAutoRemovePre(String autoRemovePre)
{
GenConfig.autoRemovePre = autoRemovePre;
}
public static String getTablePrefix()
{
return tablePrefix;
}
public void setTablePrefix(String tablePrefix)
{
GenConfig.tablePrefix = tablePrefix;
}
@Override
public String toString()
{
return "GenConfig [getClass()=" + getClass() + ", hashCode()=" + hashCode() + ", toString()=" + super.toString()
+ "]";
}
}

View File

@ -0,0 +1,44 @@
package com.ruoyi.framework.config;
import java.util.Locale;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
import org.springframework.web.servlet.i18n.SessionLocaleResolver;
/**
* 资源文件配置加载
*
* @author ruoyi
*/
@Configuration
public class I18nConfig implements WebMvcConfigurer
{
@Bean
public LocaleResolver localeResolver()
{
SessionLocaleResolver slr = new SessionLocaleResolver();
// 默认语言
slr.setDefaultLocale(Locale.SIMPLIFIED_CHINESE);
return slr;
}
@Bean
public LocaleChangeInterceptor localeChangeInterceptor()
{
LocaleChangeInterceptor lci = new LocaleChangeInterceptor();
// 参数名
lci.setParamName("lang");
return lci;
}
@Override
public void addInterceptors(InterceptorRegistry registry)
{
registry.addInterceptor(localeChangeInterceptor());
}
}

View File

@ -0,0 +1,77 @@
package com.ruoyi.framework.config;
import java.util.Random;
import com.google.code.kaptcha.text.impl.DefaultTextCreator;
/**
* 验证码文本生成器
*
* @author ruoyi
*/
public class KaptchaTextCreator extends DefaultTextCreator
{
private static final String[] CNUMBERS = "0,1,2,3,4,5,6,7,8,9,10".split(",");
@Override
public String getText()
{
Integer result = 0;
Random random = new Random();
int x = random.nextInt(10);
int y = random.nextInt(10);
StringBuilder suChinese = new StringBuilder();
int randomoperands = (int) Math.round(Math.random() * 2);
if (randomoperands == 0)
{
result = x * y;
suChinese.append(CNUMBERS[x]);
suChinese.append("*");
suChinese.append(CNUMBERS[y]);
}
else if (randomoperands == 1)
{
if (!(x == 0) && y % x == 0)
{
result = y / x;
suChinese.append(CNUMBERS[y]);
suChinese.append("/");
suChinese.append(CNUMBERS[x]);
}
else
{
result = x + y;
suChinese.append(CNUMBERS[x]);
suChinese.append("+");
suChinese.append(CNUMBERS[y]);
}
}
else if (randomoperands == 2)
{
if (x >= y)
{
result = x - y;
suChinese.append(CNUMBERS[x]);
suChinese.append("-");
suChinese.append(CNUMBERS[y]);
}
else
{
result = y - x;
suChinese.append(CNUMBERS[y]);
suChinese.append("-");
suChinese.append(CNUMBERS[x]);
}
}
else
{
result = x + y;
suChinese.append(CNUMBERS[x]);
suChinese.append("+");
suChinese.append(CNUMBERS[y]);
}
suChinese.append("=?@" + result);
return suChinese.toString();
}
}

View File

@ -0,0 +1,42 @@
package com.ruoyi.framework.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* 通用配置
*
* @author ruoyi
*/
@Configuration
public class ResourcesConfig implements WebMvcConfigurer
{
/**
* 首页地址
*/
@Value("${shiro.user.indexUrl}")
private String indexUrl;
/**
* 默认首页的设置当输入域名是可以自动跳转到默认指定的网页
*/
@Override
public void addViewControllers(ViewControllerRegistry registry)
{
registry.addViewController("/").setViewName("forward:" + indexUrl);
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry)
{
/** 头像上传路径 */
registry.addResourceHandler("/profile/**").addResourceLocations("file:" + RuoYiConfig.getProfile());
/** swagger配置 */
registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
}
}

View File

@ -0,0 +1,64 @@
package com.ruoyi.framework.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* 读取项目相关配置
*
* @author ruoyi
*/
@Component
@ConfigurationProperties(prefix = "ruoyi")
public class RuoYiConfig
{
/** 项目名称 */
private String name;
/** 版本 */
private String version;
/** 版权年份 */
private String copyrightYear;
/** 上传路径 */
private static String profile;
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public String getVersion()
{
return version;
}
public void setVersion(String version)
{
this.version = version;
}
public String getCopyrightYear()
{
return copyrightYear;
}
public void setCopyrightYear(String copyrightYear)
{
this.copyrightYear = copyrightYear;
}
public static String getProfile()
{
return profile;
}
public void setProfile(String profile)
{
RuoYiConfig.profile = profile;
}
}

View File

@ -0,0 +1,56 @@
package com.ruoyi.framework.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import javax.sql.DataSource;
import java.util.Properties;
/**
* 定时任务配置
*
* @author ruoyi
*
*/
@Configuration
public class ScheduleConfig
{
@Bean
public SchedulerFactoryBean schedulerFactoryBean(DataSource dataSource)
{
SchedulerFactoryBean factory = new SchedulerFactoryBean();
factory.setDataSource(dataSource);
// quartz参数
Properties prop = new Properties();
prop.put("org.quartz.scheduler.instanceName", "RuoyiScheduler");
prop.put("org.quartz.scheduler.instanceId", "AUTO");
// 线程池配置
prop.put("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool");
prop.put("org.quartz.threadPool.threadCount", "20");
prop.put("org.quartz.threadPool.threadPriority", "5");
// JobStore配置
prop.put("org.quartz.jobStore.class", "org.quartz.impl.jdbcjobstore.JobStoreTX");
// 集群配置
prop.put("org.quartz.jobStore.isClustered", "true");
prop.put("org.quartz.jobStore.clusterCheckinInterval", "15000");
prop.put("org.quartz.jobStore.maxMisfiresToHandleAtATime", "1");
prop.put("org.quartz.jobStore.misfireThreshold", "12000");
prop.put("org.quartz.jobStore.tablePrefix", "QRTZ_");
factory.setQuartzProperties(prop);
factory.setSchedulerName("RuoyiScheduler");
// 延时启动
factory.setStartupDelay(1);
factory.setApplicationContextSchedulerContextKey("applicationContextKey");
// 可选QuartzScheduler
// 启动时更新己存在的Job这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了
factory.setOverwriteExistingJobs(true);
// 设置自动启动默认为true
factory.setAutoStartup(true);
return factory;
}
}

View File

@ -0,0 +1,360 @@
package com.ruoyi.framework.config;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.servlet.Filter;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.ruoyi.framework.shiro.realm.UserRealm;
import com.ruoyi.framework.shiro.session.OnlineSessionDAO;
import com.ruoyi.framework.shiro.session.OnlineSessionFactory;
import com.ruoyi.framework.shiro.web.filter.LogoutFilter;
import com.ruoyi.framework.shiro.web.filter.captcha.CaptchaValidateFilter;
import com.ruoyi.framework.shiro.web.filter.online.OnlineSessionFilter;
import com.ruoyi.framework.shiro.web.filter.sync.SyncOnlineSessionFilter;
import com.ruoyi.framework.shiro.web.session.OnlineWebSessionManager;
import com.ruoyi.framework.shiro.web.session.SpringSessionValidationScheduler;
import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
/**
* 权限配置加载
*
* @author ruoyi
*/
@Configuration
public class ShiroConfig
{
public static final String PREMISSION_STRING = "perms[\"{0}\"]";
// Session超时时间单位为毫秒默认30分钟
@Value("${shiro.session.expireTime}")
private int expireTime;
// 相隔多久检查一次session的有效性单位毫秒默认就是10分钟
@Value("${shiro.session.validationInterval}")
private int validationInterval;
// 验证码开关
@Value("${shiro.user.captchaEbabled}")
private boolean captchaEbabled;
// 验证码类型
@Value("${shiro.user.captchaType}")
private String captchaType;
// 设置Cookie的域名
@Value("${shiro.cookie.domain}")
private String domain;
// 设置cookie的有效访问路径
@Value("${shiro.cookie.path}")
private String path;
// 设置HttpOnly属性
@Value("${shiro.cookie.httpOnly}")
private boolean httpOnly;
// 设置Cookie的过期时间秒为单位
@Value("${shiro.cookie.maxAge}")
private int maxAge;
// 登录地址
@Value("${shiro.user.loginUrl}")
private String loginUrl;
// 权限认证失败地址
@Value("${shiro.user.unauthorizedUrl}")
private String unauthorizedUrl;
/**
* 缓存管理器 使用Ehcache实现
*/
@Bean
public EhCacheManager getEhCacheManager()
{
EhCacheManager em = new EhCacheManager();
em.setCacheManagerConfigFile("classpath:ehcache/ehcache-shiro.xml");
return em;
}
/**
* 自定义Realm
*/
@Bean
public UserRealm userRealm(EhCacheManager cacheManager)
{
UserRealm userRealm = new UserRealm();
userRealm.setCacheManager(cacheManager);
return userRealm;
}
/**
* 自定义sessionDAO会话
*/
@Bean
public OnlineSessionDAO sessionDAO()
{
OnlineSessionDAO sessionDAO = new OnlineSessionDAO();
return sessionDAO;
}
/**
* 自定义sessionFactory会话
*/
@Bean
public OnlineSessionFactory sessionFactory()
{
OnlineSessionFactory sessionFactory = new OnlineSessionFactory();
return sessionFactory;
}
/**
* 自定义sessionFactory调度器
*/
@Bean
public SpringSessionValidationScheduler sessionValidationScheduler()
{
SpringSessionValidationScheduler sessionValidationScheduler = new SpringSessionValidationScheduler();
// 相隔多久检查一次session的有效性单位毫秒默认就是10分钟
sessionValidationScheduler.setSessionValidationInterval(validationInterval * 60 * 1000);
// 设置会话验证调度器进行会话验证时的会话管理器
sessionValidationScheduler.setSessionManager(sessionValidationManager());
return sessionValidationScheduler;
}
/**
* 会话管理器
*/
@Bean
public OnlineWebSessionManager sessionValidationManager()
{
OnlineWebSessionManager manager = new OnlineWebSessionManager();
// 加入缓存管理器
manager.setCacheManager(getEhCacheManager());
// 删除过期的session
manager.setDeleteInvalidSessions(true);
// 设置全局session超时时间
manager.setGlobalSessionTimeout(expireTime * 60 * 1000);
// 去掉 JSESSIONID
manager.setSessionIdUrlRewritingEnabled(false);
// 是否定时检查session
manager.setSessionValidationSchedulerEnabled(true);
// 自定义SessionDao
manager.setSessionDAO(sessionDAO());
// 自定义sessionFactory
manager.setSessionFactory(sessionFactory());
return manager;
}
/**
* 会话管理器
*/
@Bean
public OnlineWebSessionManager sessionManager()
{
OnlineWebSessionManager manager = new OnlineWebSessionManager();
// 加入缓存管理器
manager.setCacheManager(getEhCacheManager());
// 删除过期的session
manager.setDeleteInvalidSessions(true);
// 设置全局session超时时间
manager.setGlobalSessionTimeout(expireTime * 60 * 1000);
// 去掉 JSESSIONID
manager.setSessionIdUrlRewritingEnabled(false);
// 定义要使用的无效的Session定时调度器
manager.setSessionValidationScheduler(sessionValidationScheduler());
// 是否定时检查session
manager.setSessionValidationSchedulerEnabled(true);
// 自定义SessionDao
manager.setSessionDAO(sessionDAO());
// 自定义sessionFactory
manager.setSessionFactory(sessionFactory());
return manager;
}
/**
* 安全管理器
*/
@Bean
public SecurityManager securityManager(UserRealm userRealm)
{
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
// 设置realm.
securityManager.setRealm(userRealm);
// 记住我
securityManager.setRememberMeManager(rememberMeManager());
// 注入缓存管理器;
securityManager.setCacheManager(getEhCacheManager());
// session管理器
securityManager.setSessionManager(sessionManager());
return securityManager;
}
/**
* 退出过滤器
*/
public LogoutFilter logoutFilter()
{
LogoutFilter logoutFilter = new LogoutFilter();
logoutFilter.setLoginUrl(loginUrl);
return logoutFilter;
}
/**
* Shiro过滤器配置
*/
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager)
{
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// Shiro的核心安全接口,这个属性是必须的
shiroFilterFactoryBean.setSecurityManager(securityManager);
// 身份认证失败则跳转到登录页面的配置
shiroFilterFactoryBean.setLoginUrl(loginUrl);
// 权限认证失败则跳转到指定页面
shiroFilterFactoryBean.setUnauthorizedUrl(unauthorizedUrl);
// Shiro连接约束配置即过滤链的定义
LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
// 对静态资源设置匿名访问
filterChainDefinitionMap.put("/favicon.ico**", "anon");
filterChainDefinitionMap.put("/ruoyi.png**", "anon");
filterChainDefinitionMap.put("/css/**", "anon");
filterChainDefinitionMap.put("/docs/**", "anon");
filterChainDefinitionMap.put("/fonts/**", "anon");
filterChainDefinitionMap.put("/img/**", "anon");
filterChainDefinitionMap.put("/ajax/**", "anon");
filterChainDefinitionMap.put("/js/**", "anon");
filterChainDefinitionMap.put("/ruoyi/**", "anon");
filterChainDefinitionMap.put("/druid/**", "anon");
filterChainDefinitionMap.put("/captcha/captchaImage**", "anon");
// 退出 logout地址shiro去清除session
filterChainDefinitionMap.put("/logout", "logout");
// 不需要拦截的访问
filterChainDefinitionMap.put("/login", "anon,captchaValidate");
// 系统权限列表
// filterChainDefinitionMap.putAll(SpringUtils.getBean(IMenuService.class).selectPermsAll());
Map<String, Filter> filters = new LinkedHashMap<>();
filters.put("onlineSession", onlineSessionFilter());
filters.put("syncOnlineSession", syncOnlineSessionFilter());
filters.put("captchaValidate", captchaValidateFilter());
// 注销成功则跳转到指定页面
filters.put("logout", logoutFilter());
shiroFilterFactoryBean.setFilters(filters);
// 所有请求需要认证
filterChainDefinitionMap.put("/**", "user");
// 系统请求记录当前会话
filterChainDefinitionMap.put("/main", "onlineSession,syncOnlineSession");
filterChainDefinitionMap.put("/system/**", "onlineSession,syncOnlineSession");
filterChainDefinitionMap.put("/monitor/**", "onlineSession,syncOnlineSession");
filterChainDefinitionMap.put("/tool/**", "onlineSession,syncOnlineSession");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
/**
* 自定义在线用户处理过滤器
*/
@Bean
public OnlineSessionFilter onlineSessionFilter()
{
OnlineSessionFilter onlineSessionFilter = new OnlineSessionFilter();
onlineSessionFilter.setLoginUrl(loginUrl);
return onlineSessionFilter;
}
/**
* 自定义在线用户同步过滤器
*/
@Bean
public SyncOnlineSessionFilter syncOnlineSessionFilter()
{
SyncOnlineSessionFilter syncOnlineSessionFilter = new SyncOnlineSessionFilter();
return syncOnlineSessionFilter;
}
/**
* 自定义验证码过滤器
*/
@Bean
public CaptchaValidateFilter captchaValidateFilter()
{
CaptchaValidateFilter captchaValidateFilter = new CaptchaValidateFilter();
captchaValidateFilter.setCaptchaEbabled(captchaEbabled);
captchaValidateFilter.setCaptchaType(captchaType);
return captchaValidateFilter;
}
/**
* cookie 属性设置
*/
public SimpleCookie rememberMeCookie()
{
SimpleCookie cookie = new SimpleCookie("rememberMe");
cookie.setDomain(domain);
cookie.setPath(path);
cookie.setHttpOnly(httpOnly);
cookie.setMaxAge(maxAge * 24 * 60 * 60);
return cookie;
}
/**
* 记住我
*/
public CookieRememberMeManager rememberMeManager()
{
CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
cookieRememberMeManager.setCookie(rememberMeCookie());
cookieRememberMeManager.setCipherKey(Base64.decode("fCq+/xW488hMTCD+cmJ3aQ=="));
return cookieRememberMeManager;
}
/**
* 开启Shiro代理
*/
@Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator()
{
DefaultAdvisorAutoProxyCreator proxyCreator = new DefaultAdvisorAutoProxyCreator();
proxyCreator.setProxyTargetClass(true);
return proxyCreator;
}
/**
* thymeleaf模板引擎和shiro框架的整合
*/
@Bean
public ShiroDialect shiroDialect()
{
return new ShiroDialect();
}
/**
* 开启Shiro注解通知器
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(
@Qualifier("securityManager") SecurityManager securityManager)
{
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
}

View File

@ -0,0 +1,58 @@
package com.ruoyi.framework.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
/**
* Swagger2的接口配置
*
* @author ruoyi
*/
@Configuration
@EnableSwagger2
public class SwaggerConfig
{
/** 系统基础配置 */
@Autowired
private RuoYiConfig ruoYiConfig;
/**
* 创建API
*/
@Bean
public Docket createRestApi()
{
return new Docket(DocumentationType.SWAGGER_2)
// 详细定制
.apiInfo(apiInfo())
.select()
// 指定当前包路径
.apis(RequestHandlerSelectors.basePackage("com.ruoyi.project.tool.swagger"))
// 扫描所有 .apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build();
}
/**
* 添加摘要信息
*/
private ApiInfo apiInfo()
{
// 用ApiInfoBuilder进行定制
return new ApiInfoBuilder()
.title("标题若依管理系统_接口文档")
.description("描述:用于管理集团旗下公司的人员信息,具体包括XXX,XXX模块...")
.contact(new Contact(ruoYiConfig.getName(), null, null))
.version("版本号:" + ruoYiConfig.getVersion())
.build();
}
}

View File

@ -0,0 +1,126 @@
package com.ruoyi.framework.shiro.realm;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.ExcessiveAttemptsException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import com.ruoyi.common.exception.user.CaptchaException;
import com.ruoyi.common.exception.user.RoleBlockedException;
import com.ruoyi.common.exception.user.UserBlockedException;
import com.ruoyi.common.exception.user.UserNotExistsException;
import com.ruoyi.common.exception.user.UserPasswordNotMatchException;
import com.ruoyi.common.exception.user.UserPasswordRetryLimitExceedException;
import com.ruoyi.common.utils.security.ShiroUtils;
import com.ruoyi.framework.shiro.service.LoginService;
import com.ruoyi.project.system.menu.service.IMenuService;
import com.ruoyi.project.system.role.service.IRoleService;
import com.ruoyi.project.system.user.domain.User;
/**
* 自定义Realm 处理登录 权限
*
* @author ruoyi
*/
public class UserRealm extends AuthorizingRealm
{
private static final Logger log = LoggerFactory.getLogger(UserRealm.class);
@Autowired
private IMenuService menuService;
@Autowired
private IRoleService roleService;
@Autowired
private LoginService loginService;
/**
* 授权
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0)
{
Long userId = ShiroUtils.getUserId();
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
// 角色加入AuthorizationInfo认证对象
info.setRoles(roleService.selectRoleKeys(userId));
// 权限加入AuthorizationInfo认证对象
info.setStringPermissions(menuService.selectPermsByUserId(userId));
return info;
}
/**
* 登录认证
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException
{
UsernamePasswordToken upToken = (UsernamePasswordToken) token;
String username = upToken.getUsername();
String password = "";
if (upToken.getPassword() != null)
{
password = new String(upToken.getPassword());
}
User user = null;
try
{
user = loginService.login(username, password);
}
catch (CaptchaException e)
{
throw new AuthenticationException(e.getMessage(), e);
}
catch (UserNotExistsException e)
{
throw new UnknownAccountException(e.getMessage(), e);
}
catch (UserPasswordNotMatchException e)
{
throw new IncorrectCredentialsException(e.getMessage(), e);
}
catch (UserPasswordRetryLimitExceedException e)
{
throw new ExcessiveAttemptsException(e.getMessage(), e);
}
catch (UserBlockedException e)
{
throw new LockedAccountException(e.getMessage(), e);
}
catch (RoleBlockedException e)
{
throw new LockedAccountException(e.getMessage(), e);
}
catch (Exception e)
{
log.info("对用户[" + username + "]进行登录验证..验证未通过{}", e.getMessage());
throw new AuthenticationException(e.getMessage(), e);
}
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, password, getName());
return info;
}
/**
* 清理缓存权限
*/
public void clearCachedAuthorizationInfo()
{
this.clearCachedAuthorizationInfo(SecurityUtils.getSubject().getPrincipals());
}
}

View File

@ -0,0 +1,128 @@
package com.ruoyi.framework.shiro.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.constant.ShiroConstants;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.exception.user.CaptchaException;
import com.ruoyi.common.exception.user.UserBlockedException;
import com.ruoyi.common.exception.user.UserNotExistsException;
import com.ruoyi.common.exception.user.UserPasswordNotMatchException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.MessageUtils;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.SystemLogUtils;
import com.ruoyi.common.utils.security.ShiroUtils;
import com.ruoyi.project.system.user.domain.User;
import com.ruoyi.project.system.user.domain.UserStatus;
import com.ruoyi.project.system.user.service.IUserService;
/**
* 登录校验方法
*
* @author ruoyi
*/
@Component
public class LoginService
{
@Autowired
private PasswordService passwordService;
@Autowired
private IUserService userService;
/**
* 登录
*/
public User login(String username, String password)
{
// 验证码校验
if (!StringUtils.isEmpty(ServletUtils.getRequest().getAttribute(ShiroConstants.CURRENT_CAPTCHA)))
{
SystemLogUtils.log(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error"));
throw new CaptchaException();
}
// 用户名或密码为空 错误
if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password))
{
SystemLogUtils.log(username, Constants.LOGIN_FAIL, MessageUtils.message("not.null"));
throw new UserNotExistsException();
}
// 密码如果不在指定范围内 错误
if (password.length() < UserConstants.PASSWORD_MIN_LENGTH
|| password.length() > UserConstants.PASSWORD_MAX_LENGTH)
{
SystemLogUtils.log(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match"));
throw new UserPasswordNotMatchException();
}
// 用户名不在指定范围内 错误
if (username.length() < UserConstants.USERNAME_MIN_LENGTH
|| username.length() > UserConstants.USERNAME_MAX_LENGTH)
{
SystemLogUtils.log(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match"));
throw new UserPasswordNotMatchException();
}
// 查询用户信息
User user = userService.selectUserByLoginName(username);
if (user == null && maybeMobilePhoneNumber(username))
{
user = userService.selectUserByPhoneNumber(username);
}
if (user == null && maybeEmail(username))
{
user = userService.selectUserByEmail(username);
}
if (user == null || UserStatus.DELETED.getCode().equals(user.getDelFlag()))
{
SystemLogUtils.log(username, Constants.LOGIN_FAIL, MessageUtils.message("user.not.exists"));
throw new UserNotExistsException();
}
passwordService.validate(user, password);
if (UserStatus.DISABLE.getCode().equals(user.getStatus()))
{
SystemLogUtils.log(username, Constants.LOGIN_FAIL, MessageUtils.message("user.blocked", user.getRemark()));
throw new UserBlockedException(user.getRemark());
}
SystemLogUtils.log(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"));
recordLoginInfo(user);
return user;
}
private boolean maybeEmail(String username)
{
if (!username.matches(UserConstants.EMAIL_PATTERN))
{
return false;
}
return true;
}
private boolean maybeMobilePhoneNumber(String username)
{
if (!username.matches(UserConstants.MOBILE_PHONE_NUMBER_PATTERN))
{
return false;
}
return true;
}
/**
* 记录登录信息
*/
public void recordLoginInfo(User user)
{
user.setLoginIp(ShiroUtils.getIp());
user.setLoginDate(DateUtils.getNowDate());
userService.updateUser(user);
}
}

View File

@ -0,0 +1,101 @@
package com.ruoyi.framework.shiro.service;
import java.util.concurrent.atomic.AtomicInteger;
import javax.annotation.PostConstruct;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.exception.user.UserPasswordNotMatchException;
import com.ruoyi.common.exception.user.UserPasswordRetryLimitExceedException;
import com.ruoyi.common.utils.MessageUtils;
import com.ruoyi.common.utils.SystemLogUtils;
import com.ruoyi.project.system.user.domain.User;
/**
* 登录密码方法
*
* @author ruoyi
*/
@Component
public class PasswordService
{
@Autowired
private CacheManager cacheManager;
private Cache<String, AtomicInteger> loginRecordCache;
@Value(value = "${user.password.maxRetryCount}")
private String maxRetryCount;
@PostConstruct
public void init()
{
loginRecordCache = cacheManager.getCache("loginRecordCache");
}
public void validate(User user, String password)
{
String loginName = user.getLoginName();
AtomicInteger retryCount = loginRecordCache.get(loginName);
if (retryCount == null)
{
retryCount = new AtomicInteger(0);
loginRecordCache.put(loginName, retryCount);
}
if (retryCount.incrementAndGet() > Integer.valueOf(maxRetryCount).intValue())
{
SystemLogUtils.log(loginName, Constants.LOGIN_FAIL, MessageUtils.message("user.password.retry.limit.exceed", maxRetryCount));
throw new UserPasswordRetryLimitExceedException(Integer.valueOf(maxRetryCount).intValue());
}
if (!matches(user, password))
{
SystemLogUtils.log(loginName, Constants.LOGIN_FAIL, MessageUtils.message("user.password.retry.limit.count", retryCount, password));
loginRecordCache.put(loginName, retryCount);
throw new UserPasswordNotMatchException();
}
else
{
clearLoginRecordCache(loginName);
}
}
public boolean matches(User user, String newPassword)
{
return user.getPassword().equals(encryptPassword(user.getLoginName(), newPassword, user.getSalt()));
}
public void clearLoginRecordCache(String username)
{
loginRecordCache.remove(username);
}
public String encryptPassword(String username, String password, String salt)
{
return new Md5Hash(username + password + salt).toHex().toString();
}
public static void main(String[] args)
{
//System.out.println(new PasswordService().encryptPassword("admin", "admin123", "111111"));
//System.out.println(new PasswordService().encryptPassword("ry", "admin123", "222222"));
System.out.println(new PasswordService().encryptPassword("ly", "admin123", "123456"));
System.out.println(new PasswordService().encryptPassword("ce", "admin123", "123456"));
System.out.println(new PasswordService().encryptPassword("zs", "admin123", "123456"));
System.out.println(new PasswordService().encryptPassword("ls", "admin123", "123456"));
System.out.println(new PasswordService().encryptPassword("ww", "admin123", "123456"));
System.out.println(new PasswordService().encryptPassword("zl", "admin123", "123456"));
System.out.println(new PasswordService().encryptPassword("sq", "admin123", "123456"));
System.out.println(new PasswordService().encryptPassword("zb", "admin123", "123456"));
System.out.println(new PasswordService().encryptPassword("wj", "admin123", "123456"));
System.out.println(new PasswordService().encryptPassword("ys", "admin123", "123456"));
}
}

View File

@ -0,0 +1,31 @@
package com.ruoyi.framework.shiro.service;
import org.apache.shiro.SecurityUtils;
import org.springframework.stereotype.Component;
/**
* RuoYi首创 js调用 thymeleaf 实现按钮权限可见性
*
* @author ruoyi
*/
@Component
public class PermissionService
{
public String hasPermi(String permission)
{
return isPermittedOperator(permission) ? "" : "hidden";
}
private boolean isPermittedOperator(String permission)
{
if (SecurityUtils.getSubject().isPermitted(permission))
{
return true;
}
else
{
return false;
}
}
}

View File

@ -0,0 +1,115 @@
package com.ruoyi.framework.shiro.session;
import java.io.Serializable;
import java.util.Date;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import com.ruoyi.project.monitor.online.domain.OnlineSession;
import com.ruoyi.project.monitor.online.domain.UserOnline;
import com.ruoyi.project.monitor.online.service.IUserOnlineService;
/**
* 针对自定义的ShiroSession的db操作
*
* @author ruoyi
*/
public class OnlineSessionDAO extends EnterpriseCacheSessionDAO
{
/**
* 同步session到数据库的周期 单位为毫秒默认1分钟
*/
@Value("${shiro.session.dbSyncPeriod}")
private int dbSyncPeriod;
/**
* 上次同步数据库的时间戳
*/
private static final String LAST_SYNC_DB_TIMESTAMP = OnlineSessionDAO.class.getName() + "LAST_SYNC_DB_TIMESTAMP";
@Autowired
private IUserOnlineService onlineService;
@Autowired
private OnlineSessionFactory onlineSessionFactory;
public OnlineSessionDAO()
{
super();
}
public OnlineSessionDAO(long expireTime)
{
super();
}
/**
* 根据会话ID获取会话
*
* @param sessionId 会话ID
* @return ShiroSession
*/
@Override
protected Session doReadSession(Serializable sessionId)
{
UserOnline userOnline = onlineService.selectOnlineById(String.valueOf(sessionId));
if (userOnline == null)
{
return null;
}
return onlineSessionFactory.createSession(userOnline);
}
/**
* 更新会话如更新会话最后访问时间/停止会话/设置超时时间/设置移除属性等会调用
*/
public void syncToDb(OnlineSession onlineSession)
{
Date lastSyncTimestamp = (Date) onlineSession.getAttribute(LAST_SYNC_DB_TIMESTAMP);
if (lastSyncTimestamp != null)
{
boolean needSync = true;
long deltaTime = onlineSession.getLastAccessTime().getTime() - lastSyncTimestamp.getTime();
if (deltaTime < dbSyncPeriod * 60 * 1000)
{
// 时间差不足 无需同步
needSync = false;
}
boolean isGuest = onlineSession.getUserId() == null || onlineSession.getUserId() == 0L;
// session 数据变更了 同步
if (isGuest == false && onlineSession.isAttributeChanged())
{
needSync = true;
}
if (needSync == false)
{
return;
}
}
onlineSession.setAttribute(LAST_SYNC_DB_TIMESTAMP, onlineSession.getLastAccessTime());
// 更新完后 重置标识
if (onlineSession.isAttributeChanged())
{
onlineSession.resetAttributeChanged();
}
onlineService.saveOnline(UserOnline.fromOnlineSession(onlineSession));
}
/**
* 当会话过期/停止如用户退出时属性等会调用
*/
@Override
protected void doDelete(Session session)
{
OnlineSession onlineSession = (OnlineSession) session;
if (null == onlineSession)
{
return;
}
onlineSession.setStatus(OnlineSession.OnlineStatus.off_line);
onlineService.deleteOnlineById(String.valueOf(onlineSession.getId()));
}
}

View File

@ -0,0 +1,56 @@
package com.ruoyi.framework.shiro.session;
import javax.servlet.http.HttpServletRequest;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.mgt.SessionContext;
import org.apache.shiro.session.mgt.SessionFactory;
import org.apache.shiro.web.session.mgt.WebSessionContext;
import org.springframework.stereotype.Component;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.IpUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.project.monitor.online.domain.OnlineSession;
import com.ruoyi.project.monitor.online.domain.UserOnline;
import eu.bitwalker.useragentutils.UserAgent;
/**
* 自定义sessionFactory会话
*
* @author ruoyi
*/
@Component
public class OnlineSessionFactory implements SessionFactory
{
public Session createSession(UserOnline userOnline)
{
OnlineSession onlineSession = userOnline.getSession();
if (StringUtils.isNotNull(onlineSession) && onlineSession.getId() == null)
{
onlineSession.setId(userOnline.getSessionId());
}
return userOnline.getSession();
}
@Override
public Session createSession(SessionContext initData)
{
OnlineSession session = new OnlineSession();
if (initData != null && initData instanceof WebSessionContext)
{
WebSessionContext sessionContext = (WebSessionContext) initData;
HttpServletRequest request = (HttpServletRequest) sessionContext.getServletRequest();
if (request != null)
{
UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent"));
// 获取客户端操作系统
String os = userAgent.getOperatingSystem().getName();
// 获取客户端浏览器
String browser = userAgent.getBrowser().getName();
session.setHost(IpUtils.getIpAddr(request));
session.setBrowser(browser);
session.setOs(os);
}
}
return session;
}
}

View File

@ -0,0 +1,86 @@
package com.ruoyi.framework.shiro.web.filter;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.apache.shiro.session.SessionException;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.utils.MessageUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.SystemLogUtils;
import com.ruoyi.common.utils.security.ShiroUtils;
import com.ruoyi.project.system.user.domain.User;
/**
* 退出过滤器
*
* @author ruoyi
*/
public class LogoutFilter extends org.apache.shiro.web.filter.authc.LogoutFilter
{
private static final Logger log = LoggerFactory.getLogger(LogoutFilter.class);
/**
* 退出后重定向的地址
*/
private String loginUrl;
public String getLoginUrl()
{
return loginUrl;
}
public void setLoginUrl(String loginUrl)
{
this.loginUrl = loginUrl;
}
@Override
protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception
{
try
{
Subject subject = getSubject(request, response);
String redirectUrl = getRedirectUrl(request, response, subject);
try
{
User user = (User) ShiroUtils.getSubjct().getPrincipal();
if (StringUtils.isNotNull(user))
{
String loginName = user.getLoginName();
// 记录用户退出日志
SystemLogUtils.log(loginName, Constants.LOGOUT, MessageUtils.message("user.logout.success"));
}
// 退出登录
subject.logout();
}
catch (SessionException ise)
{
log.error("logout fail.", ise);
}
issueRedirect(request, response, redirectUrl);
}
catch (Exception e)
{
log.debug("Encountered session exception during logout. This can generally safely be ignored.", e);
}
return false;
}
/**
* 退出跳转URL
*/
@Override
protected String getRedirectUrl(ServletRequest request, ServletResponse response, Subject subject)
{
String url = getLoginUrl();
if (StringUtils.isNotEmpty(url))
{
return url;
}
return super.getRedirectUrl(request, response, subject);
}
}

View File

@ -0,0 +1,78 @@
package com.ruoyi.framework.shiro.web.filter.captcha;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import org.apache.shiro.web.filter.AccessControlFilter;
import com.google.code.kaptcha.Constants;
import com.ruoyi.common.constant.ShiroConstants;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.security.ShiroUtils;
/**
* 验证码过滤器
*
* @author ruoyi
*/
public class CaptchaValidateFilter extends AccessControlFilter
{
/**
* 是否开启验证码
*/
private boolean captchaEbabled = true;
/**
* 验证码类型
*/
private String captchaType = "math";
public void setCaptchaEbabled(boolean captchaEbabled)
{
this.captchaEbabled = captchaEbabled;
}
public void setCaptchaType(String captchaType)
{
this.captchaType = captchaType;
}
@Override
public boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception
{
request.setAttribute(ShiroConstants.CURRENT_EBABLED, captchaEbabled);
request.setAttribute(ShiroConstants.CURRENT_TYPE, captchaType);
return super.onPreHandle(request, response, mappedValue);
}
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue)
throws Exception
{
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
// 验证码禁用 或不是表单提交 允许访问
if (captchaEbabled == false || !"post".equals(httpServletRequest.getMethod().toLowerCase()))
{
return true;
}
return validateResponse(httpServletRequest, httpServletRequest.getParameter(ShiroConstants.CURRENT_VALIDATECODE));
}
public boolean validateResponse(HttpServletRequest request, String validateCode)
{
Object obj = ShiroUtils.getSession().getAttribute(Constants.KAPTCHA_SESSION_KEY);
String code = String.valueOf(obj != null ? obj : "");
if (StringUtils.isEmpty(validateCode) || !validateCode.equalsIgnoreCase(code))
{
return false;
}
return true;
}
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception
{
request.setAttribute(ShiroConstants.CURRENT_CAPTCHA, ShiroConstants.CAPTCHA_ERROR);
return true;
}
}

View File

@ -0,0 +1,97 @@
package com.ruoyi.framework.shiro.web.filter.online;
import java.io.IOException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.AccessControlFilter;
import org.apache.shiro.web.util.WebUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import com.ruoyi.common.constant.ShiroConstants;
import com.ruoyi.common.utils.security.ShiroUtils;
import com.ruoyi.framework.shiro.session.OnlineSessionDAO;
import com.ruoyi.project.monitor.online.domain.OnlineSession;
import com.ruoyi.project.system.user.domain.User;
/**
* 自定义访问控制
*
* @author ruoyi
*/
public class OnlineSessionFilter extends AccessControlFilter
{
/**
* 强制退出后重定向的地址
*/
@Value("${shiro.user.loginUrl}")
private String loginUrl;
@Autowired
private OnlineSessionDAO onlineSessionDAO;
/**
* 表示是否允许访问mappedValue就是[urls]配置中拦截器参数部分如果允许访问返回true否则false
*/
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue)
throws Exception
{
Subject subject = getSubject(request, response);
if (subject == null || subject.getSession() == null)
{
return true;
}
Session session = onlineSessionDAO.readSession(subject.getSession().getId());
if (session != null && session instanceof OnlineSession)
{
OnlineSession onlineSession = (OnlineSession) session;
request.setAttribute(ShiroConstants.ONLINE_SESSION, onlineSession);
// 把user对象设置进去
boolean isGuest = onlineSession.getUserId() == null || onlineSession.getUserId() == 0L;
if (isGuest == true)
{
User user = ShiroUtils.getUser();
if (user != null)
{
onlineSession.setUserId(user.getUserId());
onlineSession.setLoginName(user.getLoginName());
onlineSession.setDeptName(user.getDept().getDeptName());
onlineSession.markAttributeChanged();
}
}
if (onlineSession.getStatus() == OnlineSession.OnlineStatus.off_line)
{
return false;
}
}
return true;
}
/**
* 表示当访问拒绝时是否已经处理了如果返回true表示需要继续处理如果返回false表示该拦截器实例已经处理了将直接返回即可
*/
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception
{
Subject subject = getSubject(request, response);
if (subject != null)
{
subject.logout();
}
saveRequestAndRedirectToLogin(request, response);
return true;
}
// 跳转到登录页
@Override
protected void redirectToLogin(ServletRequest request, ServletResponse response) throws IOException
{
WebUtils.issueRedirect(request, response, loginUrl);
}
}

View File

@ -0,0 +1,42 @@
package com.ruoyi.framework.shiro.web.filter.sync;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.apache.shiro.web.filter.PathMatchingFilter;
import org.springframework.beans.factory.annotation.Autowired;
import com.ruoyi.common.constant.ShiroConstants;
import com.ruoyi.framework.shiro.session.OnlineSessionDAO;
import com.ruoyi.project.monitor.online.domain.OnlineSession;
/**
* 同步Session数据到Db
*
* @author ruoyi
*/
public class SyncOnlineSessionFilter extends PathMatchingFilter
{
@Autowired
private OnlineSessionDAO onlineSessionDAO;
/**
* 同步会话数据到DB 一次请求最多同步一次 防止过多处理 需要放到Shiro过滤器之前
*
* @param request
* @param response
* @return
* @throws Exception
*/
@Override
protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception
{
OnlineSession session = (OnlineSession) request.getAttribute(ShiroConstants.ONLINE_SESSION);
// 如果session stop了 也不同步
// session停止时间如果stopTimestamp不为null则代表已停止
if (session != null && session.getUserId() != null && session.getStopTimestamp() == null)
{
onlineSessionDAO.syncToDb(session);
}
return true;
}
}

View File

@ -0,0 +1,155 @@
package com.ruoyi.framework.shiro.web.session;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import org.apache.commons.lang3.time.DateUtils;
import org.apache.shiro.session.ExpiredSessionException;
import org.apache.shiro.session.InvalidSessionException;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.mgt.DefaultSessionKey;
import org.apache.shiro.session.mgt.SessionKey;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.ruoyi.common.constant.ShiroConstants;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.project.monitor.online.domain.OnlineSession;
import com.ruoyi.project.monitor.online.domain.UserOnline;
import com.ruoyi.project.monitor.online.service.UserOnlineServiceImpl;
/**
* 主要是在此如果会话的属性修改了 就标识下其修改了 然后方便 OnlineSessionDao同步
*
* @author ruoyi
*/
public class OnlineWebSessionManager extends DefaultWebSessionManager
{
private static final Logger log = LoggerFactory.getLogger(OnlineWebSessionManager.class);
@Override
public void setAttribute(SessionKey sessionKey, Object attributeKey, Object value) throws InvalidSessionException
{
super.setAttribute(sessionKey, attributeKey, value);
if (value != null && needMarkAttributeChanged(attributeKey))
{
OnlineSession s = (OnlineSession) doGetSession(sessionKey);
s.markAttributeChanged();
}
}
private boolean needMarkAttributeChanged(Object attributeKey)
{
if (attributeKey == null)
{
return false;
}
String attributeKeyStr = attributeKey.toString();
// 优化 flash属性没必要持久化
if (attributeKeyStr.startsWith("org.springframework"))
{
return false;
}
if (attributeKeyStr.startsWith("javax.servlet"))
{
return false;
}
if (attributeKeyStr.equals(ShiroConstants.CURRENT_USERNAME))
{
return false;
}
return true;
}
@Override
public Object removeAttribute(SessionKey sessionKey, Object attributeKey) throws InvalidSessionException
{
Object removed = super.removeAttribute(sessionKey, attributeKey);
if (removed != null)
{
OnlineSession s = (OnlineSession) doGetSession(sessionKey);
s.markAttributeChanged();
}
return removed;
}
/**
* 验证session是否有效 用于删除过期session
*/
@Override
public void validateSessions()
{
if (log.isInfoEnabled())
{
log.info("invalidation sessions...");
}
int invalidCount = 0;
int timeout = (int) this.getGlobalSessionTimeout();
Date expiredDate = DateUtils.addMilliseconds(new Date(), 0 - timeout);
UserOnlineServiceImpl userOnlineService = SpringUtils.getBean(UserOnlineServiceImpl.class);
List<UserOnline> userOnlineList = userOnlineService.selectOnlineByExpired(expiredDate);
// 批量过期删除
List<String> needOfflineIdList = new ArrayList<String>();
for (UserOnline userOnline : userOnlineList)
{
try
{
SessionKey key = new DefaultSessionKey(userOnline.getSessionId());
Session session = retrieveSession(key);
if (session != null)
{
throw new InvalidSessionException();
}
}
catch (InvalidSessionException e)
{
if (log.isDebugEnabled())
{
boolean expired = (e instanceof ExpiredSessionException);
String msg = "Invalidated session with id [" + userOnline.getSessionId() + "]"
+ (expired ? " (expired)" : " (stopped)");
log.debug(msg);
}
invalidCount++;
needOfflineIdList.add(userOnline.getSessionId());
}
}
if (needOfflineIdList.size() > 0)
{
try
{
userOnlineService.batchDeleteOnline(needOfflineIdList);
}
catch (Exception e)
{
log.error("batch delete db session error.", e);
}
}
if (log.isInfoEnabled())
{
String msg = "Finished invalidation session.";
if (invalidCount > 0)
{
msg += " [" + invalidCount + "] sessions were stopped.";
}
else
{
msg += " No sessions were stopped.";
}
log.info(msg);
}
}
@Override
protected Collection<Session> getActiveSessions()
{
throw new UnsupportedOperationException("getActiveSessions method not supported");
}
}

View File

@ -0,0 +1,141 @@
package com.ruoyi.framework.shiro.web.session;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import org.apache.shiro.session.mgt.DefaultSessionManager;
import org.apache.shiro.session.mgt.SessionValidationScheduler;
import org.apache.shiro.session.mgt.ValidatingSessionManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 自定义任务调度器完成
*
* @author ruoyi
*/
public class SpringSessionValidationScheduler implements SessionValidationScheduler
{
private static final Logger log = LoggerFactory.getLogger(SpringSessionValidationScheduler.class);
public static final long DEFAULT_SESSION_VALIDATION_INTERVAL = DefaultSessionManager.DEFAULT_SESSION_VALIDATION_INTERVAL;
/**
* 定时器用于处理超时的挂起请求也用于连接断开时的重连
*/
private final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
private volatile boolean enabled = false;
/**
* The session manager used to validate sessions.
*/
private ValidatingSessionManager sessionManager;
/**
* The session validation interval in milliseconds.
*/
private long sessionValidationInterval = DEFAULT_SESSION_VALIDATION_INTERVAL;
/**
* Default constructor.
*/
public SpringSessionValidationScheduler()
{
}
/**
* Constructor that specifies the session manager that should be used for validating sessions.
*
* @param sessionManager the <tt>SessionManager</tt> that should be used to validate sessions.
*/
public SpringSessionValidationScheduler(ValidatingSessionManager sessionManager)
{
this.sessionManager = sessionManager;
}
public void setSessionManager(ValidatingSessionManager sessionManager)
{
this.sessionManager = sessionManager;
}
@Override
public boolean isEnabled()
{
return this.enabled;
}
/**
* Specifies how frequently (in milliseconds) this Scheduler will call the
* {@link org.apache.shiro.session.mgt.ValidatingSessionManager#validateSessions()
* ValidatingSessionManager#validateSessions()} method.
*
* <p>
* Unless this method is called, the default value is {@link #DEFAULT_SESSION_VALIDATION_INTERVAL}.
*
* @param sessionValidationInterval
*/
public void setSessionValidationInterval(long sessionValidationInterval)
{
this.sessionValidationInterval = sessionValidationInterval;
}
/**
* Starts session validation by creating a spring PeriodicTrigger.
*/
@Override
public void enableSessionValidation()
{
enabled = true;
if (log.isDebugEnabled())
{
log.debug("Scheduling session validation job using Spring Scheduler with "
+ "session validation interval of [" + sessionValidationInterval + "]ms...");
}
try
{
executorService.scheduleAtFixedRate(new Runnable()
{
@Override
public void run()
{
if (enabled)
{
sessionManager.validateSessions();
}
}
}, 1000, sessionValidationInterval, TimeUnit.MILLISECONDS);
this.enabled = true;
if (log.isDebugEnabled())
{
log.debug("Session validation job successfully scheduled with Spring Scheduler.");
}
}
catch (Exception e)
{
if (log.isErrorEnabled())
{
log.error(
"Error starting the Spring Scheduler session validation job. Session validation may not occur.",
e);
}
}
}
@Override
public void disableSessionValidation()
{
if (log.isDebugEnabled())
{
log.debug("Stopping Spring Scheduler session validation job...");
}
this.enabled = false;
}
}

View File

@ -0,0 +1,131 @@
package com.ruoyi.framework.web.controller;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.security.ShiroUtils;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.framework.web.page.PageDomain;
import com.ruoyi.framework.web.page.TableDataInfo;
import com.ruoyi.framework.web.page.TableSupport;
import com.ruoyi.project.system.user.domain.User;
/**
* web层通用数据处理
*
* @author ruoyi
*/
public class BaseController
{
/**
* 将前台传递过来的日期格式的字符串自动转化为Date类型
*/
@InitBinder
public void initBinder(WebDataBinder binder)
{
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
dateFormat.setLenient(false);
binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));
}
/**
* 设置请求分页数据
*/
protected void startPage()
{
PageDomain pageDomain = TableSupport.buildPageRequest();
Integer pageNum = pageDomain.getPageNum();
Integer pageSize = pageDomain.getPageSize();
if (StringUtils.isNotNull(pageNum) && StringUtils.isNotNull(pageSize))
{
String orderBy = pageDomain.getOrderBy();
PageHelper.startPage(pageNum, pageSize, orderBy);
}
}
/**
* 响应请求分页数据
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
protected TableDataInfo getDataTable(List<?> list)
{
TableDataInfo rspData = new TableDataInfo();
rspData.setRows(list);
rspData.setTotal(new PageInfo(list).getTotal());
return rspData;
}
/**
* 返回成功
*/
public AjaxResult success()
{
return AjaxResult.success();
}
/**
* 返回失败消息
*/
public AjaxResult error()
{
return AjaxResult.error();
}
/**
* 返回成功消息
*/
public AjaxResult success(String message)
{
return AjaxResult.success(message);
}
/**
* 返回失败消息
*/
public AjaxResult error(String message)
{
return AjaxResult.error(message);
}
/**
* 返回错误码消息
*/
public AjaxResult error(int code, String message)
{
return AjaxResult.error(code, message);
}
/**
* 页面跳转
*/
public String redirect(String url)
{
return StringUtils.format("redirect:{}", url);
}
public User getUser()
{
return ShiroUtils.getUser();
}
public void setUser(User user)
{
ShiroUtils.setUser(user);
}
public Long getUserId()
{
return getUser().getUserId();
}
public String getLoginName()
{
return getUser().getLoginName();
}
}

View File

@ -0,0 +1,94 @@
package com.ruoyi.framework.web.domain;
import java.util.HashMap;
/**
* 操作消息提醒
*
* @author ruoyi
*/
public class AjaxResult extends HashMap<String, Object>
{
private static final long serialVersionUID = 1L;
/**
* 初始化一个新创建的 Message 对象
*/
public AjaxResult()
{
}
/**
* 返回错误消息
*
* @return 错误消息
*/
public static AjaxResult error()
{
return error(1, "操作失败");
}
/**
* 返回错误消息
*
* @param msg 内容
* @return 错误消息
*/
public static AjaxResult error(String msg)
{
return error(500, msg);
}
/**
* 返回错误消息
*
* @param code 错误码
* @param msg 内容
* @return 错误消息
*/
public static AjaxResult error(int code, String msg)
{
AjaxResult json = new AjaxResult();
json.put("code", code);
json.put("msg", msg);
return json;
}
/**
* 返回成功消息
*
* @param msg 内容
* @return 成功消息
*/
public static AjaxResult success(String msg)
{
AjaxResult json = new AjaxResult();
json.put("msg", msg);
json.put("code", 0);
return json;
}
/**
* 返回成功消息
*
* @return 成功消息
*/
public static AjaxResult success()
{
return AjaxResult.success("操作成功");
}
/**
* 返回成功消息
*
* @param key 键值
* @param value 内容
* @return 成功消息
*/
@Override
public AjaxResult put(String key, Object value)
{
super.put(key, value);
return this;
}
}

View File

@ -0,0 +1,98 @@
package com.ruoyi.framework.web.domain;
import java.io.Serializable;
import java.util.Date;
import com.ruoyi.common.utils.DateUtils;
/**
* Entity基类
*
* @author ruoyi
*/
public class BaseEntity implements Serializable
{
private static final long serialVersionUID = 1L;
/** 搜索值 */
private String searchValue;
/** 创建者 */
private String createBy;
/** 创建时间 */
private Date createTime;
/** 更新者 */
private String updateBy;
/** 更新时间 */
private Date updateTime;
/** 备注 */
private String remark;
public String getSearchValue()
{
return searchValue;
}
public void setSearchValue(String searchValue)
{
this.searchValue = searchValue;
}
public String getCreateBy()
{
return createBy;
}
public void setCreateBy(String createBy)
{
this.createBy = createBy;
}
public String getCreateTimeStr()
{
return createTime != null ? DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD, createTime) : "";
}
public String getCreateDateTimeStr()
{
return createTime != null ? DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, createTime) : "";
}
public void setCreateTime(Date createTime)
{
this.createTime = createTime;
}
public String getUpdateBy()
{
return updateBy;
}
public void setUpdateBy(String updateBy)
{
this.updateBy = updateBy;
}
public String getUpdateTimeStr()
{
return updateTime != null ? DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD, updateTime) : "";
}
public String getUpdateDateTimeStr()
{
return updateTime != null ? DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, updateTime) : "";
}
public void setUpdateTime(Date updateTime)
{
this.updateTime = updateTime;
}
public String getRemark()
{
return remark;
}
public void setRemark(String remark)
{
this.remark = remark;
}
}

View File

@ -0,0 +1,72 @@
package com.ruoyi.framework.web.exception;
import org.apache.shiro.authz.AuthorizationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import com.ruoyi.common.exception.DemoModeException;
import com.ruoyi.framework.web.domain.AjaxResult;
/**
* 自定义异常处理器
*
* @author ruoyi
*/
@RestControllerAdvice
public class DefaultExceptionHandler
{
private static final Logger log = LoggerFactory.getLogger(DefaultExceptionHandler.class);
/**
* 权限校验失败
*/
@ExceptionHandler(AuthorizationException.class)
public AjaxResult handleAuthorizationException(AuthorizationException e)
{
log.error(e.getMessage(), e);
return AjaxResult.error("您没有数据的权限,请联系管理员添加");
}
/**
* 请求方式不支持
*/
@ExceptionHandler({ HttpRequestMethodNotSupportedException.class })
public AjaxResult handleException(HttpRequestMethodNotSupportedException e)
{
log.error(e.getMessage(), e);
return AjaxResult.error("不支持' " + e.getMethod() + "'请求");
}
/**
* 拦截未知的运行时异常
*/
@ExceptionHandler(RuntimeException.class)
public AjaxResult notFount(RuntimeException e)
{
log.error("运行时异常:", e);
return AjaxResult.error("运行时异常:" + e.getMessage());
}
/**
* 系统异常
*/
@ExceptionHandler(Exception.class)
public AjaxResult handleException(Exception e)
{
log.error(e.getMessage(), e);
return AjaxResult.error("服务器错误,请联系管理员");
}
/**
* 演示模式异常
*/
@ExceptionHandler(DemoModeException.class)
public AjaxResult demoModeException(DemoModeException e)
{
return AjaxResult.error("演示模式,不允许操作");
}
}

View File

@ -0,0 +1,70 @@
package com.ruoyi.framework.web.page;
import com.ruoyi.common.utils.StringUtils;
/**
* 分页数据
*
* @author ruoyi
*/
public class PageDomain
{
/** 当前记录起始索引 */
private Integer pageNum;
/** 每页显示记录数 */
private Integer pageSize;
/** 排序列 */
private String orderByColumn;
/** 排序的方向 "desc" 或者 "asc". */
private String isAsc;
public String getOrderBy()
{
if (StringUtils.isEmpty(orderByColumn))
{
return "";
}
return StringUtils.toUnderScoreCase(orderByColumn) + " " + isAsc;
}
public Integer getPageNum()
{
return pageNum;
}
public void setPageNum(Integer pageNum)
{
this.pageNum = pageNum;
}
public Integer getPageSize()
{
return pageSize;
}
public void setPageSize(Integer pageSize)
{
this.pageSize = pageSize;
}
public String getOrderByColumn()
{
return orderByColumn;
}
public void setOrderByColumn(String orderByColumn)
{
this.orderByColumn = orderByColumn;
}
public String getIsAsc()
{
return isAsc;
}
public void setIsAsc(String isAsc)
{
this.isAsc = isAsc;
}
}

View File

@ -0,0 +1,58 @@
package com.ruoyi.framework.web.page;
import java.io.Serializable;
import java.util.List;
/**
* 表格分页数据对象
*
* @author ruoyi
*/
public class TableDataInfo implements Serializable
{
private static final long serialVersionUID = 1L;
/** 总记录数 */
private long total;
/** 列表数据 */
private List<?> rows;
/**
* 表格数据对象
*/
public TableDataInfo()
{
}
/**
* 分页
*
* @param list 列表数据
* @param total 总记录数
*/
public TableDataInfo(List<?> list, int total)
{
this.rows = list;
this.total = total;
}
public long getTotal()
{
return total;
}
public void setTotal(long total)
{
this.total = total;
}
public List<?> getRows()
{
return rows;
}
public void setRows(List<?> rows)
{
this.rows = rows;
}
}

View File

@ -0,0 +1,31 @@
package com.ruoyi.framework.web.page;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.constant.Constants;
/**
* 表格数据处理
*
* @author ruoyi
*/
public class TableSupport
{
/**
* 封装分页对象
*/
public static PageDomain getPageDomain()
{
PageDomain pageDomain = new PageDomain();
pageDomain.setPageNum(ServletUtils.getParameterToInt(Constants.PAGENUM));
pageDomain.setPageSize(ServletUtils.getParameterToInt(Constants.PAGESIZE));
pageDomain.setOrderByColumn(ServletUtils.getParameter(Constants.ORDERBYCOLUMN));
pageDomain.setIsAsc(ServletUtils.getParameter(Constants.ISASC));
return pageDomain;
}
public static PageDomain buildPageRequest()
{
return getPageDomain();
}
}

View File

@ -0,0 +1,29 @@
package com.ruoyi.framework.web.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.ruoyi.project.system.config.service.IConfigService;
/**
* RuoYi首创 html调用 thymeleaf 实现参数管理
*
* @author ruoyi
*/
@Component
public class ConfigService
{
@Autowired
private IConfigService configService;
/**
* 根据键名查询参数配置信息
*
* @param configName 参数名称
* @return 参数键值
*/
public String selectConfigByKey(String configKey)
{
return configService.selectConfigByKey(configKey);
}
}

View File

@ -0,0 +1,30 @@
package com.ruoyi.framework.web.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.ruoyi.project.system.dict.domain.DictData;
import com.ruoyi.project.system.dict.service.IDictDataService;
/**
* RuoYi首创 html调用 thymeleaf 实现字典读取
*
* @author ruoyi
*/
@Component
public class DictService
{
@Autowired
private IDictDataService dictDataService;
/**
* 根据字典类型查询字典数据信息
*
* @param dictType 字典类型
* @return 参数键值
*/
public List<DictData> selectDictData(String dictType)
{
return dictDataService.selectDictDataByType(dictType);
}
}

View File

@ -0,0 +1,84 @@
package com.ruoyi.project.common;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.util.ResourceUtils;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* 通用请求处理
*
* @author ruoyi
*/
@Controller
public class CommonController
{
@RequestMapping("common/download")
public void fileDownload(String fileName, Boolean delete, HttpServletResponse response, HttpServletRequest request)
{
String realFileName = System.currentTimeMillis() + fileName.substring(fileName.indexOf("_") + 1);
try
{
String filePath = ResourceUtils.getURL("classpath:").getPath() + "static/file/" + fileName;
response.setCharacterEncoding("utf-8");
response.setContentType("multipart/form-data");
response.setHeader("Content-Disposition", "attachment;fileName=" + setFileDownloadHeader(request, realFileName));
File file = new File(filePath);
InputStream inputStream = new FileInputStream(file);
OutputStream os = response.getOutputStream();
byte[] b = new byte[1024];
int length;
while ((length = inputStream.read(b)) > 0)
{
os.write(b, 0, length);
}
os.close();
inputStream.close();
if (delete && file.exists())
{
file.delete();
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
public 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;
}
}

View File

@ -0,0 +1,26 @@
package com.ruoyi.project.monitor.druid;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import com.ruoyi.framework.web.controller.BaseController;
/**
* druid 监控
*
* @author ruoyi
*/
@Controller
@RequestMapping("/monitor/data")
public class DruidController extends BaseController
{
private String prefix = "/monitor/druid";
@RequiresPermissions("monitor:data:view")
@GetMapping()
public String index()
{
return redirect(prefix + "/index");
}
}

View File

@ -0,0 +1,159 @@
package com.ruoyi.project.monitor.job.controller;
import java.util.List;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.constant.BusinessType;
import com.ruoyi.framework.web.controller.BaseController;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.framework.web.page.TableDataInfo;
import com.ruoyi.project.monitor.job.domain.Job;
import com.ruoyi.project.monitor.job.service.IJobService;
/**
* 调度任务信息操作处理
*
* @author ruoyi
*/
@Controller
@RequestMapping("/monitor/job")
public class JobController extends BaseController
{
private String prefix = "monitor/job";
@Autowired
private IJobService jobService;
@RequiresPermissions("monitor:job:view")
@GetMapping()
public String job()
{
return prefix + "/job";
}
@RequiresPermissions("monitor:job:list")
@PostMapping("/list")
@ResponseBody
public TableDataInfo list(Job job)
{
startPage();
List<Job> list = jobService.selectJobList(job);
return getDataTable(list);
}
@Log(title = "定时任务", action = BusinessType.EXPORT)
@PostMapping("/export")
@ResponseBody
public AjaxResult export(Job job) throws Exception
{
try
{
List<Job> list = jobService.selectJobList(job);
ExcelUtil<Job> util = new ExcelUtil<Job>(Job.class);
return util.exportExcel(list, "job");
}
catch (Exception e)
{
return error("导出Excel失败请联系网站管理员");
}
}
@Log(title = "定时任务", action = BusinessType.DELETE)
@RequiresPermissions("monitor:job:remove")
@PostMapping("/remove")
@ResponseBody
public AjaxResult remove(String ids)
{
try
{
jobService.deleteJobByIds(ids);
return success();
}
catch (Exception e)
{
e.printStackTrace();
return error(e.getMessage());
}
}
/**
* 任务调度状态修改
*/
@Log(title = "定时任务", action = BusinessType.UPDATE)
@RequiresPermissions("monitor:job:changeStatus")
@PostMapping("/changeStatus")
@ResponseBody
public AjaxResult changeStatus(Job job)
{
if (jobService.changeStatus(job) > 0)
{
return success();
}
return error();
}
/**
* 任务调度立即执行一次
*/
@Log(title = "定时任务", action = BusinessType.UPDATE)
@RequiresPermissions("monitor:job:changeStatus")
@PostMapping("/run")
@ResponseBody
public AjaxResult run(Job job)
{
if (jobService.run(job) > 0)
{
return success();
}
return error();
}
/**
* 新增调度
*/
@Log(title = "定时任务", action = BusinessType.INSERT)
@RequiresPermissions("monitor:job:add")
@GetMapping("/add")
public String add(Model model)
{
return prefix + "/add";
}
/**
* 修改调度
*/
@Log(title = "定时任务", action = BusinessType.UPDATE)
@RequiresPermissions("monitor:job:edit")
@GetMapping("/edit/{jobId}")
public String edit(@PathVariable("jobId") Long jobId, Model model)
{
Job job = jobService.selectJobById(jobId);
model.addAttribute("job", job);
return prefix + "/edit";
}
/**
* 保存调度
*/
@Log(title = "定时任务", action = BusinessType.SAVE)
@RequiresPermissions("monitor:job:save")
@PostMapping("/save")
@ResponseBody
public AjaxResult save(Job job)
{
if (jobService.saveJobCron(job) > 0)
{
return success();
}
return error();
}
}

View File

@ -0,0 +1,85 @@
package com.ruoyi.project.monitor.job.controller;
import java.util.List;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.constant.BusinessType;
import com.ruoyi.framework.web.controller.BaseController;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.framework.web.page.TableDataInfo;
import com.ruoyi.project.monitor.job.domain.JobLog;
import com.ruoyi.project.monitor.job.service.IJobLogService;
/**
* 调度日志操作处理
*
* @author ruoyi
*/
@Controller
@RequestMapping("/monitor/jobLog")
public class JobLogController extends BaseController
{
private String prefix = "monitor/job";
@Autowired
private IJobLogService jobLogService;
@RequiresPermissions("monitor:job:view")
@GetMapping()
public String jobLog()
{
return prefix + "/jobLog";
}
@RequiresPermissions("monitor:job:list")
@PostMapping("/list")
@ResponseBody
public TableDataInfo list(JobLog jobLog)
{
startPage();
List<JobLog> list = jobLogService.selectJobLogList(jobLog);
return getDataTable(list);
}
@Log(title = "调度日志", action = BusinessType.EXPORT)
@PostMapping("/export")
@ResponseBody
public AjaxResult export(JobLog jobLog) throws Exception
{
try
{
List<JobLog> list = jobLogService.selectJobLogList(jobLog);
ExcelUtil<JobLog> util = new ExcelUtil<JobLog>(JobLog.class);
return util.exportExcel(list, "jobLog");
}
catch (Exception e)
{
return error("导出Excel失败请联系网站管理员");
}
}
@Log(title = "调度日志", action = BusinessType.DELETE)
@RequiresPermissions("monitor:job:remove")
@PostMapping("/remove")
@ResponseBody
public AjaxResult remove(String ids)
{
try
{
jobLogService.deleteJobLogByIds(ids);
return success();
}
catch (Exception e)
{
e.printStackTrace();
return error(e.getMessage());
}
}
}

View File

@ -0,0 +1,122 @@
package com.ruoyi.project.monitor.job.domain;
import java.io.Serializable;
import com.ruoyi.framework.aspectj.lang.annotation.Excel;
import com.ruoyi.framework.web.domain.BaseEntity;
/**
* 定时任务调度信息 sys_job
*
* @author ruoyi
*/
public class Job extends BaseEntity implements Serializable
{
private static final long serialVersionUID = 1L;
/** 任务ID */
@Excel(name = "任务序号", column = "A")
private Long jobId;
/** 任务名称 */
@Excel(name = "任务名称", column = "B")
private String jobName;
/** 任务组名 */
@Excel(name = "任务组名", column = "C")
private String jobGroup;
/** 任务方法 */
@Excel(name = "任务方法", column = "D")
private String methodName;
/** 方法参数 */
@Excel(name = "方法参数", column = "E")
private String params;
/** cron执行表达式 */
@Excel(name = "执行表达式 ", column = "F")
private String cronExpression;
/** 任务状态0正常 1暂停 */
@Excel(name = "任务状态", column = "G")
private String status;
public Long getJobId()
{
return jobId;
}
public void setJobId(Long jobId)
{
this.jobId = jobId;
}
public String getJobName()
{
return jobName;
}
public void setJobName(String jobName)
{
this.jobName = jobName;
}
public String getJobGroup()
{
return jobGroup;
}
public void setJobGroup(String jobGroup)
{
this.jobGroup = jobGroup;
}
public String getMethodName()
{
return methodName;
}
public void setMethodName(String methodName)
{
this.methodName = methodName;
}
public String getParams()
{
return params;
}
public void setParams(String params)
{
this.params = params;
}
public String getCronExpression()
{
return cronExpression;
}
public void setCronExpression(String cronExpression)
{
this.cronExpression = cronExpression;
}
public String getStatus()
{
return status;
}
public void setStatus(String status)
{
this.status = status;
}
@Override
public String toString()
{
return "Job [jobId=" + jobId + ", jobName=" + jobName + ", jobGroup=" + jobGroup + ", methodName=" + methodName
+ ", params=" + params + ", cronExpression=" + cronExpression + ", status=" + status + "]";
}
}

View File

@ -0,0 +1,135 @@
package com.ruoyi.project.monitor.job.domain;
import com.ruoyi.framework.aspectj.lang.annotation.Excel;
import com.ruoyi.framework.web.domain.BaseEntity;
/**
* 定时任务调度日志信息 sys_job_log
*
* @author ruoyi
*/
public class JobLog extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** ID */
@Excel(name = "日志序号", column = "A")
private Long jobLogId;
/** 任务名称 */
@Excel(name = "任务名称", column = "B")
private String jobName;
/** 任务组名 */
@Excel(name = "任务组名", column = "C")
private String jobGroup;
/** 任务方法 */
@Excel(name = "任务方法", column = "D")
private String methodName;
/** 方法参数 */
@Excel(name = "方法参数", column = "E")
private String params;
/** 日志信息 */
@Excel(name = "日志信息", column = "F")
private String jobMessage;
/** 执行状态0正常 1失败 */
@Excel(name = "执行状态", column = "G")
private String status;
/** 异常信息 */
@Excel(name = "异常信息", column = "H")
private String exceptionInfo;
public Long getJobLogId()
{
return jobLogId;
}
public void setJobLogId(Long jobLogId)
{
this.jobLogId = jobLogId;
}
public String getJobName()
{
return jobName;
}
public void setJobName(String jobName)
{
this.jobName = jobName;
}
public String getJobGroup()
{
return jobGroup;
}
public void setJobGroup(String jobGroup)
{
this.jobGroup = jobGroup;
}
public String getMethodName()
{
return methodName;
}
public void setMethodName(String methodName)
{
this.methodName = methodName;
}
public String getParams()
{
return params;
}
public void setParams(String params)
{
this.params = params;
}
public String getJobMessage()
{
return jobMessage;
}
public void setJobMessage(String jobMessage)
{
this.jobMessage = jobMessage;
}
public String getStatus()
{
return status;
}
public void setStatus(String status)
{
this.status = status;
}
public String getExceptionInfo()
{
return exceptionInfo;
}
public void setExceptionInfo(String exceptionInfo)
{
this.exceptionInfo = exceptionInfo;
}
@Override
public String toString()
{
return "JobLog [jobLogId=" + jobLogId + ", jobName=" + jobName + ", jobGroup=" + jobGroup + ", methodName="
+ methodName + ", params=" + params + ", jobMessage=" + jobMessage + ", status=" + status
+ ", exceptionInfo=" + exceptionInfo + "]";
}
}

View File

@ -0,0 +1,54 @@
package com.ruoyi.project.monitor.job.mapper;
import java.util.List;
import com.ruoyi.project.monitor.job.domain.JobLog;
/**
* 调度任务日志信息 数据层
*
* @author ruoyi
*/
public interface JobLogMapper
{
/**
* 获取quartz调度器日志的计划任务
*
* @param jobLog 调度日志信息
* @return 调度任务日志集合
*/
public List<JobLog> selectJobLogList(JobLog jobLog);
/**
* 通过调度任务日志ID查询调度信息
*
* @param jobLogId 调度任务日志ID
* @return 调度任务日志对象信息
*/
public JobLog selectJobLogById(Long jobLogId);
/**
* 新增任务日志
*
* @param jobLog 调度日志信息
* @return 结果
*/
public int insertJobLog(JobLog jobLog);
/**
* 批量删除调度日志信息
*
* @param ids 需要删除的数据ID
* @return 结果
*/
public int deleteJobLogByIds(String[] ids);
/**
* 删除任务日志
*
* @param jobId 调度日志ID
* @return 结果
*/
public int deleteJobLogById(Long jobId);
}

View File

@ -0,0 +1,69 @@
package com.ruoyi.project.monitor.job.mapper;
import java.util.List;
import com.ruoyi.project.monitor.job.domain.Job;
/**
* 调度任务信息 数据层
*
* @author ruoyi
*/
public interface JobMapper
{
/**
* 查询调度任务日志集合
*
* @param job 调度信息
* @return 操作日志集合
*/
public List<Job> selectJobList(Job job);
/**
* 查询所有调度任务
*
* @return 调度任务列表
*/
public List<Job> selectJobAll();
/**
* 通过调度ID查询调度任务信息
*
* @param jobId 调度ID
* @return 角色对象信息
*/
public Job selectJobById(Long jobId);
/**
* 通过调度ID删除调度任务信息
*
* @param jobId 调度ID
* @return 结果
*/
public int deleteJobById(Job job);
/**
* 批量删除调度任务信息
*
* @param ids 需要删除的数据ID
* @return 结果
*/
public int deleteJobLogByIds(Long[] ids);
/**
* 修改调度任务信息
*
* @param job 调度任务信息
* @return 结果
*/
public int updateJob(Job job);
/**
* 新增调度任务信息
*
* @param job 调度任务信息
* @return 结果
*/
public int insertJob(Job job);
}

View File

@ -0,0 +1,53 @@
package com.ruoyi.project.monitor.job.service;
import java.util.List;
import com.ruoyi.project.monitor.job.domain.JobLog;
/**
* 定时任务调度日志信息信息 服务层
*
* @author ruoyi
*/
public interface IJobLogService
{
/**
* 获取quartz调度器日志的计划任务
*
* @param jobLog 调度日志信息
* @return 调度任务日志集合
*/
public List<JobLog> selectJobLogList(JobLog jobLog);
/**
* 通过调度任务日志ID查询调度信息
*
* @param jobLogId 调度任务日志ID
* @return 调度任务日志对象信息
*/
public JobLog selectJobLogById(Long jobLogId);
/**
* 新增任务日志
*
* @param jobLog 调度日志信息
*/
public void addJobLog(JobLog jobLog);
/**
* 批量删除调度日志信息
*
* @param ids 需要删除的数据ID
* @return 结果
*/
public int deleteJobLogByIds(String ids);
/**
* 删除任务日志
*
* @param jobId 调度日志ID
* @return 结果
*/
public int deleteJobLogById(Long jobId);
}

View File

@ -0,0 +1,101 @@
package com.ruoyi.project.monitor.job.service;
import java.util.List;
import com.ruoyi.project.monitor.job.domain.Job;
/**
* 定时任务调度信息信息 服务层
*
* @author ruoyi
*/
public interface IJobService
{
/**
* 获取quartz调度器的计划任务
*
* @param job 调度信息
* @return 调度任务集合
*/
public List<Job> selectJobList(Job job);
/**
* 通过调度任务ID查询调度信息
*
* @param jobId 调度任务ID
* @return 调度任务对象信息
*/
public Job selectJobById(Long jobId);
/**
* 暂停任务
*
* @param job 调度信息
* @return 结果
*/
public int pauseJob(Job job);
/**
* 恢复任务
*
* @param job 调度信息
* @return 结果
*/
public int resumeJob(Job job);
/**
* 删除任务后所对应的trigger也将被删除
*
* @param job 调度信息
* @return 结果
*/
public int deleteJob(Job job);
/**
* 批量删除调度信息
*
* @param ids 需要删除的数据ID
* @return 结果
*/
public void deleteJobByIds(String ids);
/**
* 任务调度状态修改
*
* @param job 调度信息
* @return 结果
*/
public int changeStatus(Job job);
/**
* 立即运行任务
*
* @param job 调度信息
* @return 结果
*/
public int run(Job job);
/**
* 新增任务表达式
*
* @param job 调度信息
* @return 结果
*/
public int addJobCron(Job job);
/**
* 更新任务的时间表达式
*
* @param job 调度信息
* @return 结果
*/
public int updateJobCron(Job job);
/**
* 保存任务的时间表达式
*
* @param job 调度信息
* @return 结果
*/
public int saveJobCron(Job job);
}

View File

@ -0,0 +1,80 @@
package com.ruoyi.project.monitor.job.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.common.support.Convert;
import com.ruoyi.project.monitor.job.domain.JobLog;
import com.ruoyi.project.monitor.job.mapper.JobLogMapper;
/**
* 定时任务调度日志信息 服务层
*
* @author ruoyi
*/
@Service("jobLogService")
public class JobLogServiceImpl implements IJobLogService
{
@Autowired
private JobLogMapper jobLogMapper;
/**
* 获取quartz调度器日志的计划任务
*
* @param jobLog 调度日志信息
* @return 调度任务日志集合
*/
@Override
public List<JobLog> selectJobLogList(JobLog jobLog)
{
return jobLogMapper.selectJobLogList(jobLog);
}
/**
* 通过调度任务日志ID查询调度信息
*
* @param jobId 调度任务日志ID
* @return 调度任务日志对象信息
*/
@Override
public JobLog selectJobLogById(Long jobLogId)
{
return jobLogMapper.selectJobLogById(jobLogId);
}
/**
* 新增任务日志
*
* @param jobLog 调度日志信息
*/
@Override
public void addJobLog(JobLog jobLog)
{
jobLogMapper.insertJobLog(jobLog);
}
/**
* 批量删除调度日志信息
*
* @param ids 需要删除的数据ID
* @return 结果
*/
@Override
public int deleteJobLogByIds(String ids)
{
return jobLogMapper.deleteJobLogByIds(Convert.toStrArray(ids));
}
/**
* 删除任务日志
*
* @param jobId 调度日志ID
*/
@Override
public int deleteJobLogById(Long jobId)
{
return jobLogMapper.deleteJobLogById(jobId);
}
}

View File

@ -0,0 +1,236 @@
package com.ruoyi.project.monitor.job.service;
import java.util.List;
import javax.annotation.PostConstruct;
import org.quartz.CronTrigger;
import org.quartz.Scheduler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.common.constant.ScheduleConstants;
import com.ruoyi.common.support.Convert;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.security.ShiroUtils;
import com.ruoyi.project.monitor.job.domain.Job;
import com.ruoyi.project.monitor.job.mapper.JobMapper;
import com.ruoyi.project.monitor.job.util.ScheduleUtils;
/**
* 定时任务调度信息 服务层
*
* @author ruoyi
*/
@Service("jobService")
public class JobServiceImpl implements IJobService
{
@Autowired
private Scheduler scheduler;
@Autowired
private JobMapper jobMapper;
/**
* 项目启动时初始化定时器
*/
@PostConstruct
public void init()
{
List<Job> jobList = jobMapper.selectJobAll();
for (Job job : jobList)
{
CronTrigger cronTrigger = ScheduleUtils.getCronTrigger(scheduler, job.getJobId());
// 如果不存在则创建
if (cronTrigger == null)
{
ScheduleUtils.createScheduleJob(scheduler, job);
}
else
{
ScheduleUtils.updateScheduleJob(scheduler, job);
}
}
}
/**
* 获取quartz调度器的计划任务列表
*
* @param job 调度信息
* @return
*/
@Override
public List<Job> selectJobList(Job job)
{
return jobMapper.selectJobList(job);
}
/**
* 通过调度任务ID查询调度信息
*
* @param jobId 调度任务ID
* @return 调度任务对象信息
*/
@Override
public Job selectJobById(Long jobId)
{
return jobMapper.selectJobById(jobId);
}
/**
* 暂停任务
*
* @param job 调度信息
*/
@Override
public int pauseJob(Job job)
{
job.setStatus(ScheduleConstants.Status.PAUSE.getValue());
job.setUpdateBy(ShiroUtils.getLoginName());
int rows = jobMapper.updateJob(job);
if (rows > 0)
{
ScheduleUtils.pauseJob(scheduler, job.getJobId());
}
return rows;
}
/**
* 恢复任务
*
* @param job 调度信息
*/
@Override
public int resumeJob(Job job)
{
job.setStatus(ScheduleConstants.Status.NORMAL.getValue());
job.setUpdateBy(ShiroUtils.getLoginName());
int rows = jobMapper.updateJob(job);
if (rows > 0)
{
ScheduleUtils.resumeJob(scheduler, job.getJobId());
}
return rows;
}
/**
* 删除任务后所对应的trigger也将被删除
*
* @param job 调度信息
*/
@Override
public int deleteJob(Job job)
{
int rows = jobMapper.deleteJobById(job);
if (rows > 0)
{
ScheduleUtils.deleteScheduleJob(scheduler, job.getJobId());
}
return rows;
}
/**
* 批量删除调度信息
*
* @param ids 需要删除的数据ID
* @return 结果
*/
@Override
public void deleteJobByIds(String ids)
{
Long[] jobIds = Convert.toLongArray(ids);
for (Long jobId : jobIds)
{
Job job = jobMapper.selectJobById(jobId);
deleteJob(job);
}
}
/**
* 任务调度状态修改
*
* @param job 调度信息
*/
@Override
public int changeStatus(Job job)
{
int rows = 0;
String status = job.getStatus();
if (ScheduleConstants.Status.NORMAL.getValue().equals(status))
{
rows = resumeJob(job);
}
else if (ScheduleConstants.Status.PAUSE.getValue().equals(status))
{
rows = pauseJob(job);
}
return rows;
}
/**
* 立即运行任务
*
* @param job 调度信息
*/
@Override
public int run(Job job)
{
return ScheduleUtils.run(scheduler, selectJobById(job.getJobId()));
}
/**
* 新增任务
*
* @param job 调度信息 调度信息
*/
@Override
public int addJobCron(Job job)
{
job.setCreateBy(ShiroUtils.getLoginName());
job.setStatus(ScheduleConstants.Status.PAUSE.getValue());
int rows = jobMapper.insertJob(job);
if (rows > 0)
{
ScheduleUtils.createScheduleJob(scheduler, job);
}
return rows;
}
/**
* 更新任务的时间表达式
*
* @param job 调度信息
*/
@Override
public int updateJobCron(Job job)
{
int rows = jobMapper.updateJob(job);
if (rows > 0)
{
ScheduleUtils.updateScheduleJob(scheduler, job);
}
return rows;
}
/**
* 保存任务的时间表达式
*
* @param job 调度信息
*/
@Override
public int saveJobCron(Job job)
{
Long jobId = job.getJobId();
int rows = 0;
if (StringUtils.isNotNull(jobId))
{
rows = updateJobCron(job);
}
else
{
rows = addJobCron(job);
}
return rows;
}
}

View File

@ -0,0 +1,24 @@
package com.ruoyi.project.monitor.job.task;
import org.springframework.stereotype.Component;
/**
* 定时任务调度测试
*
* @author ruoyi
*/
@Component("ryTask")
public class RyTask
{
public void ryParams(String params)
{
System.out.println("执行有参方法:" + params);
}
public void ryNoParams()
{
System.out.println("执行无参方法");
}
}

View File

@ -0,0 +1,88 @@
package com.ruoyi.project.monitor.job.util;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import org.apache.commons.beanutils.PropertyUtils;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.quartz.QuartzJobBean;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.constant.ScheduleConstants;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.project.monitor.job.domain.Job;
import com.ruoyi.project.monitor.job.domain.JobLog;
import com.ruoyi.project.monitor.job.service.IJobLogService;
/**
* 定时任务
*
* @author ruoyi
*
*/
public class ScheduleJob extends QuartzJobBean
{
private static final Logger log = LoggerFactory.getLogger(ScheduleJob.class);
private ExecutorService service = Executors.newSingleThreadExecutor();
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException
{
// Job job = (Job) context.getMergedJobDataMap().get(ScheduleConstants.JOB_PARAM_KEY);
JobDataMap jobDataMap = context.getMergedJobDataMap();
Job job = new Job();
try
{
PropertyUtils.copyProperties(job, jobDataMap.get(ScheduleConstants.JOB_PARAM_KEY));
}
catch (Exception e)
{
log.error("copyProperties执行异常 - ", e);
}
IJobLogService jobLogService = (IJobLogService) SpringUtils.getBean(IJobLogService.class);
JobLog jobLog = new JobLog();
jobLog.setJobName(job.getJobName());
jobLog.setJobGroup(job.getJobGroup());
jobLog.setMethodName(job.getMethodName());
jobLog.setParams(job.getParams());
jobLog.setCreateTime(new Date());
long startTime = System.currentTimeMillis();
try
{
// 执行任务
log.info("任务开始执行 - 名称:{} 方法:{}", job.getJobName(), job.getMethodName());
ScheduleRunnable task = new ScheduleRunnable(job.getJobName(), job.getMethodName(), job.getParams());
Future<?> future = service.submit(task);
future.get();
long times = System.currentTimeMillis() - startTime;
// 任务状态 0成功 1失败
jobLog.setStatus(Constants.SUCCESS);
jobLog.setJobMessage(job.getJobName() + " 总共耗时:" + times + "毫秒");
log.info("任务执行结束 - 名称:{} 耗时:{} 毫秒", job.getJobName(), times);
}
catch (Exception e)
{
log.info("任务执行失败 - 名称:{} 方法:{}", job.getJobName(), job.getMethodName());
log.error("任务执行异常 - ", e);
long times = System.currentTimeMillis() - startTime;
jobLog.setJobMessage(job.getJobName() + " 总共耗时:" + times + "毫秒");
// 任务状态 0成功 1失败
jobLog.setStatus(Constants.FAIL);
jobLog.setExceptionInfo(e.toString());
}
finally
{
jobLogService.addJobLog(jobLog);
}
}
}

View File

@ -0,0 +1,59 @@
package com.ruoyi.project.monitor.job.util;
import java.lang.reflect.Method;
import org.springframework.util.ReflectionUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
/**
* 执行定时任务
*
* @author ruoyi
*
*/
public class ScheduleRunnable implements Runnable
{
private Object target;
private Method method;
private String params;
public ScheduleRunnable(String beanName, String methodName, String params)
throws NoSuchMethodException, SecurityException
{
this.target = SpringUtils.getBean(beanName);
this.params = params;
if (StringUtils.isNotEmpty(params))
{
this.method = target.getClass().getDeclaredMethod(methodName, String.class);
}
else
{
this.method = target.getClass().getDeclaredMethod(methodName);
}
}
@Override
public void run()
{
try
{
ReflectionUtils.makeAccessible(method);
if (StringUtils.isNotEmpty(params))
{
method.invoke(target, params);
}
else
{
method.invoke(target);
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
}

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