RuoYi преди 6 години
родител
ревизия
ffd68de51b
променени са 100 файла, в които са добавени 14012 реда и са изтрити 0 реда
  1. 20 0
      LICENSE
  2. 54 0
      README.md
  3. BIN
      doc/若依环境使用手册v1.0.1.docx
  4. 290 0
      pom.xml
  5. 170 0
      sql/quartz.sql
  6. 242 0
      sql/ruoyi.html
  7. 4068 0
      sql/ruoyi.pdm
  8. 594 0
      sql/ry_20180701.sql
  9. 33 0
      src/main/java/com/ruoyi/RuoYiApplication.java
  10. 19 0
      src/main/java/com/ruoyi/RuoYiServletInitializer.java
  11. 46 0
      src/main/java/com/ruoyi/common/constant/CommonMap.java
  12. 65 0
      src/main/java/com/ruoyi/common/constant/Constants.java
  13. 39 0
      src/main/java/com/ruoyi/common/constant/ScheduleConstants.java
  14. 65 0
      src/main/java/com/ruoyi/common/constant/ShiroConstants.java
  15. 80 0
      src/main/java/com/ruoyi/common/constant/UserConstants.java
  16. 15 0
      src/main/java/com/ruoyi/common/exception/DemoModeException.java
  17. 105 0
      src/main/java/com/ruoyi/common/exception/base/BaseException.java
  18. 41 0
      src/main/java/com/ruoyi/common/exception/file/FileNameLengthLimitExceededException.java
  19. 74 0
      src/main/java/com/ruoyi/common/exception/file/InvalidExtensionException.java
  20. 16 0
      src/main/java/com/ruoyi/common/exception/user/CaptchaException.java
  21. 18 0
      src/main/java/com/ruoyi/common/exception/user/RoleBlockedException.java
  22. 16 0
      src/main/java/com/ruoyi/common/exception/user/UserBlockedException.java
  23. 20 0
      src/main/java/com/ruoyi/common/exception/user/UserException.java
  24. 17 0
      src/main/java/com/ruoyi/common/exception/user/UserNotExistsException.java
  25. 17 0
      src/main/java/com/ruoyi/common/exception/user/UserPasswordNotMatchException.java
  26. 16 0
      src/main/java/com/ruoyi/common/exception/user/UserPasswordRetryLimitCountException.java
  27. 16 0
      src/main/java/com/ruoyi/common/exception/user/UserPasswordRetryLimitExceedException.java
  28. 88 0
      src/main/java/com/ruoyi/common/support/CharsetKit.java
  29. 1002 0
      src/main/java/com/ruoyi/common/support/Convert.java
  30. 95 0
      src/main/java/com/ruoyi/common/support/StrFormatter.java
  31. 96 0
      src/main/java/com/ruoyi/common/utils/AddressUtils.java
  32. 100 0
      src/main/java/com/ruoyi/common/utils/DateUtils.java
  33. 155 0
      src/main/java/com/ruoyi/common/utils/FileUploadUtils.java
  34. 43 0
      src/main/java/com/ruoyi/common/utils/IpUtils.java
  35. 136 0
      src/main/java/com/ruoyi/common/utils/LogUtils.java
  36. 51 0
      src/main/java/com/ruoyi/common/utils/MapDataUtil.java
  37. 27 0
      src/main/java/com/ruoyi/common/utils/MessageUtils.java
  38. 89 0
      src/main/java/com/ruoyi/common/utils/ServletUtils.java
  39. 373 0
      src/main/java/com/ruoyi/common/utils/StringUtils.java
  40. 71 0
      src/main/java/com/ruoyi/common/utils/SystemLogUtils.java
  41. 145 0
      src/main/java/com/ruoyi/common/utils/TreeUtils.java
  42. 409 0
      src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java
  43. 76 0
      src/main/java/com/ruoyi/common/utils/security/ShiroUtils.java
  44. 102 0
      src/main/java/com/ruoyi/common/utils/spring/SpringUtils.java
  45. 86 0
      src/main/java/com/ruoyi/common/xss/XssFilter.java
  46. 41 0
      src/main/java/com/ruoyi/common/xss/XssHttpServletRequestWrapper.java
  47. 178 0
      src/main/java/com/ruoyi/framework/aspectj/LogAspect.java
  48. 41 0
      src/main/java/com/ruoyi/framework/aspectj/lang/annotation/Excel.java
  49. 33 0
      src/main/java/com/ruoyi/framework/aspectj/lang/annotation/Log.java
  50. 19 0
      src/main/java/com/ruoyi/framework/aspectj/lang/constant/BusinessStatus.java
  51. 33 0
      src/main/java/com/ruoyi/framework/aspectj/lang/constant/BusinessType.java
  52. 22 0
      src/main/java/com/ruoyi/framework/aspectj/lang/constant/OperatorType.java
  53. 61 0
      src/main/java/com/ruoyi/framework/config/CaptchaConfig.java
  54. 161 0
      src/main/java/com/ruoyi/framework/config/DruidConfig.java
  55. 36 0
      src/main/java/com/ruoyi/framework/config/FilterConfig.java
  56. 71 0
      src/main/java/com/ruoyi/framework/config/GenConfig.java
  57. 44 0
      src/main/java/com/ruoyi/framework/config/I18nConfig.java
  58. 77 0
      src/main/java/com/ruoyi/framework/config/KaptchaTextCreator.java
  59. 42 0
      src/main/java/com/ruoyi/framework/config/ResourcesConfig.java
  60. 64 0
      src/main/java/com/ruoyi/framework/config/RuoYiConfig.java
  61. 56 0
      src/main/java/com/ruoyi/framework/config/ScheduleConfig.java
  62. 360 0
      src/main/java/com/ruoyi/framework/config/ShiroConfig.java
  63. 58 0
      src/main/java/com/ruoyi/framework/config/SwaggerConfig.java
  64. 126 0
      src/main/java/com/ruoyi/framework/shiro/realm/UserRealm.java
  65. 128 0
      src/main/java/com/ruoyi/framework/shiro/service/LoginService.java
  66. 101 0
      src/main/java/com/ruoyi/framework/shiro/service/PasswordService.java
  67. 31 0
      src/main/java/com/ruoyi/framework/shiro/service/PermissionService.java
  68. 115 0
      src/main/java/com/ruoyi/framework/shiro/session/OnlineSessionDAO.java
  69. 56 0
      src/main/java/com/ruoyi/framework/shiro/session/OnlineSessionFactory.java
  70. 86 0
      src/main/java/com/ruoyi/framework/shiro/web/filter/LogoutFilter.java
  71. 78 0
      src/main/java/com/ruoyi/framework/shiro/web/filter/captcha/CaptchaValidateFilter.java
  72. 97 0
      src/main/java/com/ruoyi/framework/shiro/web/filter/online/OnlineSessionFilter.java
  73. 42 0
      src/main/java/com/ruoyi/framework/shiro/web/filter/sync/SyncOnlineSessionFilter.java
  74. 155 0
      src/main/java/com/ruoyi/framework/shiro/web/session/OnlineWebSessionManager.java
  75. 141 0
      src/main/java/com/ruoyi/framework/shiro/web/session/SpringSessionValidationScheduler.java
  76. 131 0
      src/main/java/com/ruoyi/framework/web/controller/BaseController.java
  77. 94 0
      src/main/java/com/ruoyi/framework/web/domain/AjaxResult.java
  78. 98 0
      src/main/java/com/ruoyi/framework/web/domain/BaseEntity.java
  79. 72 0
      src/main/java/com/ruoyi/framework/web/exception/DefaultExceptionHandler.java
  80. 70 0
      src/main/java/com/ruoyi/framework/web/page/PageDomain.java
  81. 58 0
      src/main/java/com/ruoyi/framework/web/page/TableDataInfo.java
  82. 31 0
      src/main/java/com/ruoyi/framework/web/page/TableSupport.java
  83. 29 0
      src/main/java/com/ruoyi/framework/web/service/ConfigService.java
  84. 30 0
      src/main/java/com/ruoyi/framework/web/service/DictService.java
  85. 84 0
      src/main/java/com/ruoyi/project/common/CommonController.java
  86. 26 0
      src/main/java/com/ruoyi/project/monitor/druid/DruidController.java
  87. 143 0
      src/main/java/com/ruoyi/project/monitor/job/controller/JobController.java
  88. 85 0
      src/main/java/com/ruoyi/project/monitor/job/controller/JobLogController.java
  89. 122 0
      src/main/java/com/ruoyi/project/monitor/job/domain/Job.java
  90. 135 0
      src/main/java/com/ruoyi/project/monitor/job/domain/JobLog.java
  91. 54 0
      src/main/java/com/ruoyi/project/monitor/job/mapper/JobLogMapper.java
  92. 69 0
      src/main/java/com/ruoyi/project/monitor/job/mapper/JobMapper.java
  93. 53 0
      src/main/java/com/ruoyi/project/monitor/job/service/IJobLogService.java
  94. 101 0
      src/main/java/com/ruoyi/project/monitor/job/service/IJobService.java
  95. 80 0
      src/main/java/com/ruoyi/project/monitor/job/service/JobLogServiceImpl.java
  96. 241 0
      src/main/java/com/ruoyi/project/monitor/job/service/JobServiceImpl.java
  97. 24 0
      src/main/java/com/ruoyi/project/monitor/job/task/RyTask.java
  98. 77 0
      src/main/java/com/ruoyi/project/monitor/job/util/ScheduleJob.java
  99. 59 0
      src/main/java/com/ruoyi/project/monitor/job/util/ScheduleRunnable.java
  100. 193 0
      src/main/java/com/ruoyi/project/monitor/job/util/ScheduleUtils.java

+ 20 - 0
LICENSE

@@ -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.

+ 54 - 0
README.md

@@ -0,0 +1,54 @@
+## 平台简介
+
+一直想做一款后台管理系统,看了很多优秀的开源项目但是发现没有合适的。于是利用空闲休息时间开始自己写了一套后台系统。如此有了若依。她可以用于所有的Web应用程序,如网站管理后台,网站会员中心,CMS,CRM,OA。所有前端后台代码封装过后十分精简易上手,出错概率低。同时支持移动客户端访问。系统会陆续更新一些实用功能。
+
+性别男,若依是女儿的名字。
+
+若依基于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、js、xml、sql。
+14. 在线构建器:拖动表单元素生成相应的HTML代码。
+15. 连接池监视:监视当期系统数据库连接池状态,可进行分析SQL找出系统性能瓶颈。
+
+## 在线体验
+> admin/admin123
+
+地址:http://www.ruoyi.club
+
+## 演示图
+
+![登录界面](https://static.oschina.net/uploads/space/2018/0524/212432_qg6h_1438828.png)
+
+![系统首页](https://static.oschina.net/uploads/space/2018/0701/115902_NCkT_1438828.png)
+
+![用户管理](https://static.oschina.net/uploads/space/2018/0701/115839_hYTP_1438828.png)
+
+![用户修改](https://static.oschina.net/uploads/space/2018/0701/115832_x60e_1438828.png)
+
+![头像修改](https://static.oschina.net/uploads/space/2018/0524/212518_Yfce_1438828.png)
+
+![菜单管理](https://static.oschina.net/uploads/space/2018/0524/212531_h3M5_1438828.png)
+
+![角色管理](https://static.oschina.net/uploads/space/2018/0701/115825_EsL2_1438828.png)
+
+## 若依交流群
+
+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`进行添加

BIN
doc/若依环境使用手册v1.0.1.docx


+ 290 - 0
pom.xml

@@ -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.0.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.3.2</shiro.version>
+		<thymeleaf.extras.shiro.version>2.0.0</thymeleaf.extras.shiro.version>
+		<mybatis.spring.boot.starter.version>1.1.1</mybatis.spring.boot.starter.version>
+		<pagehelper.spring.boot.starter.version>1.2.3</pagehelper.spring.boot.starter.version>
+		<fastjson.version>1.2.31</fastjson.version>
+		<druid.version>1.0.28</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>

+ 170 - 0
sql/quartz.sql

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

Файловите разлики са ограничени, защото са твърде много
+ 242 - 0
sql/ruoyi.html


+ 4068 - 0
sql/ruoyi.pdm

@@ -0,0 +1,4068 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?PowerDesigner AppLocale="UTF16" ID="{21C20947-ED50-4632-B638-DC1A02BD948A}" Label="" Name="ruoyi" Objects="175" Symbols="15" Target="MySQL 5.0" Type="{CDE44E21-9669-11D1-9914-006097355D9B}" signature="PDM_DATA_MODEL_XML" version="12.5.0.2169"?>
+<!-- do not edit this file -->
+
+<Model xmlns:a="attribute" xmlns:c="collection" xmlns:o="object">
+
+<o:RootObject Id="o1">
+<c:Children>
+<o:Model Id="o2">
+<a:ObjectID>21C20947-ED50-4632-B638-DC1A02BD948A</a:ObjectID>
+<a:Name>ruoyi</a:Name>
+<a:Code>ruoyi</a:Code>
+<a:CreationDate>1524449337</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:PackageOptionsText>[FolderOptions]
+
+[FolderOptions\Physical Objects]
+GenerationCheckModel=Yes
+GenerationPath=
+GenerationOptions=
+GenerationTasks=
+GenerationTargets=
+GenerationSelections=
+RevPkey=Yes
+RevFkey=Yes
+RevAkey=Yes
+RevCheck=Yes
+RevIndx=Yes
+RevOpts=Yes
+RevViewAsTabl=No
+RevViewOpts=Yes
+RevSystAsTabl=Yes
+RevTablPerm=No
+RevViewPerm=No
+RevProcPerm=No
+RevDbpkPerm=No
+RevSqncPerm=No
+RevAdtPerm=No
+RevUserPriv=No
+RevUserOpts=No
+RevGrpePriv=No
+RevRolePriv=No
+RevDtbsOpts=Yes
+RevDtbsPerm=No
+RevViewIndx=Yes
+RevJidxOpts=Yes
+RevStats=No
+RevCaseSensitive=No
+GenTrgrStdMsg=Yes
+GenTrgrMsgTab=
+GenTrgrMsgNo=
+GenTrgrMsgTxt=
+TrgrPreserve=No
+TrgrIns=Yes
+TrgrUpd=Yes
+TrgrDel=Yes
+TrgrC2Ins=Yes
+TrgrC2Upd=Yes
+TrgrC3=Yes
+TrgrC4=Yes
+TrgrC5=Yes
+TrgrC6=Yes
+TrgrC7=Yes
+TrgrC8=Yes
+TrgrC9=Yes
+TrgrC10=Yes
+TrgrC11=Yes
+TrgrC1=Yes
+TrgrC12Ins=Yes
+TrgrC12Upd=Yes
+TrgrC13=Yes
+UpdateTableStatistics=Yes
+UpdateColumnStatistics=Yes
+
+[FolderOptions\Physical Objects\Database Generation]
+GenScriptName=orders.sql
+GenScriptName0=orders.sql
+GenScriptName1=studentsystem.sql
+GenScriptName2=NetCTOSS.sql
+GenScriptName3=product.sql
+GenScriptName4=voteSystem.sql
+GenScriptName5=.sql
+GenScriptName6=enterpriseManagement.sql
+GenScriptName7=crebas.sql
+GenScriptName8=
+GenScriptName9=
+GenPathName=C:\Users\Administrator\Desktop\
+GenSingleFile=Yes
+GenODBC=No
+GenCheckModel=Yes
+GenScriptPrev=Yes
+GenArchiveModel=No
+GenUseSync=No
+GenSyncChoice=0
+GenSyncArch=
+GenSyncRmg=0
+
+[FolderOptions\Physical Objects\Database Generation\Format]
+GenScriptTitle=Yes
+GenScriptNamLabl=No
+GenScriptQDtbs=No
+GenScriptQOwnr=Yes
+GenScriptCase=0
+GenScriptEncoding=ANSI
+GenScriptNAcct=No
+IdentifierDelimiter=&quot;
+
+[FolderOptions\Physical Objects\Database Generation\Database]
+Create=Yes
+Open=Yes
+Close=Yes
+Drop=Yes
+Permission=No
+
+[FolderOptions\Physical Objects\Database Generation\Database\Create]
+Physical Options=Yes
+Header=Yes
+Footer=Yes
+
+[FolderOptions\Physical Objects\Database Generation\Tablespace]
+Create=Yes
+Drop=Yes
+Comment=Yes
+
+[FolderOptions\Physical Objects\Database Generation\Tablespace\Create]
+Header=Yes
+Footer=Yes
+
+[FolderOptions\Physical Objects\Database Generation\Storage]
+Create=Yes
+Drop=Yes
+Comment=Yes
+
+[FolderOptions\Physical Objects\Database Generation\User]
+Create=Yes
+Grant=Yes
+Drop=Yes
+Comment=Yes
+Privilege=No
+
+[FolderOptions\Physical Objects\Database Generation\User\Create]
+Physical Options=No
+
+[FolderOptions\Physical Objects\Database Generation\Group]
+Create=Yes
+Drop=Yes
+Comment=Yes
+Privilege=No
+
+[FolderOptions\Physical Objects\Database Generation\Role]
+Create=Yes
+Drop=Yes
+Privilege=No
+
+[FolderOptions\Physical Objects\Database Generation\UserDefinedDataType]
+Create=Yes
+Comment=Yes
+Drop=Yes
+
+[FolderOptions\Physical Objects\Database Generation\UserDefinedDataType\Create]
+Default value=Yes
+Check=Yes
+
+[FolderOptions\Physical Objects\Database Generation\AbstractDataType]
+Create=Yes
+Header=Yes
+Footer=Yes
+Drop=Yes
+Comment=Yes
+Install JAVA class=Yes
+Remove JAVA class=Yes
+Permission=No
+
+[FolderOptions\Physical Objects\Database Generation\Rule]
+Create=Yes
+Drop=Yes
+Comment=Yes
+
+[FolderOptions\Physical Objects\Database Generation\Default]
+Create=Yes
+Comment=Yes
+Drop=Yes
+
+[FolderOptions\Physical Objects\Database Generation\Sequence]
+Create=Yes
+Drop=Yes
+Comment=Yes
+Permission=No
+
+[FolderOptions\Physical Objects\Database Generation\Table&amp;&amp;Column]
+
+[FolderOptions\Physical Objects\Database Generation\Table&amp;&amp;Column\Table]
+Create=Yes
+Drop=Yes
+Comment=Yes
+Permission=No
+
+[FolderOptions\Physical Objects\Database Generation\Table&amp;&amp;Column\Table\Create]
+Check=Yes
+Physical Options=Yes
+Header=Yes
+Footer=Yes
+
+[FolderOptions\Physical Objects\Database Generation\Table&amp;&amp;Column\Table\Create\Check]
+Constraint declaration=No
+
+[FolderOptions\Physical Objects\Database Generation\Table&amp;&amp;Column\Column]
+User datatype=Yes
+Default value=Yes
+Check=Yes
+Physical Options=Yes
+Comment=Yes
+
+[FolderOptions\Physical Objects\Database Generation\Table&amp;&amp;Column\Column\Check]
+Constraint declaration=No
+
+[FolderOptions\Physical Objects\Database Generation\Table&amp;&amp;Column\Key]
+
+[FolderOptions\Physical Objects\Database Generation\Table&amp;&amp;Column\Key\Primary key]
+Create=Yes
+Drop=Yes
+Comment=Yes
+
+[FolderOptions\Physical Objects\Database Generation\Table&amp;&amp;Column\Key\Primary key\Create]
+Constraint declaration=No
+Physical Options=Yes
+
+[FolderOptions\Physical Objects\Database Generation\Table&amp;&amp;Column\Key\Alternate key]
+Create=Yes
+Drop=Yes
+Comment=Yes
+
+[FolderOptions\Physical Objects\Database Generation\Table&amp;&amp;Column\Key\Alternate key\Create]
+Constraint declaration=No
+Physical Options=Yes
+
+[FolderOptions\Physical Objects\Database Generation\Table&amp;&amp;Column\Foreign key]
+Create=Yes
+Drop=Yes
+Comment=Yes
+
+[FolderOptions\Physical Objects\Database Generation\Table&amp;&amp;Column\Foreign key\Create]
+Constraint declaration=Yes
+
+[FolderOptions\Physical Objects\Database Generation\Table&amp;&amp;Column\Index]
+Create=Yes
+Drop=Yes
+Comment=Yes
+
+[FolderOptions\Physical Objects\Database Generation\Table&amp;&amp;Column\Index\Create]
+Constraint declaration=Yes
+Physical Options=Yes
+
+[FolderOptions\Physical Objects\Database Generation\Table&amp;&amp;Column\Index\Filter]
+Primary key=Yes
+Foreign key=Yes
+Alternate key=Yes
+Cluster=Yes
+Other=Yes
+
+[FolderOptions\Physical Objects\Database Generation\Table&amp;&amp;Column\Trigger]
+Create=Yes
+Drop=Yes
+Comment=Yes
+
+[FolderOptions\Physical Objects\Database Generation\Table&amp;&amp;Column\Trigger\Filter]
+For insert=Yes
+For update=Yes
+For delete=Yes
+For other=Yes
+
+[FolderOptions\Physical Objects\Database Generation\View]
+Create=Yes
+Drop=Yes
+Comment=Yes
+Permission=No
+
+[FolderOptions\Physical Objects\Database Generation\View\Create]
+Force Column list=No
+Physical Options=Yes
+Header=Yes
+Footer=Yes
+
+[FolderOptions\Physical Objects\Database Generation\View\ViewColumn]
+Comment=Yes
+
+[FolderOptions\Physical Objects\Database Generation\View\ViewIndex]
+Create=Yes
+Drop=Yes
+Comment=Yes
+
+[FolderOptions\Physical Objects\Database Generation\View\ViewIndex\Create]
+Physical Options=Yes
+
+[FolderOptions\Physical Objects\Database Generation\View\ViewIndex\Filter]
+Cluster=Yes
+Other=Yes
+
+[FolderOptions\Physical Objects\Database Generation\View\Trigger]
+Create=Yes
+Drop=Yes
+Comment=Yes
+
+[FolderOptions\Physical Objects\Database Generation\View\Trigger\Filter]
+For insert=Yes
+For update=Yes
+For delete=Yes
+For other=Yes
+
+[FolderOptions\Physical Objects\Database Generation\Synonym]
+Create=Yes
+Drop=Yes
+
+[FolderOptions\Physical Objects\Database Generation\Synonym\Filter]
+Table=Yes
+View=Yes
+Proc=Yes
+Synonym=Yes
+Database Package=Yes
+Sequence=Yes
+
+[FolderOptions\Physical Objects\Database Generation\JoinIndex]
+Create=Yes
+Drop=Yes
+Comment=Yes
+
+[FolderOptions\Physical Objects\Database Generation\JoinIndex\Create]
+Physical Options=Yes
+Header=Yes
+Footer=Yes
+
+[FolderOptions\Physical Objects\Database Generation\Procedure]
+Create=Yes
+Drop=Yes
+Comment=Yes
+Permission=No
+
+[FolderOptions\Physical Objects\Database Generation\Procedure\Create]
+Header=Yes
+Footer=Yes
+
+[FolderOptions\Physical Objects\Database Generation\DatabasePackage]
+Create=Yes
+Drop=Yes
+Permission=No
+
+[FolderOptions\Physical Objects\Database Generation\WebService]
+Create=Yes
+Drop=Yes
+Comment=Yes
+
+[FolderOptions\Physical Objects\Database Generation\Dimension]
+Create=Yes
+Drop=Yes
+
+[FolderOptions\Physical Objects\Database Generation\Synchronization]
+GenBackupTabl=1
+GenKeepBackTabl=1
+GenTmpTablDrop=No
+GenKeepTablOpts=No
+
+[FolderOptions\Physical Objects\Test Data]
+GenDataPathName=
+GenDataSinglefile=Yes
+GenDataScriptName=testdata
+GenDataScriptName0=
+GenDataScriptName1=
+GenDataScriptName2=
+GenDataScriptName3=
+GenDataScriptName4=
+GenDataScriptName5=
+GenDataScriptName6=
+GenDataScriptName7=
+GenDataScriptName8=
+GenDataScriptName9=
+GenDataOdbc=0
+GenDataDelOld=No
+GenDataTitle=No
+GenDataDefNumRows=20
+GenDataCommit=0
+GenDataPacket=0
+GenDataOwner=No
+GenDataProfNumb=
+GenDataProfChar=
+GenDataProfDate=
+GenDataCSVSeparator=,
+GenDataFileFormat=CSV
+GenDataUseWizard=No
+
+[FolderOptions\Pdm]
+IndxIQName=%COLUMN%_%INDEXTYPE%
+IndxPK=Yes
+IndxFK=Yes
+IndxAK=Yes
+IndxPKName=%TABLE%_PK
+IndxFKName=%REFR%_FK
+IndxAKName=%AKEY%_AK
+IndxPreserve=No
+IndxThreshold=0
+IndxStats=No
+RefrPreserve=No
+JidxPreserve=No
+RbldMultiFact=Yes
+RbldMultiDim=Yes
+RbldMultiJidx=Yes
+CubePreserve=No
+TablStProcPreserve=No
+ProcDepPreserve=Yes
+TrgrDepPreserve=Yes
+CubeScriptPath=
+CubeScriptCase=0
+CubeScriptEncoding=ANSI
+CubeScriptNacct=No
+CubeScriptHeader=No
+CubeScriptExt=csv
+CubeScriptExt0=txt
+CubeScriptExt1=
+CubeScriptExt2=
+CubeScriptSep=,
+CubeScriptDeli=&quot;
+DfltDomnName=D_%.U:VALUE%
+DfltColnName=D_%.U:VALUE%
+DfltReuse=Yes
+DfltDrop=Yes</a:PackageOptionsText>
+<a:ModelOptionsText>[ModelOptions]
+
+[ModelOptions\Physical Objects]
+CaseSensitive=No
+DisplayName=Yes
+EnableTrans=No
+EnableRequirements=No
+DefaultDttp=
+IgnoreOwner=No
+RebuildTrigger=Yes
+RefrUnique=No
+RefrAutoMigrate=Yes
+RefrMigrateReuse=Yes
+RefrMigrateDomain=Yes
+RefrMigrateCheck=Yes
+RefrMigrateRule=Yes
+RefrMigrateExtd=No
+RefrMigrDefaultLink=No
+RefrDfltImpl=D
+RefrPrgtColn=No
+RefrMigrateToEnd=No
+RebuildTriggerDep=No
+ColnFKName=%.3:PARENT%_%COLUMN%
+ColnFKNameUse=No
+DomnCopyDttp=Yes
+DomnCopyChck=No
+DomnCopyRule=No
+DomnCopyMand=No
+DomnCopyExtd=No
+DomnCopyProf=No
+Notation=0
+DomnDefaultMandatory=No
+ColnDefaultMandatory=No
+TablDefaultOwner=
+ViewDefaultOwner=
+TrgrDefaultOwnerTabl=
+TrgrDefaultOwnerView=
+IdxDefaultOwnerTabl=
+IdxDefaultOwnerView=
+JdxDefaultOwner=
+DBPackDefaultOwner=
+SeqDefaultOwner=
+ProcDefaultOwner=
+RefrDeleteConstraint=1
+RefrUpdateConstraint=1
+RefrParentMandatory=No
+RefrParentChangeAllow=Yes
+RefrCheckOnCommit=No
+
+[ModelOptions\Physical Objects\NamingOptionsTemplates]
+
+[ModelOptions\Physical Objects\ClssNamingOptions]
+
+[ModelOptions\Physical Objects\ClssNamingOptions\PDMPCKG]
+
+[ModelOptions\Physical Objects\ClssNamingOptions\PDMPCKG\Name]
+Template=
+MaxLen=254
+Case=M
+ValidChar=
+InvldChar=
+AllValid=Yes
+NoAccent=No
+DefaultChar=
+Script=
+ConvTable=
+
+[ModelOptions\Physical Objects\ClssNamingOptions\PDMPCKG\Code]
+Template=
+MaxLen=254
+Case=M
+ValidChar=
+InvldChar=
+AllValid=Yes
+NoAccent=No
+DefaultChar=
+Script=
+ConvTable=
+
+[ModelOptions\Physical Objects\ClssNamingOptions\PDMDOMN]
+
+[ModelOptions\Physical Objects\ClssNamingOptions\PDMDOMN\Name]
+Template=
+MaxLen=254
+Case=M
+ValidChar=
+InvldChar=
+AllValid=Yes
+NoAccent=No
+DefaultChar=
+Script=
+ConvTable=
+
+[ModelOptions\Physical Objects\ClssNamingOptions\PDMDOMN\Code]
+Template=
+MaxLen=254
+Case=M
+ValidChar=
+InvldChar=
+AllValid=Yes
+NoAccent=No
+DefaultChar=
+Script=
+ConvTable=
+
+[ModelOptions\Physical Objects\ClssNamingOptions\TABL]
+
+[ModelOptions\Physical Objects\ClssNamingOptions\TABL\Name]
+Template=
+MaxLen=254
+Case=M
+ValidChar=
+InvldChar=
+AllValid=Yes
+NoAccent=No
+DefaultChar=
+Script=
+ConvTable=
+
+[ModelOptions\Physical Objects\ClssNamingOptions\TABL\Code]
+Template=
+MaxLen=64
+Case=M
+ValidChar=
+InvldChar=
+AllValid=Yes
+NoAccent=No
+DefaultChar=
+Script=
+ConvTable=
+
+[ModelOptions\Physical Objects\ClssNamingOptions\COLN]
+
+[ModelOptions\Physical Objects\ClssNamingOptions\COLN\Name]
+Template=
+MaxLen=254
+Case=M
+ValidChar=
+InvldChar=
+AllValid=Yes
+NoAccent=No
+DefaultChar=
+Script=
+ConvTable=
+
+[ModelOptions\Physical Objects\ClssNamingOptions\COLN\Code]
+Template=
+MaxLen=64
+Case=M
+ValidChar=
+InvldChar=
+AllValid=Yes
+NoAccent=No
+DefaultChar=
+Script=
+ConvTable=
+
+[ModelOptions\Physical Objects\ClssNamingOptions\INDX]
+
+[ModelOptions\Physical Objects\ClssNamingOptions\INDX\Name]
+Template=
+MaxLen=254
+Case=M
+ValidChar=
+InvldChar=
+AllValid=Yes
+NoAccent=No
+DefaultChar=
+Script=
+ConvTable=
+
+[ModelOptions\Physical Objects\ClssNamingOptions\INDX\Code]
+Template=
+MaxLen=254
+Case=M
+ValidChar=
+InvldChar=
+AllValid=Yes
+NoAccent=No
+DefaultChar=
+Script=
+ConvTable=
+
+[ModelOptions\Physical Objects\ClssNamingOptions\REFR]
+
+[ModelOptions\Physical Objects\ClssNamingOptions\REFR\Name]
+Template=
+MaxLen=254
+Case=M
+ValidChar=
+InvldChar=
+AllValid=Yes
+NoAccent=No
+DefaultChar=
+Script=
+ConvTable=
+
+[ModelOptions\Physical Objects\ClssNamingOptions\REFR\Code]
+Template=
+MaxLen=254
+Case=M
+ValidChar=
+InvldChar=
+AllValid=Yes
+NoAccent=No
+DefaultChar=
+Script=
+ConvTable=
+
+[ModelOptions\Physical Objects\ClssNamingOptions\VREF]
+
+[ModelOptions\Physical Objects\ClssNamingOptions\VREF\Name]
+Template=
+MaxLen=254
+Case=M
+ValidChar=
+InvldChar=
+AllValid=Yes
+NoAccent=No
+DefaultChar=
+Script=
+ConvTable=
+
+[ModelOptions\Physical Objects\ClssNamingOptions\VREF\Code]
+Template=
+MaxLen=254
+Case=M
+ValidChar=
+InvldChar=
+AllValid=Yes
+NoAccent=No
+DefaultChar=
+Script=
+ConvTable=
+
+[ModelOptions\Physical Objects\ClssNamingOptions\VIEW]
+
+[ModelOptions\Physical Objects\ClssNamingOptions\VIEW\Name]
+Template=
+MaxLen=254
+Case=M
+ValidChar=
+InvldChar=
+AllValid=Yes
+NoAccent=No
+DefaultChar=
+Script=
+ConvTable=
+
+[ModelOptions\Physical Objects\ClssNamingOptions\VIEW\Code]
+Template=
+MaxLen=64
+Case=M
+ValidChar=
+InvldChar=
+AllValid=Yes
+NoAccent=No
+DefaultChar=
+Script=
+ConvTable=
+
+[ModelOptions\Physical Objects\ClssNamingOptions\VIEWC]
+
+[ModelOptions\Physical Objects\ClssNamingOptions\VIEWC\Name]
+Template=
+MaxLen=254
+Case=M
+ValidChar=
+InvldChar=
+AllValid=Yes
+NoAccent=No
+DefaultChar=
+Script=
+ConvTable=
+
+[ModelOptions\Physical Objects\ClssNamingOptions\VIEWC\Code]
+Template=
+MaxLen=254
+Case=M
+ValidChar=
+InvldChar=
+AllValid=Yes
+NoAccent=No
+DefaultChar=
+Script=
+ConvTable=
+
+[ModelOptions\Physical Objects\ClssNamingOptions\WEBSERV]
+
+[ModelOptions\Physical Objects\ClssNamingOptions\WEBSERV\Name]
+Template=
+MaxLen=254
+Case=M
+ValidChar=
+InvldChar=
+AllValid=Yes
+NoAccent=No
+DefaultChar=
+Script=
+ConvTable=
+
+[ModelOptions\Physical Objects\ClssNamingOptions\WEBSERV\Code]
+Template=
+MaxLen=254
+Case=M
+ValidChar=&#39;a&#39;-&#39;z&#39;,&#39;A&#39;-&#39;Z&#39;,&#39;0&#39;-&#39;9&#39;,&quot;/-_.!~*&#39;()&quot;
+InvldChar=
+AllValid=Yes
+NoAccent=No
+DefaultChar=
+Script=
+ConvTable=
+
+[ModelOptions\Physical Objects\ClssNamingOptions\WEBOP]
+
+[ModelOptions\Physical Objects\ClssNamingOptions\WEBOP\Name]
+Template=
+MaxLen=254
+Case=M
+ValidChar=
+InvldChar=
+AllValid=Yes
+NoAccent=No
+DefaultChar=
+Script=
+ConvTable=
+
+[ModelOptions\Physical Objects\ClssNamingOptions\WEBOP\Code]
+Template=
+MaxLen=254
+Case=M
+ValidChar=&#39;a&#39;-&#39;z&#39;,&#39;A&#39;-&#39;Z&#39;,&#39;0&#39;-&#39;9&#39;,&quot;/-_.!~*&#39;()&quot;
+InvldChar=
+AllValid=Yes
+NoAccent=No
+DefaultChar=
+Script=
+ConvTable=
+
+[ModelOptions\Physical Objects\ClssNamingOptions\WPARAM]
+
+[ModelOptions\Physical Objects\ClssNamingOptions\WPARAM\Name]
+Template=
+MaxLen=254
+Case=M
+ValidChar=
+InvldChar=
+AllValid=Yes
+NoAccent=No
+DefaultChar=
+Script=
+ConvTable=
+
+[ModelOptions\Physical Objects\ClssNamingOptions\WPARAM\Code]
+Template=
+MaxLen=254
+Case=M
+ValidChar=
+InvldChar=
+AllValid=Yes
+NoAccent=No
+DefaultChar=
+Script=
+ConvTable=
+
+[ModelOptions\Physical Objects\ClssNamingOptions\FACT]
+
+[ModelOptions\Physical Objects\ClssNamingOptions\FACT\Name]
+Template=
+MaxLen=254
+Case=M
+ValidChar=
+InvldChar=
+AllValid=Yes
+NoAccent=No
+DefaultChar=
+Script=
+ConvTable=
+
+[ModelOptions\Physical Objects\ClssNamingOptions\FACT\Code]
+Template=
+MaxLen=254
+Case=M
+ValidChar=
+InvldChar=
+AllValid=Yes
+NoAccent=No
+DefaultChar=
+Script=
+ConvTable=
+
+[ModelOptions\Physical Objects\ClssNamingOptions\DIMN]
+
+[ModelOptions\Physical Objects\ClssNamingOptions\DIMN\Name]
+Template=
+MaxLen=254
+Case=M
+ValidChar=
+InvldChar=
+AllValid=Yes
+NoAccent=No
+DefaultChar=
+Script=
+ConvTable=
+
+[ModelOptions\Physical Objects\ClssNamingOptions\DIMN\Code]
+Template=
+MaxLen=254
+Case=M
+ValidChar=
+InvldChar=
+AllValid=Yes
+NoAccent=No
+DefaultChar=
+Script=
+ConvTable=
+
+[ModelOptions\Physical Objects\ClssNamingOptions\CUBE]
+
+[ModelOptions\Physical Objects\ClssNamingOptions\CUBE\Name]
+Template=
+MaxLen=254
+Case=M
+ValidChar=
+InvldChar=
+AllValid=Yes
+NoAccent=No
+DefaultChar=
+Script=
+ConvTable=
+
+[ModelOptions\Physical Objects\ClssNamingOptions\CUBE\Code]
+Template=
+MaxLen=254
+Case=M
+ValidChar=
+InvldChar=
+AllValid=Yes
+NoAccent=No
+DefaultChar=
+Script=
+ConvTable=
+
+[ModelOptions\Physical Objects\ClssNamingOptions\MEAS]
+
+[ModelOptions\Physical Objects\ClssNamingOptions\MEAS\Name]
+Template=
+MaxLen=254
+Case=M
+ValidChar=
+InvldChar=
+AllValid=Yes
+NoAccent=No
+DefaultChar=
+Script=
+ConvTable=
+
+[ModelOptions\Physical Objects\ClssNamingOptions\MEAS\Code]
+Template=
+MaxLen=254
+Case=M
+ValidChar=
+InvldChar=
+AllValid=Yes
+NoAccent=No
+DefaultChar=
+Script=
+ConvTable=
+
+[ModelOptions\Physical Objects\ClssNamingOptions\DATTR]
+
+[ModelOptions\Physical Objects\ClssNamingOptions\DATTR\Name]
+Template=
+MaxLen=254
+Case=M
+ValidChar=
+InvldChar=
+AllValid=Yes
+NoAccent=No
+DefaultChar=
+Script=
+ConvTable=
+
+[ModelOptions\Physical Objects\ClssNamingOptions\DATTR\Code]
+Template=
+MaxLen=254
+Case=M
+ValidChar=
+InvldChar=
+AllValid=Yes
+NoAccent=No
+DefaultChar=
+Script=
+ConvTable=
+
+[ModelOptions\Physical Objects\ClssNamingOptions\FILO]
+
+[ModelOptions\Physical Objects\ClssNamingOptions\FILO\Name]
+Template=
+MaxLen=254
+Case=M
+ValidChar=
+InvldChar=
+AllValid=Yes
+NoAccent=No
+DefaultChar=
+Script=
+ConvTable=
+
+[ModelOptions\Physical Objects\ClssNamingOptions\FILO\Code]
+Template=
+MaxLen=254
+Case=M
+ValidChar=
+InvldChar=
+AllValid=Yes
+NoAccent=No
+DefaultChar=
+Script=
+ConvTable=
+
+[ModelOptions\Physical Objects\ClssNamingOptions\FRMEOBJ]
+
+[ModelOptions\Physical Objects\ClssNamingOptions\FRMEOBJ\Name]
+Template=
+MaxLen=254
+Case=M
+ValidChar=
+InvldChar=
+AllValid=Yes
+NoAccent=No
+DefaultChar=
+Script=
+ConvTable=
+
+[ModelOptions\Physical Objects\ClssNamingOptions\FRMEOBJ\Code]
+Template=
+MaxLen=254
+Case=M
+ValidChar=
+InvldChar=
+AllValid=Yes
+NoAccent=No
+DefaultChar=
+Script=
+ConvTable=
+
+[ModelOptions\Physical Objects\ClssNamingOptions\FRMELNK]
+
+[ModelOptions\Physical Objects\ClssNamingOptions\FRMELNK\Name]
+Template=
+MaxLen=254
+Case=M
+ValidChar=
+InvldChar=
+AllValid=Yes
+NoAccent=No
+DefaultChar=
+Script=
+ConvTable=
+
+[ModelOptions\Physical Objects\ClssNamingOptions\FRMELNK\Code]
+Template=
+MaxLen=254
+Case=M
+ValidChar=
+InvldChar=
+AllValid=Yes
+NoAccent=No
+DefaultChar=
+Script=
+ConvTable=
+
+[ModelOptions\Physical Objects\ClssNamingOptions\DefaultClass]
+
+[ModelOptions\Physical Objects\ClssNamingOptions\DefaultClass\Name]
+Template=
+MaxLen=254
+Case=M
+ValidChar=
+InvldChar=
+AllValid=Yes
+NoAccent=No
+DefaultChar=
+Script=
+ConvTable=
+
+[ModelOptions\Physical Objects\ClssNamingOptions\DefaultClass\Code]
+Template=
+MaxLen=254
+Case=M
+ValidChar=
+InvldChar=
+AllValid=Yes
+NoAccent=No
+DefaultChar=
+Script=
+ConvTable=
+
+[ModelOptions\Connection]
+
+[ModelOptions\Pdm]
+
+[ModelOptions\Generate]
+
+[ModelOptions\Generate\Pdm]
+RRMapping=No
+
+[ModelOptions\Generate\Cdm]
+CheckModel=Yes
+SaveLinks=Yes
+NameToCode=No
+Notation=2
+
+[ModelOptions\Generate\Oom]
+CheckModel=Yes
+SaveLinks=Yes
+ORMapping=No
+NameToCode=Yes
+ClassPrefix=
+
+[ModelOptions\Generate\Xsm]
+CheckModel=Yes
+SaveLinks=Yes
+ORMapping=No
+NameToCode=No
+
+[ModelOptions\Default Opts]
+
+[ModelOptions\Default Opts\TABL]
+PhysOpts=
+
+[ModelOptions\Default Opts\COLN]
+PhysOpts=
+
+[ModelOptions\Default Opts\INDX]
+PhysOpts=
+
+[ModelOptions\Default Opts\AKEY]
+PhysOpts=
+
+[ModelOptions\Default Opts\PKEY]
+PhysOpts=
+
+[ModelOptions\Default Opts\STOR]
+PhysOpts=
+
+[ModelOptions\Default Opts\TSPC]
+PhysOpts=
+
+[ModelOptions\Default Opts\SQNC]
+PhysOpts=
+
+[ModelOptions\Default Opts\DTBS]
+PhysOpts=
+
+[ModelOptions\Default Opts\USER]
+PhysOpts=
+
+[ModelOptions\Default Opts\JIDX]
+PhysOpts=</a:ModelOptionsText>
+<c:DBMS>
+<o:Shortcut Id="o3">
+<a:ObjectID>AFAD9ECF-F417-4FCE-BEA4-884857D4C1A9</a:ObjectID>
+<a:Name>MySQL 5.0</a:Name>
+<a:Code>MYSQL50</a:Code>
+<a:CreationDate>1524449337</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449337</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:TargetStereotype/>
+<a:TargetID>F4F16ECD-F2F1-4006-AF6F-638D5C65F35E</a:TargetID>
+<a:TargetClassID>4BA9F647-DAB1-11D1-9944-006097355D9B</a:TargetClassID>
+</o:Shortcut>
+</c:DBMS>
+<c:PhysicalDiagrams>
+<o:PhysicalDiagram Id="o4">
+<a:ObjectID>B6C2C4A4-6A8A-41F3-909D-C7B514E1EAE2</a:ObjectID>
+<a:Name>PhysicalDiagram_1</a:Name>
+<a:Code>PhysicalDiagram_1</a:Code>
+<a:CreationDate>1524449325</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:DisplayPreferences>[DisplayPreferences]
+
+[DisplayPreferences\PDM]
+
+[DisplayPreferences\General]
+Adjust to text=Yes
+Snap Grid=No
+Display Grid=No
+Show Page Delimiter=Yes
+Grid size=0
+Graphic unit=2
+Window color=255, 255, 255
+Background image=
+Background mode=8
+Watermark image=
+Watermark mode=8
+Show watermark on screen=No
+Gradient mode=0
+Gradient end color=255, 255, 255
+Show Swimlane=No
+SwimlaneVert=Yes
+TreeVert=No
+
+[DisplayPreferences\Object]
+Mode=0
+Trunc Length=80
+Word Length=80
+Word Text=!&quot;&quot;#$%&amp;&#39;()*+,-./:;&lt;=&gt;?@[\]^_`{|}~
+Shortcut IntIcon=Yes
+Shortcut IntLoct=Yes
+Shortcut IntFullPath=No
+Shortcut IntLastPackage=Yes
+Shortcut ExtIcon=Yes
+Shortcut ExtLoct=No
+Shortcut ExtFullPath=No
+Shortcut ExtLastPackage=Yes
+Shortcut ExtIncludeModl=Yes
+EObjShowStrn=Yes
+ExtendedObject.Comment=No
+ELnkShowStrn=Yes
+ELnkShowName=Yes
+File Location=No
+PckgStrn=Yes
+Package.Comment=No
+Display Model Version=Yes
+TablColn=Yes
+ColnMode=0
+ColnMax=5
+TablIndx=No
+TablKey=No
+TablTrgr=No
+TablOwnr=No
+TablStrn=Yes
+TablCmmt=No
+ColnDttp=Yes
+ColnDomn=No
+ColnShowDomn=No
+ColnKey=Yes
+ColnIndx=No
+ColnMand=No
+ColnStrn=Yes
+ViewTabl=Yes
+ViewIndx=No
+ViewColn=Yes
+ViewOwnr=No
+ViewStrn=Yes
+ViewCmmt=No
+VColName=Yes
+VColExpr=No
+VColDttp=No
+VColIndx=No
+VColCMod=0
+VColCMax=5
+RefrName=No
+RefrJoin=No
+RefrCnam=Yes
+RefrIntg=No
+RefrImpl=No
+RefrCard=No
+RefrStrn=Yes
+RefrRole=Yes
+VRefName=No
+VRefJoin=No
+VRefStrn=Yes
+VRefRole=Yes
+ProcOwnr=No
+KeyStrn=Yes
+IndxStrn=Yes
+TrgrStrn=Yes
+ProcStrn=No
+Procedure.Comment=No
+
+[DisplayPreferences\Symbol]
+
+[DisplayPreferences\Symbol\FRMEOBJ]
+STRNFont=新宋体,8,N
+STRNFont color=0, 0, 0
+DISPNAMEFont=新宋体,8,N
+DISPNAMEFont color=0, 0, 0
+LABLFont=新宋体,8,N
+LABLFont color=0, 0, 0
+AutoAdjustToText=Yes
+Keep aspect=No
+Keep center=No
+Keep size=No
+Width=6000
+Height=2000
+Brush color=255 255 255
+Fill Color=Yes
+Brush style=6
+Brush bitmap mode=12
+Brush gradient mode=64
+Brush gradient color=192 192 192
+Brush background image=
+Custom shape=
+Custom text mode=0
+Pen=1 0 255 128 128
+Shadow color=192 192 192
+Shadow=0
+
+[DisplayPreferences\Symbol\FRMELNK]
+DISPNAMEFont=新宋体,8,N
+DISPNAMEFont color=0, 0, 0
+Line style=0
+Pen=1 0 128 128 255
+Shadow color=192 192 192
+Shadow=0
+
+[DisplayPreferences\Symbol\PDMPCKG]
+PckgStrnFont=新宋体,8,N
+PckgStrnFont color=0, 0, 0
+DISPNAMEFont=新宋体,8,N
+DISPNAMEFont color=0, 0, 0
+LABLFont=新宋体,8,N
+LABLFont color=0, 0, 0
+AutoAdjustToText=Yes
+Keep aspect=No
+Keep center=No
+Keep size=No
+Width=4800
+Height=3600
+Brush color=255 255 192
+Fill Color=Yes
+Brush style=6
+Brush bitmap mode=12
+Brush gradient mode=65
+Brush gradient color=255 255 255
+Brush background image=
+Custom shape=
+Custom text mode=0
+Pen=1 0 178 178 178
+Shadow color=192 192 192
+Shadow=0
+
+[DisplayPreferences\Symbol\TABL]
+TablStrnFont=新宋体,8,N
+TablStrnFont color=0, 0, 0
+DISPNAMEFont=新宋体,8,N
+DISPNAMEFont color=0, 0, 0
+TableColumnsFont=新宋体,8,N
+TableColumnsFont color=0, 0, 0
+TablePkColumnsFont=新宋体,8,U
+TablePkColumnsFont color=0, 0, 0
+TableFkColumnsFont=新宋体,8,N
+TableFkColumnsFont color=0, 0, 0
+TableKeysFont=新宋体,8,N
+TableKeysFont color=0, 0, 0
+TableIndexsFont=新宋体,8,N
+TableIndexsFont color=0, 0, 0
+TableTriggersFont=新宋体,8,N
+TableTriggersFont color=0, 0, 0
+TableCommentFont=新宋体,8,N
+TableCommentFont color=0, 0, 0
+AutoAdjustToText=Yes
+Keep aspect=No
+Keep center=No
+Keep size=No
+Width=4800
+Height=4000
+Brush color=178 214 252
+Fill Color=Yes
+Brush style=6
+Brush bitmap mode=12
+Brush gradient mode=65
+Brush gradient color=255 255 255
+Brush background image=
+Custom shape=
+Custom text mode=0
+Pen=1 0 0 128 192
+Shadow color=192 192 192
+Shadow=0
+
+[DisplayPreferences\Symbol\REFR]
+DISPNAMEFont=新宋体,8,N
+DISPNAMEFont color=0, 0, 0
+CARDFont=新宋体,8,N
+CARDFont color=0, 0, 0
+Line style=0
+AutoAdjustToText=Yes
+Keep aspect=No
+Keep center=No
+Keep size=No
+Brush color=255 255 255
+Fill Color=Yes
+Brush style=1
+Brush bitmap mode=12
+Brush gradient mode=0
+Brush gradient color=118 118 118
+Brush background image=
+Custom shape=
+Custom text mode=0
+Pen=1 0 0 128 192
+Shadow color=192 192 192
+Shadow=0
+
+[DisplayPreferences\Symbol\VREF]
+DISPNAMEFont=新宋体,8,N
+DISPNAMEFont color=0, 0, 0
+Line style=0
+AutoAdjustToText=Yes
+Keep aspect=No
+Keep center=No
+Keep size=No
+Brush color=255 255 255
+Fill Color=Yes
+Brush style=1
+Brush bitmap mode=12
+Brush gradient mode=0
+Brush gradient color=118 118 118
+Brush background image=
+Custom shape=
+Custom text mode=0
+Pen=1 0 128 128 192
+Shadow color=192 192 192
+Shadow=0
+
+[DisplayPreferences\Symbol\VIEW]
+ViewStrnFont=新宋体,8,N
+ViewStrnFont color=0, 0, 0
+DISPNAMEFont=新宋体,8,N
+DISPNAMEFont color=0, 0, 0
+ViewColumnnFont=新宋体,8,N
+ViewColumnnFont color=0, 0, 0
+ViewIndexesFont=新宋体,8,N
+ViewIndexesFont color=0, 0, 0
+ViewTablesFont=新宋体,8,N
+ViewTablesFont color=0, 0, 0
+ViewCommentFont=新宋体,8,N
+ViewCommentFont color=0, 0, 0
+AutoAdjustToText=Yes
+Keep aspect=No
+Keep center=No
+Keep size=No
+Width=4800
+Height=4000
+Brush color=208 208 255
+Fill Color=Yes
+Brush style=6
+Brush bitmap mode=12
+Brush gradient mode=65
+Brush gradient color=255 255 255
+Brush background image=
+Custom shape=
+Custom text mode=0
+Pen=1 0 128 128 192
+Shadow color=192 192 192
+Shadow=0
+
+[DisplayPreferences\Symbol\PROC]
+ProcStrnFont=新宋体,8,N
+ProcStrnFont color=0, 0, 0
+DISPNAMEFont=新宋体,8,N
+DISPNAMEFont color=0, 0, 0
+LABLFont=新宋体,8,N
+LABLFont color=0, 0, 0
+AutoAdjustToText=Yes
+Keep aspect=No
+Keep center=No
+Keep size=No
+Width=4000
+Height=1000
+Brush color=255 255 192
+Fill Color=Yes
+Brush style=6
+Brush bitmap mode=12
+Brush gradient mode=65
+Brush gradient color=255 255 255
+Brush background image=
+Custom shape=
+Custom text mode=0
+Pen=1 0 128 108 0
+Shadow color=192 192 192
+Shadow=0
+
+[DisplayPreferences\Symbol\FILO]
+NAMEFont=新宋体,8,N
+NAMEFont color=0, 0, 0
+AutoAdjustToText=Yes
+Keep aspect=Yes
+Keep center=Yes
+Keep size=No
+Width=2400
+Height=2400
+Brush color=255 255 255
+Fill Color=No
+Brush style=1
+Brush bitmap mode=12
+Brush gradient mode=0
+Brush gradient color=118 118 118
+Brush background image=
+Custom shape=
+Custom text mode=0
+Pen=1 0 0 0 255
+Shadow color=192 192 192
+Shadow=0
+
+[DisplayPreferences\Symbol\USRDEPD]
+OBJXSTRFont=新宋体,8,N
+OBJXSTRFont color=0, 0, 0
+Line style=0
+AutoAdjustToText=Yes
+Keep aspect=No
+Keep center=No
+Keep size=No
+Brush color=255 255 255
+Fill Color=Yes
+Brush style=1
+Brush bitmap mode=12
+Brush gradient mode=0
+Brush gradient color=118 118 118
+Brush background image=
+Custom shape=
+Custom text mode=0
+Pen=2 0 128 128 255
+Shadow color=192 192 192
+Shadow=0
+
+[DisplayPreferences\Symbol\Free Symbol]
+Free TextFont=新宋体,8,N
+Free TextFont color=0, 0, 0
+Line style=0
+AutoAdjustToText=Yes
+Keep aspect=No
+Keep center=No
+Keep size=No
+Brush color=255 255 255
+Fill Color=Yes
+Brush style=1
+Brush bitmap mode=12
+Brush gradient mode=0
+Brush gradient color=118 118 118
+Brush background image=
+Custom shape=
+Custom text mode=0
+Pen=1 0 0 0 255
+Shadow color=192 192 192
+Shadow=0</a:DisplayPreferences>
+<a:PaperSize>(8268, 11693)</a:PaperSize>
+<a:PageMargins>((315,354), (433,354))</a:PageMargins>
+<a:PageOrientation>1</a:PageOrientation>
+<a:PaperSource>15</a:PaperSource>
+<c:Symbols>
+<o:TableSymbol Id="o5">
+<a:CreationDate>1524449375</a:CreationDate>
+<a:ModificationDate>1524449886</a:ModificationDate>
+<a:Rect>((-38123,15548), (-26435,27219))</a:Rect>
+<a:LineColor>12615680</a:LineColor>
+<a:FillColor>16570034</a:FillColor>
+<a:ShadowColor>12632256</a:ShadowColor>
+<a:FontList>TablStrn 0 新宋体,8,N
+DISPNAME 0 新宋体,8,N
+TableColumns 0 新宋体,8,N
+TablePkColumns 0 新宋体,8,U
+TableFkColumns 0 新宋体,8,N
+TableKeys 0 新宋体,8,N
+TableIndexs 0 新宋体,8,N
+TableTriggers 0 新宋体,8,N
+TableComment 0 新宋体,8,N</a:FontList>
+<a:BrushStyle>6</a:BrushStyle>
+<a:GradientFillMode>65</a:GradientFillMode>
+<a:GradientEndColor>16777215</a:GradientEndColor>
+<c:Object>
+<o:Table Ref="o6"/>
+</c:Object>
+</o:TableSymbol>
+<o:TableSymbol Id="o7">
+<a:CreationDate>1524449375</a:CreationDate>
+<a:ModificationDate>1524449886</a:ModificationDate>
+<a:Rect>((-23934,13073), (-11861,27219))</a:Rect>
+<a:LineColor>12615680</a:LineColor>
+<a:FillColor>16570034</a:FillColor>
+<a:ShadowColor>12632256</a:ShadowColor>
+<a:FontList>TablStrn 0 新宋体,8,N
+DISPNAME 0 新宋体,8,N
+TableColumns 0 新宋体,8,N
+TablePkColumns 0 新宋体,8,U
+TableFkColumns 0 新宋体,8,N
+TableKeys 0 新宋体,8,N
+TableIndexs 0 新宋体,8,N
+TableTriggers 0 新宋体,8,N
+TableComment 0 新宋体,8,N</a:FontList>
+<a:BrushStyle>6</a:BrushStyle>
+<a:GradientFillMode>65</a:GradientFillMode>
+<a:GradientEndColor>16777215</a:GradientEndColor>
+<c:Object>
+<o:Table Ref="o8"/>
+</c:Object>
+</o:TableSymbol>
+<o:TableSymbol Id="o9">
+<a:CreationDate>1524449375</a:CreationDate>
+<a:ModificationDate>1524449886</a:ModificationDate>
+<a:Rect>((-9360,17198), (2713,27219))</a:Rect>
+<a:LineColor>12615680</a:LineColor>
+<a:FillColor>16570034</a:FillColor>
+<a:ShadowColor>12632256</a:ShadowColor>
+<a:FontList>TablStrn 0 新宋体,8,N
+DISPNAME 0 新宋体,8,N
+TableColumns 0 新宋体,8,N
+TablePkColumns 0 新宋体,8,U
+TableFkColumns 0 新宋体,8,N
+TableKeys 0 新宋体,8,N
+TableIndexs 0 新宋体,8,N
+TableTriggers 0 新宋体,8,N
+TableComment 0 新宋体,8,N</a:FontList>
+<a:BrushStyle>6</a:BrushStyle>
+<a:GradientFillMode>65</a:GradientFillMode>
+<a:GradientEndColor>16777215</a:GradientEndColor>
+<c:Object>
+<o:Table Ref="o10"/>
+</c:Object>
+</o:TableSymbol>
+<o:TableSymbol Id="o11">
+<a:CreationDate>1524449375</a:CreationDate>
+<a:ModificationDate>1524449886</a:ModificationDate>
+<a:Rect>((5214,17198), (17287,27219))</a:Rect>
+<a:LineColor>12615680</a:LineColor>
+<a:FillColor>16570034</a:FillColor>
+<a:ShadowColor>12632256</a:ShadowColor>
+<a:FontList>TablStrn 0 新宋体,8,N
+DISPNAME 0 新宋体,8,N
+TableColumns 0 新宋体,8,N
+TablePkColumns 0 新宋体,8,U
+TableFkColumns 0 新宋体,8,N
+TableKeys 0 新宋体,8,N
+TableIndexs 0 新宋体,8,N
+TableTriggers 0 新宋体,8,N
+TableComment 0 新宋体,8,N</a:FontList>
+<a:BrushStyle>6</a:BrushStyle>
+<a:GradientFillMode>65</a:GradientFillMode>
+<a:GradientEndColor>16777215</a:GradientEndColor>
+<c:Object>
+<o:Table Ref="o12"/>
+</c:Object>
+</o:TableSymbol>
+<o:TableSymbol Id="o13">
+<a:CreationDate>1524449375</a:CreationDate>
+<a:ModificationDate>1524449886</a:ModificationDate>
+<a:Rect>((19788,13898), (31861,27219))</a:Rect>
+<a:LineColor>12615680</a:LineColor>
+<a:FillColor>16570034</a:FillColor>
+<a:ShadowColor>12632256</a:ShadowColor>
+<a:FontList>TablStrn 0 新宋体,8,N
+DISPNAME 0 新宋体,8,N
+TableColumns 0 新宋体,8,N
+TablePkColumns 0 新宋体,8,U
+TableFkColumns 0 新宋体,8,N
+TableKeys 0 新宋体,8,N
+TableIndexs 0 新宋体,8,N
+TableTriggers 0 新宋体,8,N
+TableComment 0 新宋体,8,N</a:FontList>
+<a:BrushStyle>6</a:BrushStyle>
+<a:GradientFillMode>65</a:GradientFillMode>
+<a:GradientEndColor>16777215</a:GradientEndColor>
+<c:Object>
+<o:Table Ref="o14"/>
+</c:Object>
+</o:TableSymbol>
+<o:TableSymbol Id="o15">
+<a:CreationDate>1524449375</a:CreationDate>
+<a:ModificationDate>1524449886</a:ModificationDate>
+<a:Rect>((-38123,4073), (-29525,8072))</a:Rect>
+<a:LineColor>12615680</a:LineColor>
+<a:FillColor>16570034</a:FillColor>
+<a:ShadowColor>12632256</a:ShadowColor>
+<a:FontList>TablStrn 0 新宋体,8,N
+DISPNAME 0 新宋体,8,N
+TableColumns 0 新宋体,8,N
+TablePkColumns 0 新宋体,8,U
+TableFkColumns 0 新宋体,8,N
+TableKeys 0 新宋体,8,N
+TableIndexs 0 新宋体,8,N
+TableTriggers 0 新宋体,8,N
+TableComment 0 新宋体,8,N</a:FontList>
+<a:BrushStyle>6</a:BrushStyle>
+<a:GradientFillMode>65</a:GradientFillMode>
+<a:GradientEndColor>16777215</a:GradientEndColor>
+<c:Object>
+<o:Table Ref="o16"/>
+</c:Object>
+</o:TableSymbol>
+<o:TableSymbol Id="o17">
+<a:CreationDate>1524449375</a:CreationDate>
+<a:ModificationDate>1524449886</a:ModificationDate>
+<a:Rect>((-27024,4073), (-18426,8072))</a:Rect>
+<a:LineColor>12615680</a:LineColor>
+<a:FillColor>16570034</a:FillColor>
+<a:ShadowColor>12632256</a:ShadowColor>
+<a:FontList>TablStrn 0 新宋体,8,N
+DISPNAME 0 新宋体,8,N
+TableColumns 0 新宋体,8,N
+TablePkColumns 0 新宋体,8,U
+TableFkColumns 0 新宋体,8,N
+TableKeys 0 新宋体,8,N
+TableIndexs 0 新宋体,8,N
+TableTriggers 0 新宋体,8,N
+TableComment 0 新宋体,8,N</a:FontList>
+<a:BrushStyle>6</a:BrushStyle>
+<a:GradientFillMode>65</a:GradientFillMode>
+<a:GradientEndColor>16777215</a:GradientEndColor>
+<c:Object>
+<o:Table Ref="o18"/>
+</c:Object>
+</o:TableSymbol>
+<o:TableSymbol Id="o19">
+<a:CreationDate>1524449375</a:CreationDate>
+<a:ModificationDate>1524449886</a:ModificationDate>
+<a:Rect>((-15925,4073), (-5782,8072))</a:Rect>
+<a:LineColor>12615680</a:LineColor>
+<a:FillColor>16570034</a:FillColor>
+<a:ShadowColor>12632256</a:ShadowColor>
+<a:FontList>TablStrn 0 新宋体,8,N
+DISPNAME 0 新宋体,8,N
+TableColumns 0 新宋体,8,N
+TablePkColumns 0 新宋体,8,U
+TableFkColumns 0 新宋体,8,N
+TableKeys 0 新宋体,8,N
+TableIndexs 0 新宋体,8,N
+TableTriggers 0 新宋体,8,N
+TableComment 0 新宋体,8,N</a:FontList>
+<a:BrushStyle>6</a:BrushStyle>
+<a:GradientFillMode>65</a:GradientFillMode>
+<a:GradientEndColor>16777215</a:GradientEndColor>
+<c:Object>
+<o:Table Ref="o20"/>
+</c:Object>
+</o:TableSymbol>
+<o:TableSymbol Id="o21">
+<a:CreationDate>1524449375</a:CreationDate>
+<a:ModificationDate>1524449886</a:ModificationDate>
+<a:Rect>((-3551,1426), (8522,13922))</a:Rect>
+<a:LineColor>12615680</a:LineColor>
+<a:FillColor>16570034</a:FillColor>
+<a:ShadowColor>12632256</a:ShadowColor>
+<a:FontList>TablStrn 0 新宋体,8,N
+DISPNAME 0 新宋体,8,N
+TableColumns 0 新宋体,8,N
+TablePkColumns 0 新宋体,8,U
+TableFkColumns 0 新宋体,8,N
+TableKeys 0 新宋体,8,N
+TableIndexs 0 新宋体,8,N
+TableTriggers 0 新宋体,8,N
+TableComment 0 新宋体,8,N</a:FontList>
+<a:BrushStyle>6</a:BrushStyle>
+<a:GradientFillMode>65</a:GradientFillMode>
+<a:GradientEndColor>16777215</a:GradientEndColor>
+<c:Object>
+<o:Table Ref="o22"/>
+</c:Object>
+</o:TableSymbol>
+<o:TableSymbol Id="o23">
+<a:CreationDate>1524449375</a:CreationDate>
+<a:ModificationDate>1524449886</a:ModificationDate>
+<a:Rect>((11293,-1949), (23366,8072))</a:Rect>
+<a:LineColor>12615680</a:LineColor>
+<a:FillColor>16570034</a:FillColor>
+<a:ShadowColor>12632256</a:ShadowColor>
+<a:FontList>TablStrn 0 新宋体,8,N
+DISPNAME 0 新宋体,8,N
+TableColumns 0 新宋体,8,N
+TablePkColumns 0 新宋体,8,U
+TableFkColumns 0 新宋体,8,N
+TableKeys 0 新宋体,8,N
+TableIndexs 0 新宋体,8,N
+TableTriggers 0 新宋体,8,N
+TableComment 0 新宋体,8,N</a:FontList>
+<a:BrushStyle>6</a:BrushStyle>
+<a:GradientFillMode>65</a:GradientFillMode>
+<a:GradientEndColor>16777215</a:GradientEndColor>
+<c:Object>
+<o:Table Ref="o24"/>
+</c:Object>
+</o:TableSymbol>
+<o:TableSymbol Id="o25">
+<a:CreationDate>1524449375</a:CreationDate>
+<a:ModificationDate>1524449886</a:ModificationDate>
+<a:Rect>((25867,-2775), (37940,8072))</a:Rect>
+<a:LineColor>12615680</a:LineColor>
+<a:FillColor>16570034</a:FillColor>
+<a:ShadowColor>12632256</a:ShadowColor>
+<a:FontList>TablStrn 0 新宋体,8,N
+DISPNAME 0 新宋体,8,N
+TableColumns 0 新宋体,8,N
+TablePkColumns 0 新宋体,8,U
+TableFkColumns 0 新宋体,8,N
+TableKeys 0 新宋体,8,N
+TableIndexs 0 新宋体,8,N
+TableTriggers 0 新宋体,8,N
+TableComment 0 新宋体,8,N</a:FontList>
+<a:BrushStyle>6</a:BrushStyle>
+<a:GradientFillMode>65</a:GradientFillMode>
+<a:GradientEndColor>16777215</a:GradientEndColor>
+<c:Object>
+<o:Table Ref="o26"/>
+</c:Object>
+</o:TableSymbol>
+<o:TableSymbol Id="o27">
+<a:CreationDate>1524449375</a:CreationDate>
+<a:ModificationDate>1524449886</a:ModificationDate>
+<a:Rect>((-39023,-11227), (-27335,-2855))</a:Rect>
+<a:LineColor>12615680</a:LineColor>
+<a:FillColor>16570034</a:FillColor>
+<a:ShadowColor>12632256</a:ShadowColor>
+<a:FontList>TablStrn 0 新宋体,8,N
+DISPNAME 0 新宋体,8,N
+TableColumns 0 新宋体,8,N
+TablePkColumns 0 新宋体,8,U
+TableFkColumns 0 新宋体,8,N
+TableKeys 0 新宋体,8,N
+TableIndexs 0 新宋体,8,N
+TableTriggers 0 新宋体,8,N
+TableComment 0 新宋体,8,N</a:FontList>
+<a:BrushStyle>6</a:BrushStyle>
+<a:GradientFillMode>65</a:GradientFillMode>
+<a:GradientEndColor>16777215</a:GradientEndColor>
+<c:Object>
+<o:Table Ref="o28"/>
+</c:Object>
+</o:TableSymbol>
+<o:TableSymbol Id="o29">
+<a:CreationDate>1524449375</a:CreationDate>
+<a:ModificationDate>1524449886</a:ModificationDate>
+<a:Rect>((-24024,-12066), (-10405,-2045))</a:Rect>
+<a:LineColor>12615680</a:LineColor>
+<a:FillColor>16570034</a:FillColor>
+<a:ShadowColor>12632256</a:ShadowColor>
+<a:FontList>TablStrn 0 新宋体,8,N
+DISPNAME 0 新宋体,8,N
+TableColumns 0 新宋体,8,N
+TablePkColumns 0 新宋体,8,U
+TableFkColumns 0 新宋体,8,N
+TableKeys 0 新宋体,8,N
+TableIndexs 0 新宋体,8,N
+TableTriggers 0 新宋体,8,N
+TableComment 0 新宋体,8,N</a:FontList>
+<a:BrushStyle>6</a:BrushStyle>
+<a:GradientFillMode>65</a:GradientFillMode>
+<a:GradientEndColor>16777215</a:GradientEndColor>
+<c:Object>
+<o:Table Ref="o30"/>
+</c:Object>
+</o:TableSymbol>
+<o:TableSymbol Id="o31">
+<a:CreationDate>1524449375</a:CreationDate>
+<a:ModificationDate>1524449886</a:ModificationDate>
+<a:Rect>((-7724,-17856), (5895,-6185))</a:Rect>
+<a:LineColor>12615680</a:LineColor>
+<a:FillColor>16570034</a:FillColor>
+<a:ShadowColor>12632256</a:ShadowColor>
+<a:FontList>TablStrn 0 新宋体,8,N
+DISPNAME 0 新宋体,8,N
+TableColumns 0 新宋体,8,N
+TablePkColumns 0 新宋体,8,U
+TableFkColumns 0 新宋体,8,N
+TableKeys 0 新宋体,8,N
+TableIndexs 0 新宋体,8,N
+TableTriggers 0 新宋体,8,N
+TableComment 0 新宋体,8,N</a:FontList>
+<a:BrushStyle>6</a:BrushStyle>
+<a:GradientFillMode>65</a:GradientFillMode>
+<a:GradientEndColor>16777215</a:GradientEndColor>
+<c:Object>
+<o:Table Ref="o32"/>
+</c:Object>
+</o:TableSymbol>
+<o:TableSymbol Id="o33">
+<a:CreationDate>1524449375</a:CreationDate>
+<a:ModificationDate>1524449886</a:ModificationDate>
+<a:Rect>((9386,-15472), (22619,-6275))</a:Rect>
+<a:LineColor>12615680</a:LineColor>
+<a:FillColor>16570034</a:FillColor>
+<a:ShadowColor>12632256</a:ShadowColor>
+<a:FontList>TablStrn 0 新宋体,8,N
+DISPNAME 0 新宋体,8,N
+TableColumns 0 新宋体,8,N
+TablePkColumns 0 新宋体,8,U
+TableFkColumns 0 新宋体,8,N
+TableKeys 0 新宋体,8,N
+TableIndexs 0 新宋体,8,N
+TableTriggers 0 新宋体,8,N
+TableComment 0 新宋体,8,N</a:FontList>
+<a:BrushStyle>6</a:BrushStyle>
+<a:GradientFillMode>65</a:GradientFillMode>
+<a:GradientEndColor>16777215</a:GradientEndColor>
+<c:Object>
+<o:Table Ref="o34"/>
+</c:Object>
+</o:TableSymbol>
+</c:Symbols>
+</o:PhysicalDiagram>
+</c:PhysicalDiagrams>
+<c:DefaultDiagram>
+<o:PhysicalDiagram Ref="o4"/>
+</c:DefaultDiagram>
+<c:Tables>
+<o:Table Id="o6">
+<a:ObjectID>BB11FFF9-9DBB-4648-87AA-9A50E1214549</a:ObjectID>
+<a:Name>sys_dept</a:Name>
+<a:Code>sys_dept</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<c:Columns>
+<o:Column Id="o35">
+<a:ObjectID>00C66282-419A-4915-8509-DFFFE6352DE8</a:ObjectID>
+<a:Name>dept_id</a:Name>
+<a:Code>dept_id</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>部门id</a:Comment>
+<a:DataType>int(11)</a:DataType>
+<a:Length>11</a:Length>
+<a:Identity>1</a:Identity>
+<a:Mandatory>1</a:Mandatory>
+</o:Column>
+<o:Column Id="o36">
+<a:ObjectID>5B6FB0B1-5B1E-4E86-AF2A-72C49EBB315E</a:ObjectID>
+<a:Name>parent_id</a:Name>
+<a:Code>parent_id</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>父部门id</a:Comment>
+<a:DefaultValue>0</a:DefaultValue>
+<a:DataType>int(11)</a:DataType>
+<a:Length>11</a:Length>
+</o:Column>
+<o:Column Id="o37">
+<a:ObjectID>EBB59EC8-AFD4-40E3-B811-DD5040728D91</a:ObjectID>
+<a:Name>dept_name</a:Name>
+<a:Code>dept_name</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>部门名称</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(30)</a:DataType>
+<a:Length>30</a:Length>
+</o:Column>
+<o:Column Id="o38">
+<a:ObjectID>2F26C025-82B0-4AC5-AEE0-32BA07B7B529</a:ObjectID>
+<a:Name>order_num</a:Name>
+<a:Code>order_num</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>显示顺序</a:Comment>
+<a:DefaultValue>0</a:DefaultValue>
+<a:DataType>int(4)</a:DataType>
+<a:Length>4</a:Length>
+</o:Column>
+<o:Column Id="o39">
+<a:ObjectID>CA504E09-528C-482E-A0C7-F86C559AA3A6</a:ObjectID>
+<a:Name>leader</a:Name>
+<a:Code>leader</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>负责人</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(20)</a:DataType>
+<a:Length>20</a:Length>
+</o:Column>
+<o:Column Id="o40">
+<a:ObjectID>9CFC55C4-DF2B-4A90-A789-C3839FAA43A8</a:ObjectID>
+<a:Name>phone</a:Name>
+<a:Code>phone</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>联系电话</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(20)</a:DataType>
+<a:Length>20</a:Length>
+</o:Column>
+<o:Column Id="o41">
+<a:ObjectID>1A9407E5-D74E-4CE9-9078-C4EC25393F7B</a:ObjectID>
+<a:Name>email</a:Name>
+<a:Code>email</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>邮箱</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(20)</a:DataType>
+<a:Length>20</a:Length>
+</o:Column>
+<o:Column Id="o42">
+<a:ObjectID>B6772812-4B69-4248-871D-FA1B4BA0E5F7</a:ObjectID>
+<a:Name>status</a:Name>
+<a:Code>status</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>部门状态:0正常,1停用</a:Comment>
+<a:DefaultValue>0</a:DefaultValue>
+<a:DataType>int(1)</a:DataType>
+<a:Length>1</a:Length>
+</o:Column>
+<o:Column Id="o43">
+<a:ObjectID>2504A090-F6D6-493F-855E-5154E01AF0CA</a:ObjectID>
+<a:Name>create_by</a:Name>
+<a:Code>create_by</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>创建者</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(64)</a:DataType>
+<a:Length>64</a:Length>
+</o:Column>
+<o:Column Id="o44">
+<a:ObjectID>D866AE9E-E7FF-47B2-BF3D-9BC1605A2F39</a:ObjectID>
+<a:Name>create_time</a:Name>
+<a:Code>create_time</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>创建时间</a:Comment>
+<a:DataType>timestamp</a:DataType>
+</o:Column>
+<o:Column Id="o45">
+<a:ObjectID>7C6C9836-FC23-4492-8CF1-A4439E01B57C</a:ObjectID>
+<a:Name>update_by</a:Name>
+<a:Code>update_by</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>更新者</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(64)</a:DataType>
+<a:Length>64</a:Length>
+</o:Column>
+<o:Column Id="o46">
+<a:ObjectID>FCED770D-005C-4531-A9D7-D1FD0A054719</a:ObjectID>
+<a:Name>update_time</a:Name>
+<a:Code>update_time</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>更新时间</a:Comment>
+<a:DataType>timestamp</a:DataType>
+</o:Column>
+</c:Columns>
+<c:Keys>
+<o:Key Id="o47">
+<a:ObjectID>15C1774B-9F17-48B6-A61F-728A25220B30</a:ObjectID>
+<a:Name>Key_1</a:Name>
+<a:Code>Key_1</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<c:Key.Columns>
+<o:Column Ref="o35"/>
+</c:Key.Columns>
+</o:Key>
+</c:Keys>
+<c:PrimaryKey>
+<o:Key Ref="o47"/>
+</c:PrimaryKey>
+</o:Table>
+<o:Table Id="o8">
+<a:ObjectID>AA56FD91-4450-4282-8F31-AE302DF6AFEC</a:ObjectID>
+<a:Name>sys_user</a:Name>
+<a:Code>sys_user</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<c:Columns>
+<o:Column Id="o48">
+<a:ObjectID>4A920BCE-4040-4F12-89D2-7DF345B90321</a:ObjectID>
+<a:Name>user_id</a:Name>
+<a:Code>user_id</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>用户ID</a:Comment>
+<a:DataType>int(11)</a:DataType>
+<a:Length>11</a:Length>
+<a:Identity>1</a:Identity>
+<a:Mandatory>1</a:Mandatory>
+</o:Column>
+<o:Column Id="o49">
+<a:ObjectID>174E10B2-4A4D-40FF-80B8-B4D285561E42</a:ObjectID>
+<a:Name>dept_id</a:Name>
+<a:Code>dept_id</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>部门ID</a:Comment>
+<a:DefaultValue>NULL</a:DefaultValue>
+<a:DataType>int(20)</a:DataType>
+<a:Length>20</a:Length>
+</o:Column>
+<o:Column Id="o50">
+<a:ObjectID>1D4908A9-5416-4252-BA09-FA122D0194C3</a:ObjectID>
+<a:Name>login_name</a:Name>
+<a:Code>login_name</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>登录账号</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(30)</a:DataType>
+<a:Length>30</a:Length>
+</o:Column>
+<o:Column Id="o51">
+<a:ObjectID>2EF63346-9E82-4746-81B7-AB67D727446D</a:ObjectID>
+<a:Name>user_name</a:Name>
+<a:Code>user_name</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>用户昵称</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(30)</a:DataType>
+<a:Length>30</a:Length>
+</o:Column>
+<o:Column Id="o52">
+<a:ObjectID>CD16FFF4-F214-473B-A9A8-FA30A3E357D1</a:ObjectID>
+<a:Name>email</a:Name>
+<a:Code>email</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>用户邮箱</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(100)</a:DataType>
+<a:Length>100</a:Length>
+</o:Column>
+<o:Column Id="o53">
+<a:ObjectID>61603FA5-3EBC-4389-AED7-1B54D238A563</a:ObjectID>
+<a:Name>phonenumber</a:Name>
+<a:Code>phonenumber</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>手机号码</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(20)</a:DataType>
+<a:Length>20</a:Length>
+</o:Column>
+<o:Column Id="o54">
+<a:ObjectID>4ED1C2BF-B826-4A82-9464-EEBF271F4054</a:ObjectID>
+<a:Name>password</a:Name>
+<a:Code>password</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>密码</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(100)</a:DataType>
+<a:Length>100</a:Length>
+</o:Column>
+<o:Column Id="o55">
+<a:ObjectID>53E6BB49-3435-46E0-832F-BCAFE1A021CB</a:ObjectID>
+<a:Name>salt</a:Name>
+<a:Code>salt</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>盐加密</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(100)</a:DataType>
+<a:Length>100</a:Length>
+</o:Column>
+<o:Column Id="o56">
+<a:ObjectID>477EA57C-0E0B-4596-9A85-EC91E72F5160</a:ObjectID>
+<a:Name>user_type</a:Name>
+<a:Code>user_type</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>类型:Y默认用户,N非默认用户</a:Comment>
+<a:DefaultValue>N</a:DefaultValue>
+<a:DataType>char(1)</a:DataType>
+<a:Length>1</a:Length>
+</o:Column>
+<o:Column Id="o57">
+<a:ObjectID>245CAD53-B33B-4EED-8CFA-7AA10ED943B8</a:ObjectID>
+<a:Name>status</a:Name>
+<a:Code>status</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>帐号状态:0正常,1禁用</a:Comment>
+<a:DefaultValue>0</a:DefaultValue>
+<a:DataType>int(1)</a:DataType>
+<a:Length>1</a:Length>
+</o:Column>
+<o:Column Id="o58">
+<a:ObjectID>7F851464-6CC5-445B-9413-2A89B9CE90CB</a:ObjectID>
+<a:Name>refuse_des</a:Name>
+<a:Code>refuse_des</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>拒绝登录描述</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(500)</a:DataType>
+<a:Length>500</a:Length>
+</o:Column>
+<o:Column Id="o59">
+<a:ObjectID>3DC8EC79-D75A-4BF8-8FBC-152E938AC14F</a:ObjectID>
+<a:Name>create_by</a:Name>
+<a:Code>create_by</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>创建者</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(64)</a:DataType>
+<a:Length>64</a:Length>
+</o:Column>
+<o:Column Id="o60">
+<a:ObjectID>48C8C936-7A34-4A97-AACA-A6F07751FFAD</a:ObjectID>
+<a:Name>create_time</a:Name>
+<a:Code>create_time</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>创建时间</a:Comment>
+<a:DataType>timestamp</a:DataType>
+</o:Column>
+<o:Column Id="o61">
+<a:ObjectID>6050B4F3-9B26-4B40-AB4C-BA483F179958</a:ObjectID>
+<a:Name>update_by</a:Name>
+<a:Code>update_by</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>更新者</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(64)</a:DataType>
+<a:Length>64</a:Length>
+</o:Column>
+<o:Column Id="o62">
+<a:ObjectID>CD1E7E11-8EB6-4C9C-A69C-39CBCF10573E</a:ObjectID>
+<a:Name>update_time</a:Name>
+<a:Code>update_time</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>更新时间</a:Comment>
+<a:DataType>timestamp</a:DataType>
+</o:Column>
+</c:Columns>
+<c:Keys>
+<o:Key Id="o63">
+<a:ObjectID>2E35FD67-A7A7-4B10-85E4-85115AD0E143</a:ObjectID>
+<a:Name>Key_1</a:Name>
+<a:Code>Key_1</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<c:Key.Columns>
+<o:Column Ref="o48"/>
+</c:Key.Columns>
+</o:Key>
+</c:Keys>
+<c:PrimaryKey>
+<o:Key Ref="o63"/>
+</c:PrimaryKey>
+</o:Table>
+<o:Table Id="o10">
+<a:ObjectID>2711A520-532C-4F14-A034-BFF047C9CD6B</a:ObjectID>
+<a:Name>sys_post</a:Name>
+<a:Code>sys_post</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<c:Columns>
+<o:Column Id="o64">
+<a:ObjectID>FB04D29E-41F0-49A3-BFDB-58E222843F21</a:ObjectID>
+<a:Name>post_id</a:Name>
+<a:Code>post_id</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>岗位ID</a:Comment>
+<a:DataType>int(11)</a:DataType>
+<a:Length>11</a:Length>
+<a:Identity>1</a:Identity>
+<a:Mandatory>1</a:Mandatory>
+</o:Column>
+<o:Column Id="o65">
+<a:ObjectID>50010C4E-4F59-47B9-8F08-05E8E071E8B1</a:ObjectID>
+<a:Name>post_code</a:Name>
+<a:Code>post_code</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>岗位编码</a:Comment>
+<a:DataType>varchar(64)</a:DataType>
+<a:Length>64</a:Length>
+<a:Mandatory>1</a:Mandatory>
+</o:Column>
+<o:Column Id="o66">
+<a:ObjectID>0F929250-051E-4344-B22A-C30E071A543B</a:ObjectID>
+<a:Name>post_name</a:Name>
+<a:Code>post_name</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>岗位名称</a:Comment>
+<a:DataType>varchar(100)</a:DataType>
+<a:Length>100</a:Length>
+<a:Mandatory>1</a:Mandatory>
+</o:Column>
+<o:Column Id="o67">
+<a:ObjectID>2BC9005E-350F-46BE-98D6-9B13060F1B20</a:ObjectID>
+<a:Name>post_sort</a:Name>
+<a:Code>post_sort</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>显示顺序</a:Comment>
+<a:DataType>int(4)</a:DataType>
+<a:Length>4</a:Length>
+<a:Mandatory>1</a:Mandatory>
+</o:Column>
+<o:Column Id="o68">
+<a:ObjectID>F6D7AD3E-5EA0-4759-B6BF-6334B7105B78</a:ObjectID>
+<a:Name>status</a:Name>
+<a:Code>status</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>状态(0正常 1停用)</a:Comment>
+<a:DataType>int(1)</a:DataType>
+<a:Length>1</a:Length>
+<a:Mandatory>1</a:Mandatory>
+</o:Column>
+<o:Column Id="o69">
+<a:ObjectID>CED01369-5063-479D-A444-32936369A486</a:ObjectID>
+<a:Name>create_by</a:Name>
+<a:Code>create_by</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>创建者</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(64)</a:DataType>
+<a:Length>64</a:Length>
+</o:Column>
+<o:Column Id="o70">
+<a:ObjectID>A29528FF-A2B9-4149-B997-1B0204D42E40</a:ObjectID>
+<a:Name>create_time</a:Name>
+<a:Code>create_time</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>创建时间</a:Comment>
+<a:DataType>timestamp</a:DataType>
+</o:Column>
+<o:Column Id="o71">
+<a:ObjectID>6026A05D-0C1E-497E-8EAF-FDB704BE6A52</a:ObjectID>
+<a:Name>update_by</a:Name>
+<a:Code>update_by</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>更新者</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(64)</a:DataType>
+<a:Length>64</a:Length>
+</o:Column>
+<o:Column Id="o72">
+<a:ObjectID>DF516F5F-CD82-4347-AC57-BDCB4E5DD75E</a:ObjectID>
+<a:Name>update_time</a:Name>
+<a:Code>update_time</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>更新时间</a:Comment>
+<a:DataType>timestamp</a:DataType>
+</o:Column>
+<o:Column Id="o73">
+<a:ObjectID>539CEC34-49F0-49A0-9B7C-B84655FD2233</a:ObjectID>
+<a:Name>remark</a:Name>
+<a:Code>remark</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>备注</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(500)</a:DataType>
+<a:Length>500</a:Length>
+</o:Column>
+</c:Columns>
+<c:Keys>
+<o:Key Id="o74">
+<a:ObjectID>14E893B1-D0BA-46A7-A905-F0FFA089B65A</a:ObjectID>
+<a:Name>Key_1</a:Name>
+<a:Code>Key_1</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<c:Key.Columns>
+<o:Column Ref="o64"/>
+</c:Key.Columns>
+</o:Key>
+</c:Keys>
+<c:PrimaryKey>
+<o:Key Ref="o74"/>
+</c:PrimaryKey>
+</o:Table>
+<o:Table Id="o12">
+<a:ObjectID>11337551-BA45-43CD-9148-92BE60E2F8F5</a:ObjectID>
+<a:Name>sys_role</a:Name>
+<a:Code>sys_role</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<c:Columns>
+<o:Column Id="o75">
+<a:ObjectID>A420E2C9-8FE3-452A-9047-C7BEACE8490C</a:ObjectID>
+<a:Name>role_id</a:Name>
+<a:Code>role_id</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>角色ID</a:Comment>
+<a:DataType>int(10)</a:DataType>
+<a:Length>10</a:Length>
+<a:Identity>1</a:Identity>
+<a:Mandatory>1</a:Mandatory>
+</o:Column>
+<o:Column Id="o76">
+<a:ObjectID>9342763D-5B89-4440-965B-2B55DB4ACD86</a:ObjectID>
+<a:Name>role_name</a:Name>
+<a:Code>role_name</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>角色名称</a:Comment>
+<a:DataType>varchar(30)</a:DataType>
+<a:Length>30</a:Length>
+<a:Mandatory>1</a:Mandatory>
+</o:Column>
+<o:Column Id="o77">
+<a:ObjectID>54480009-0C7E-40F2-AA76-CD914A6D66C5</a:ObjectID>
+<a:Name>role_key</a:Name>
+<a:Code>role_key</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>角色权限字符串</a:Comment>
+<a:DataType>varchar(100)</a:DataType>
+<a:Length>100</a:Length>
+<a:Mandatory>1</a:Mandatory>
+</o:Column>
+<o:Column Id="o78">
+<a:ObjectID>E73F4D0E-12A0-42B5-B3CE-B573D499DD6C</a:ObjectID>
+<a:Name>role_sort</a:Name>
+<a:Code>role_sort</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>显示顺序</a:Comment>
+<a:DataType>int(10)</a:DataType>
+<a:Length>10</a:Length>
+<a:Mandatory>1</a:Mandatory>
+</o:Column>
+<o:Column Id="o79">
+<a:ObjectID>424ED799-E4C1-44AD-A172-C2B3C405E9C5</a:ObjectID>
+<a:Name>status</a:Name>
+<a:Code>status</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>角色状态:0正常,1禁用</a:Comment>
+<a:DefaultValue>0</a:DefaultValue>
+<a:DataType>int(1)</a:DataType>
+<a:Length>1</a:Length>
+</o:Column>
+<o:Column Id="o80">
+<a:ObjectID>214F6E1F-28B1-454B-ABF0-D1C43220129D</a:ObjectID>
+<a:Name>create_by</a:Name>
+<a:Code>create_by</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>创建者</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(64)</a:DataType>
+<a:Length>64</a:Length>
+</o:Column>
+<o:Column Id="o81">
+<a:ObjectID>1A6D5791-0353-4ABC-8BC2-921BB87A2E5A</a:ObjectID>
+<a:Name>create_time</a:Name>
+<a:Code>create_time</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>创建时间</a:Comment>
+<a:DataType>timestamp</a:DataType>
+</o:Column>
+<o:Column Id="o82">
+<a:ObjectID>D6394880-A49C-4B83-B43A-5FDBAA918AA3</a:ObjectID>
+<a:Name>update_by</a:Name>
+<a:Code>update_by</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>更新者</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(64)</a:DataType>
+<a:Length>64</a:Length>
+</o:Column>
+<o:Column Id="o83">
+<a:ObjectID>34285DF5-8E36-452B-A3AA-9F4290C20F7E</a:ObjectID>
+<a:Name>update_time</a:Name>
+<a:Code>update_time</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>更新时间</a:Comment>
+<a:DataType>timestamp</a:DataType>
+</o:Column>
+<o:Column Id="o84">
+<a:ObjectID>2FAB98F7-68A2-460B-8A20-5D5DA73F5103</a:ObjectID>
+<a:Name>remark</a:Name>
+<a:Code>remark</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>备注</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(500)</a:DataType>
+<a:Length>500</a:Length>
+</o:Column>
+</c:Columns>
+<c:Keys>
+<o:Key Id="o85">
+<a:ObjectID>4342E67F-D33C-435F-9865-973E053B6075</a:ObjectID>
+<a:Name>Key_1</a:Name>
+<a:Code>Key_1</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<c:Key.Columns>
+<o:Column Ref="o75"/>
+</c:Key.Columns>
+</o:Key>
+</c:Keys>
+<c:PrimaryKey>
+<o:Key Ref="o85"/>
+</c:PrimaryKey>
+</o:Table>
+<o:Table Id="o14">
+<a:ObjectID>FBC2A590-443B-43C9-82D5-687B850C8B3D</a:ObjectID>
+<a:Name>sys_menu</a:Name>
+<a:Code>sys_menu</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<c:Columns>
+<o:Column Id="o86">
+<a:ObjectID>BB061292-3B99-432E-9B96-5362AAD918B9</a:ObjectID>
+<a:Name>menu_id</a:Name>
+<a:Code>menu_id</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>菜单ID</a:Comment>
+<a:DataType>int(11)</a:DataType>
+<a:Length>11</a:Length>
+<a:Identity>1</a:Identity>
+<a:Mandatory>1</a:Mandatory>
+</o:Column>
+<o:Column Id="o87">
+<a:ObjectID>EA8422AB-37B1-4D60-A3C9-A4BF9039A9D4</a:ObjectID>
+<a:Name>menu_name</a:Name>
+<a:Code>menu_name</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>菜单名称</a:Comment>
+<a:DataType>varchar(50)</a:DataType>
+<a:Length>50</a:Length>
+<a:Mandatory>1</a:Mandatory>
+</o:Column>
+<o:Column Id="o88">
+<a:ObjectID>E56E04A8-63F6-4271-92E3-974DC84DD536</a:ObjectID>
+<a:Name>parent_id</a:Name>
+<a:Code>parent_id</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>父菜单ID</a:Comment>
+<a:DefaultValue>0</a:DefaultValue>
+<a:DataType>int(11)</a:DataType>
+<a:Length>11</a:Length>
+</o:Column>
+<o:Column Id="o89">
+<a:ObjectID>1809914E-6B09-4CD2-8916-E603D6717557</a:ObjectID>
+<a:Name>order_num</a:Name>
+<a:Code>order_num</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>显示顺序</a:Comment>
+<a:DefaultValue>NULL</a:DefaultValue>
+<a:DataType>int(4)</a:DataType>
+<a:Length>4</a:Length>
+</o:Column>
+<o:Column Id="o90">
+<a:ObjectID>FCB44D46-3C21-40CB-B942-57823E52E5B1</a:ObjectID>
+<a:Name>url</a:Name>
+<a:Code>url</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>请求地址</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(200)</a:DataType>
+<a:Length>200</a:Length>
+</o:Column>
+<o:Column Id="o91">
+<a:ObjectID>667EE044-6805-4668-BAF4-E78B3052051F</a:ObjectID>
+<a:Name>menu_type</a:Name>
+<a:Code>menu_type</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>类型:M目录,C菜单,F按钮</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>char(1)</a:DataType>
+<a:Length>1</a:Length>
+</o:Column>
+<o:Column Id="o92">
+<a:ObjectID>F7658083-BCAB-46F7-AF31-8A4B1D8749EF</a:ObjectID>
+<a:Name>visible</a:Name>
+<a:Code>visible</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>菜单状态:0显示,1隐藏</a:Comment>
+<a:DefaultValue>0</a:DefaultValue>
+<a:DataType>int(1)</a:DataType>
+<a:Length>1</a:Length>
+</o:Column>
+<o:Column Id="o93">
+<a:ObjectID>528611C8-C319-430F-8F00-68FBA60F310B</a:ObjectID>
+<a:Name>perms</a:Name>
+<a:Code>perms</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>权限标识</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(100)</a:DataType>
+<a:Length>100</a:Length>
+</o:Column>
+<o:Column Id="o94">
+<a:ObjectID>38004CD7-8DD0-43F1-9E59-B50132CB6F1A</a:ObjectID>
+<a:Name>icon</a:Name>
+<a:Code>icon</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>菜单图标</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(100)</a:DataType>
+<a:Length>100</a:Length>
+</o:Column>
+<o:Column Id="o95">
+<a:ObjectID>6927665F-EC42-4E1F-A275-4B27F442B6B8</a:ObjectID>
+<a:Name>create_by</a:Name>
+<a:Code>create_by</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>创建者</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(64)</a:DataType>
+<a:Length>64</a:Length>
+</o:Column>
+<o:Column Id="o96">
+<a:ObjectID>1A6A4D0F-0B0B-4522-B4DA-3F1D592CB889</a:ObjectID>
+<a:Name>create_time</a:Name>
+<a:Code>create_time</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>创建时间</a:Comment>
+<a:DataType>timestamp</a:DataType>
+</o:Column>
+<o:Column Id="o97">
+<a:ObjectID>605D7776-4820-4BA9-91E8-AD837B73AEFB</a:ObjectID>
+<a:Name>update_by</a:Name>
+<a:Code>update_by</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>更新者</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(64)</a:DataType>
+<a:Length>64</a:Length>
+</o:Column>
+<o:Column Id="o98">
+<a:ObjectID>4CFF26BB-8736-4864-855E-C7C1B133370B</a:ObjectID>
+<a:Name>update_time</a:Name>
+<a:Code>update_time</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>更新时间</a:Comment>
+<a:DataType>timestamp</a:DataType>
+</o:Column>
+<o:Column Id="o99">
+<a:ObjectID>67C6E46C-DF06-480A-BC74-E927406E5D26</a:ObjectID>
+<a:Name>remark</a:Name>
+<a:Code>remark</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>备注</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(500)</a:DataType>
+<a:Length>500</a:Length>
+</o:Column>
+</c:Columns>
+<c:Keys>
+<o:Key Id="o100">
+<a:ObjectID>08EBE713-9E4D-4312-AA7D-2E4E439734E5</a:ObjectID>
+<a:Name>Key_1</a:Name>
+<a:Code>Key_1</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<c:Key.Columns>
+<o:Column Ref="o86"/>
+</c:Key.Columns>
+</o:Key>
+</c:Keys>
+<c:PrimaryKey>
+<o:Key Ref="o100"/>
+</c:PrimaryKey>
+</o:Table>
+<o:Table Id="o16">
+<a:ObjectID>F8CB66D1-3632-4509-97C4-17016BE261FC</a:ObjectID>
+<a:Name>sys_user_role</a:Name>
+<a:Code>sys_user_role</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<c:Columns>
+<o:Column Id="o101">
+<a:ObjectID>73701F72-C45B-4CA0-8A62-632890E3DEF0</a:ObjectID>
+<a:Name>user_id</a:Name>
+<a:Code>user_id</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>用户ID</a:Comment>
+<a:DataType>int(11)</a:DataType>
+<a:Length>11</a:Length>
+<a:Mandatory>1</a:Mandatory>
+</o:Column>
+<o:Column Id="o102">
+<a:ObjectID>CABD458B-DA59-46A8-99C3-088AD8D34097</a:ObjectID>
+<a:Name>role_id</a:Name>
+<a:Code>role_id</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>角色ID</a:Comment>
+<a:DataType>int(11)</a:DataType>
+<a:Length>11</a:Length>
+<a:Mandatory>1</a:Mandatory>
+</o:Column>
+</c:Columns>
+<c:Keys>
+<o:Key Id="o103">
+<a:ObjectID>37C3213B-EF22-4CD4-A91F-9A9A2503FB2A</a:ObjectID>
+<a:Name>Key_1</a:Name>
+<a:Code>Key_1</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<c:Key.Columns>
+<o:Column Ref="o101"/>
+<o:Column Ref="o102"/>
+</c:Key.Columns>
+</o:Key>
+</c:Keys>
+<c:PrimaryKey>
+<o:Key Ref="o103"/>
+</c:PrimaryKey>
+</o:Table>
+<o:Table Id="o18">
+<a:ObjectID>9F8C6A9F-3221-410E-AEA4-D1A80026397E</a:ObjectID>
+<a:Name>sys_role_menu</a:Name>
+<a:Code>sys_role_menu</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<c:Columns>
+<o:Column Id="o104">
+<a:ObjectID>D2E151A5-6156-46EF-844E-0ADC3070293B</a:ObjectID>
+<a:Name>role_id</a:Name>
+<a:Code>role_id</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>角色ID</a:Comment>
+<a:DataType>int(11)</a:DataType>
+<a:Length>11</a:Length>
+<a:Mandatory>1</a:Mandatory>
+</o:Column>
+<o:Column Id="o105">
+<a:ObjectID>6B8C1E62-FD8B-4504-8FA0-F69917722FBD</a:ObjectID>
+<a:Name>menu_id</a:Name>
+<a:Code>menu_id</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>菜单ID</a:Comment>
+<a:DataType>int(11)</a:DataType>
+<a:Length>11</a:Length>
+<a:Mandatory>1</a:Mandatory>
+</o:Column>
+</c:Columns>
+<c:Keys>
+<o:Key Id="o106">
+<a:ObjectID>2E72304F-91F0-4392-BAE8-BBF7A4346B7D</a:ObjectID>
+<a:Name>Key_1</a:Name>
+<a:Code>Key_1</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<c:Key.Columns>
+<o:Column Ref="o104"/>
+<o:Column Ref="o105"/>
+</c:Key.Columns>
+</o:Key>
+</c:Keys>
+<c:PrimaryKey>
+<o:Key Ref="o106"/>
+</c:PrimaryKey>
+</o:Table>
+<o:Table Id="o20">
+<a:ObjectID>726CB18E-7D5B-4E2E-9CF8-047AD5AF89E3</a:ObjectID>
+<a:Name>sys_user_post</a:Name>
+<a:Code>sys_user_post</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<c:Columns>
+<o:Column Id="o107">
+<a:ObjectID>E4A1CAB6-0F63-4917-ACEF-418DE7F894BA</a:ObjectID>
+<a:Name>user_id</a:Name>
+<a:Code>user_id</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>用户ID</a:Comment>
+<a:DataType>varchar(64)</a:DataType>
+<a:Length>64</a:Length>
+<a:Mandatory>1</a:Mandatory>
+</o:Column>
+<o:Column Id="o108">
+<a:ObjectID>8E7188D5-B3A5-4F1D-B6CB-D77D652414DE</a:ObjectID>
+<a:Name>post_id</a:Name>
+<a:Code>post_id</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>岗位ID</a:Comment>
+<a:DataType>varchar(64)</a:DataType>
+<a:Length>64</a:Length>
+<a:Mandatory>1</a:Mandatory>
+</o:Column>
+</c:Columns>
+<c:Keys>
+<o:Key Id="o109">
+<a:ObjectID>4091B7D3-2404-4C20-BBCD-B63E22A5E960</a:ObjectID>
+<a:Name>Key_1</a:Name>
+<a:Code>Key_1</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<c:Key.Columns>
+<o:Column Ref="o107"/>
+<o:Column Ref="o108"/>
+</c:Key.Columns>
+</o:Key>
+</c:Keys>
+<c:PrimaryKey>
+<o:Key Ref="o109"/>
+</c:PrimaryKey>
+</o:Table>
+<o:Table Id="o22">
+<a:ObjectID>FE347A45-D8EC-423B-9B38-4D315A3ABE42</a:ObjectID>
+<a:Name>sys_oper_log</a:Name>
+<a:Code>sys_oper_log</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<c:Columns>
+<o:Column Id="o110">
+<a:ObjectID>F5FC8AC1-7415-4A57-BA2C-EE2E7B9E1EFC</a:ObjectID>
+<a:Name>oper_id</a:Name>
+<a:Code>oper_id</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>日志主键</a:Comment>
+<a:DataType>int(11)</a:DataType>
+<a:Length>11</a:Length>
+<a:Identity>1</a:Identity>
+<a:Mandatory>1</a:Mandatory>
+</o:Column>
+<o:Column Id="o111">
+<a:ObjectID>2103BC5C-E28D-4369-8369-E898B218587A</a:ObjectID>
+<a:Name>title</a:Name>
+<a:Code>title</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>模块标题</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(50)</a:DataType>
+<a:Length>50</a:Length>
+</o:Column>
+<o:Column Id="o112">
+<a:ObjectID>6816377B-3DB6-424A-99ED-1D20FEB30ED4</a:ObjectID>
+<a:Name>action</a:Name>
+<a:Code>action</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>功能请求</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(100)</a:DataType>
+<a:Length>100</a:Length>
+</o:Column>
+<o:Column Id="o113">
+<a:ObjectID>9CA3B7C3-F52C-4E2E-893F-8E6EBA7B2667</a:ObjectID>
+<a:Name>method</a:Name>
+<a:Code>method</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>方法名称</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(100)</a:DataType>
+<a:Length>100</a:Length>
+</o:Column>
+<o:Column Id="o114">
+<a:ObjectID>A5744803-C050-4108-9D15-7A0B95F03642</a:ObjectID>
+<a:Name>channel</a:Name>
+<a:Code>channel</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>来源渠道</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(20)</a:DataType>
+<a:Length>20</a:Length>
+</o:Column>
+<o:Column Id="o115">
+<a:ObjectID>B0DF8235-6BC1-452C-8B30-A56F0430E4F5</a:ObjectID>
+<a:Name>login_name</a:Name>
+<a:Code>login_name</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>登录账号</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(50)</a:DataType>
+<a:Length>50</a:Length>
+</o:Column>
+<o:Column Id="o116">
+<a:ObjectID>25315A12-4EB9-4B67-9E2C-9F40F8EF7FAB</a:ObjectID>
+<a:Name>dept_name</a:Name>
+<a:Code>dept_name</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>部门名称</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(50)</a:DataType>
+<a:Length>50</a:Length>
+</o:Column>
+<o:Column Id="o117">
+<a:ObjectID>7AF8602B-A1DA-4EA3-BFB2-7638F96A86C0</a:ObjectID>
+<a:Name>oper_url</a:Name>
+<a:Code>oper_url</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>请求URL</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(255)</a:DataType>
+<a:Length>255</a:Length>
+</o:Column>
+<o:Column Id="o118">
+<a:ObjectID>F2A56B63-7A56-43FA-8099-411F3578B30D</a:ObjectID>
+<a:Name>oper_ip</a:Name>
+<a:Code>oper_ip</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>主机地址</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(30)</a:DataType>
+<a:Length>30</a:Length>
+</o:Column>
+<o:Column Id="o119">
+<a:ObjectID>1EF1BAF6-F5C1-496C-98E0-8B10C37279A1</a:ObjectID>
+<a:Name>oper_param</a:Name>
+<a:Code>oper_param</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>请求参数</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(255)</a:DataType>
+<a:Length>255</a:Length>
+</o:Column>
+<o:Column Id="o120">
+<a:ObjectID>AA3F3A4E-D375-4232-B152-01DCFB8F6B6D</a:ObjectID>
+<a:Name>status</a:Name>
+<a:Code>status</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>操作状态 0正常 1异常</a:Comment>
+<a:DefaultValue>0</a:DefaultValue>
+<a:DataType>int(1)</a:DataType>
+<a:Length>1</a:Length>
+</o:Column>
+<o:Column Id="o121">
+<a:ObjectID>29E44D4A-6AC7-4220-A502-4BFC8746397A</a:ObjectID>
+<a:Name>error_msg</a:Name>
+<a:Code>error_msg</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>错误消息</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(2000)</a:DataType>
+<a:Length>2000</a:Length>
+</o:Column>
+<o:Column Id="o122">
+<a:ObjectID>22343C35-D913-485B-862E-2CEF579AAF22</a:ObjectID>
+<a:Name>oper_time</a:Name>
+<a:Code>oper_time</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>操作时间</a:Comment>
+<a:DataType>timestamp</a:DataType>
+</o:Column>
+</c:Columns>
+<c:Keys>
+<o:Key Id="o123">
+<a:ObjectID>C0561C20-CC22-471B-A764-414C0D378FD6</a:ObjectID>
+<a:Name>Key_1</a:Name>
+<a:Code>Key_1</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<c:Key.Columns>
+<o:Column Ref="o110"/>
+</c:Key.Columns>
+</o:Key>
+</c:Keys>
+<c:PrimaryKey>
+<o:Key Ref="o123"/>
+</c:PrimaryKey>
+</o:Table>
+<o:Table Id="o24">
+<a:ObjectID>AA2CFBA5-FA97-4AF1-92FE-645370B5848D</a:ObjectID>
+<a:Name>sys_dict_type</a:Name>
+<a:Code>sys_dict_type</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<c:Columns>
+<o:Column Id="o124">
+<a:ObjectID>79CB7D43-B999-4D92-9477-D3AFEBD94248</a:ObjectID>
+<a:Name>dict_id</a:Name>
+<a:Code>dict_id</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>字典主键</a:Comment>
+<a:DataType>int(11)</a:DataType>
+<a:Length>11</a:Length>
+<a:Identity>1</a:Identity>
+<a:Mandatory>1</a:Mandatory>
+</o:Column>
+<o:Column Id="o125">
+<a:ObjectID>2490B755-3E0A-4935-97F0-2EFDF9A72D05</a:ObjectID>
+<a:Name>dict_name</a:Name>
+<a:Code>dict_name</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>字典名称</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(100)</a:DataType>
+<a:Length>100</a:Length>
+</o:Column>
+<o:Column Id="o126">
+<a:ObjectID>7421238A-82DB-4992-AA28-41726AB6A5D6</a:ObjectID>
+<a:Name>dict_type</a:Name>
+<a:Code>dict_type</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>字典类型</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(100)</a:DataType>
+<a:Length>100</a:Length>
+</o:Column>
+<o:Column Id="o127">
+<a:ObjectID>971D2FBD-1A24-4EE4-B943-9367609C7472</a:ObjectID>
+<a:Name>status</a:Name>
+<a:Code>status</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>状态(0正常 1禁用)</a:Comment>
+<a:DefaultValue>0</a:DefaultValue>
+<a:DataType>int(1)</a:DataType>
+<a:Length>1</a:Length>
+</o:Column>
+<o:Column Id="o128">
+<a:ObjectID>B8876246-5BBA-4A03-86D7-98CA4EBEE342</a:ObjectID>
+<a:Name>create_by</a:Name>
+<a:Code>create_by</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>创建者</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(64)</a:DataType>
+<a:Length>64</a:Length>
+</o:Column>
+<o:Column Id="o129">
+<a:ObjectID>5237CED2-0853-41DE-ACF4-BE442BC9E112</a:ObjectID>
+<a:Name>create_time</a:Name>
+<a:Code>create_time</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>创建时间</a:Comment>
+<a:DataType>timestamp</a:DataType>
+</o:Column>
+<o:Column Id="o130">
+<a:ObjectID>2CACFBC0-8349-4B3A-9183-208B18C9F56F</a:ObjectID>
+<a:Name>update_by</a:Name>
+<a:Code>update_by</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>更新者</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(64)</a:DataType>
+<a:Length>64</a:Length>
+</o:Column>
+<o:Column Id="o131">
+<a:ObjectID>ABEE7806-4F61-4B97-980C-CA081F61CA7C</a:ObjectID>
+<a:Name>update_time</a:Name>
+<a:Code>update_time</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>更新时间</a:Comment>
+<a:DataType>timestamp</a:DataType>
+</o:Column>
+<o:Column Id="o132">
+<a:ObjectID>3966B558-B911-45DE-86C6-57F3DB9267BA</a:ObjectID>
+<a:Name>remark</a:Name>
+<a:Code>remark</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>备注</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(500)</a:DataType>
+<a:Length>500</a:Length>
+</o:Column>
+<o:Column Id="o133">
+<a:ObjectID>AFC0A0ED-A469-40B2-A6C4-4616444830AA</a:ObjectID>
+<a:Name>unique</a:Name>
+<a:Code>unique</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:DataType>(dict_type)</a:DataType>
+</o:Column>
+</c:Columns>
+<c:Keys>
+<o:Key Id="o134">
+<a:ObjectID>BAD40D8E-BC11-44F5-918E-B27CABBCB051</a:ObjectID>
+<a:Name>Key_1</a:Name>
+<a:Code>Key_1</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<c:Key.Columns>
+<o:Column Ref="o124"/>
+</c:Key.Columns>
+</o:Key>
+</c:Keys>
+<c:PrimaryKey>
+<o:Key Ref="o134"/>
+</c:PrimaryKey>
+</o:Table>
+<o:Table Id="o26">
+<a:ObjectID>493D6B25-21D0-45B1-BBA0-764B9C09B57D</a:ObjectID>
+<a:Name>sys_dict_data</a:Name>
+<a:Code>sys_dict_data</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<c:Columns>
+<o:Column Id="o135">
+<a:ObjectID>CFDB23A8-AE38-4051-973A-2DABAC8283F9</a:ObjectID>
+<a:Name>dict_code</a:Name>
+<a:Code>dict_code</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>字典编码</a:Comment>
+<a:DataType>int(11)</a:DataType>
+<a:Length>11</a:Length>
+<a:Identity>1</a:Identity>
+<a:Mandatory>1</a:Mandatory>
+</o:Column>
+<o:Column Id="o136">
+<a:ObjectID>EAA405BD-12A8-472F-A42D-CDA6A82E291A</a:ObjectID>
+<a:Name>dict_sort</a:Name>
+<a:Code>dict_sort</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>字典排序</a:Comment>
+<a:DefaultValue>0</a:DefaultValue>
+<a:DataType>int(4)</a:DataType>
+<a:Length>4</a:Length>
+</o:Column>
+<o:Column Id="o137">
+<a:ObjectID>F13017F5-2AA0-4DE9-9DC2-A9A3D73A98E6</a:ObjectID>
+<a:Name>dict_label</a:Name>
+<a:Code>dict_label</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>字典标签</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(100)</a:DataType>
+<a:Length>100</a:Length>
+</o:Column>
+<o:Column Id="o138">
+<a:ObjectID>EEEC4136-823D-4892-9BB9-BB0B4ADD83E3</a:ObjectID>
+<a:Name>dict_value</a:Name>
+<a:Code>dict_value</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>字典键值</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(100)</a:DataType>
+<a:Length>100</a:Length>
+</o:Column>
+<o:Column Id="o139">
+<a:ObjectID>ADF5A383-D055-40BE-BBFC-06E2B93D4E6A</a:ObjectID>
+<a:Name>dict_type</a:Name>
+<a:Code>dict_type</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>字典类型</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(100)</a:DataType>
+<a:Length>100</a:Length>
+</o:Column>
+<o:Column Id="o140">
+<a:ObjectID>1676CDF5-01CA-4749-BA1D-6E5399257BD0</a:ObjectID>
+<a:Name>status</a:Name>
+<a:Code>status</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>状态(0正常 1禁用)</a:Comment>
+<a:DefaultValue>0</a:DefaultValue>
+<a:DataType>int(1)</a:DataType>
+<a:Length>1</a:Length>
+</o:Column>
+<o:Column Id="o141">
+<a:ObjectID>8798B094-1AAF-4A23-B2F1-4C19DACF1AA3</a:ObjectID>
+<a:Name>create_by</a:Name>
+<a:Code>create_by</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>创建者</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(64)</a:DataType>
+<a:Length>64</a:Length>
+</o:Column>
+<o:Column Id="o142">
+<a:ObjectID>D1CB9293-D762-403C-85CB-4B974ACF7328</a:ObjectID>
+<a:Name>create_time</a:Name>
+<a:Code>create_time</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>创建时间</a:Comment>
+<a:DataType>timestamp</a:DataType>
+</o:Column>
+<o:Column Id="o143">
+<a:ObjectID>5A34AF87-B25E-4349-9713-69DC50F6F5F2</a:ObjectID>
+<a:Name>update_by</a:Name>
+<a:Code>update_by</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>更新者</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(64)</a:DataType>
+<a:Length>64</a:Length>
+</o:Column>
+<o:Column Id="o144">
+<a:ObjectID>3204FBAC-1F61-4571-ADC4-BF1BE9CED85A</a:ObjectID>
+<a:Name>update_time</a:Name>
+<a:Code>update_time</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>更新时间</a:Comment>
+<a:DataType>timestamp</a:DataType>
+</o:Column>
+<o:Column Id="o145">
+<a:ObjectID>B7DE1842-809C-4401-9C80-C9A37DF9B053</a:ObjectID>
+<a:Name>remark</a:Name>
+<a:Code>remark</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>备注</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(500)</a:DataType>
+<a:Length>500</a:Length>
+</o:Column>
+</c:Columns>
+<c:Keys>
+<o:Key Id="o146">
+<a:ObjectID>2809F417-7FA5-48DA-B613-662C7C28061E</a:ObjectID>
+<a:Name>Key_1</a:Name>
+<a:Code>Key_1</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<c:Key.Columns>
+<o:Column Ref="o135"/>
+</c:Key.Columns>
+</o:Key>
+</c:Keys>
+<c:PrimaryKey>
+<o:Key Ref="o146"/>
+</c:PrimaryKey>
+</o:Table>
+<o:Table Id="o28">
+<a:ObjectID>0A7C2F56-6E3B-4E70-A549-0EC60779D180</a:ObjectID>
+<a:Name>sys_logininfor</a:Name>
+<a:Code>sys_logininfor</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<c:Columns>
+<o:Column Id="o147">
+<a:ObjectID>5CB5D942-D52B-487D-BC86-476481B0FB8D</a:ObjectID>
+<a:Name>info_id</a:Name>
+<a:Code>info_id</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>访问ID</a:Comment>
+<a:DataType>int(11)</a:DataType>
+<a:Length>11</a:Length>
+<a:Identity>1</a:Identity>
+<a:Mandatory>1</a:Mandatory>
+</o:Column>
+<o:Column Id="o148">
+<a:ObjectID>A1C66DBC-9DB7-428B-9275-3D014B6CE388</a:ObjectID>
+<a:Name>login_name</a:Name>
+<a:Code>login_name</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>登录账号</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(50)</a:DataType>
+<a:Length>50</a:Length>
+</o:Column>
+<o:Column Id="o149">
+<a:ObjectID>8E0F50A6-F98D-48B0-8D9D-78F3A76ED171</a:ObjectID>
+<a:Name>ipaddr</a:Name>
+<a:Code>ipaddr</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>登录IP地址</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(50)</a:DataType>
+<a:Length>50</a:Length>
+</o:Column>
+<o:Column Id="o150">
+<a:ObjectID>AA04F533-A044-428B-80F8-515B6BB1A302</a:ObjectID>
+<a:Name>browser</a:Name>
+<a:Code>browser</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>浏览器类型</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(50)</a:DataType>
+<a:Length>50</a:Length>
+</o:Column>
+<o:Column Id="o151">
+<a:ObjectID>D37570E9-9EEE-4349-B875-494A5415C736</a:ObjectID>
+<a:Name>os</a:Name>
+<a:Code>os</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>操作系统</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(50)</a:DataType>
+<a:Length>50</a:Length>
+</o:Column>
+<o:Column Id="o152">
+<a:ObjectID>CF10A80C-123E-42F3-A2DD-1B770E5F9D86</a:ObjectID>
+<a:Name>status</a:Name>
+<a:Code>status</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>登录状态 0成功 1失败</a:Comment>
+<a:DefaultValue>0</a:DefaultValue>
+<a:DataType>int(1)</a:DataType>
+<a:Length>1</a:Length>
+</o:Column>
+<o:Column Id="o153">
+<a:ObjectID>9113784E-932A-4FAF-82CB-A75B8C827309</a:ObjectID>
+<a:Name>msg</a:Name>
+<a:Code>msg</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>提示消息</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(255)</a:DataType>
+<a:Length>255</a:Length>
+</o:Column>
+<o:Column Id="o154">
+<a:ObjectID>BCA519C6-19C9-45DF-A0B5-F88E9E6D3557</a:ObjectID>
+<a:Name>login_time</a:Name>
+<a:Code>login_time</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>访问时间</a:Comment>
+<a:DataType>timestamp</a:DataType>
+</o:Column>
+</c:Columns>
+<c:Keys>
+<o:Key Id="o155">
+<a:ObjectID>C14E656C-0645-49EB-8B42-AD82232E0416</a:ObjectID>
+<a:Name>Key_1</a:Name>
+<a:Code>Key_1</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<c:Key.Columns>
+<o:Column Ref="o147"/>
+</c:Key.Columns>
+</o:Key>
+</c:Keys>
+<c:PrimaryKey>
+<o:Key Ref="o155"/>
+</c:PrimaryKey>
+</o:Table>
+<o:Table Id="o30">
+<a:ObjectID>4DCA223F-E98B-4D8B-A71C-CFB438C15488</a:ObjectID>
+<a:Name>sys_user_online</a:Name>
+<a:Code>sys_user_online</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<c:Columns>
+<o:Column Id="o156">
+<a:ObjectID>7FCC57CE-47DD-4948-B949-10401B2FC7B1</a:ObjectID>
+<a:Name>sessionId</a:Name>
+<a:Code>sessionId</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>用户会话id</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(50)</a:DataType>
+<a:Length>50</a:Length>
+<a:Mandatory>1</a:Mandatory>
+</o:Column>
+<o:Column Id="o157">
+<a:ObjectID>FDE5B59D-8CF7-4AAE-987F-3FF2AEBE22CB</a:ObjectID>
+<a:Name>login_name</a:Name>
+<a:Code>login_name</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>登录账号</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(50)</a:DataType>
+<a:Length>50</a:Length>
+</o:Column>
+<o:Column Id="o158">
+<a:ObjectID>AB65FF92-33A0-42C8-8B3F-454A1FAD5615</a:ObjectID>
+<a:Name>dept_name</a:Name>
+<a:Code>dept_name</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>部门名称</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(50)</a:DataType>
+<a:Length>50</a:Length>
+</o:Column>
+<o:Column Id="o159">
+<a:ObjectID>C4DAF2D0-9CDC-476B-A011-FF5D302371EB</a:ObjectID>
+<a:Name>ipaddr</a:Name>
+<a:Code>ipaddr</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>登录IP地址</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(50)</a:DataType>
+<a:Length>50</a:Length>
+</o:Column>
+<o:Column Id="o160">
+<a:ObjectID>89EC40B0-0C22-4811-90BB-BEA385ACDF20</a:ObjectID>
+<a:Name>browser</a:Name>
+<a:Code>browser</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>浏览器类型</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(50)</a:DataType>
+<a:Length>50</a:Length>
+</o:Column>
+<o:Column Id="o161">
+<a:ObjectID>AC455631-CFE0-45BB-A0C5-788D695E4B6C</a:ObjectID>
+<a:Name>os</a:Name>
+<a:Code>os</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>操作系统</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(50)</a:DataType>
+<a:Length>50</a:Length>
+</o:Column>
+<o:Column Id="o162">
+<a:ObjectID>5C56E3C9-4591-4762-89E1-C9BBFECB5F11</a:ObjectID>
+<a:Name>status</a:Name>
+<a:Code>status</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>在线状态on_line在线off_line离线</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(10)</a:DataType>
+<a:Length>10</a:Length>
+</o:Column>
+<o:Column Id="o163">
+<a:ObjectID>0CAF2F1F-459F-4F78-9075-D95F924A4FF7</a:ObjectID>
+<a:Name>start_timestsamp</a:Name>
+<a:Code>start_timestsamp</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>session创建时间</a:Comment>
+<a:DataType>timestamp</a:DataType>
+</o:Column>
+<o:Column Id="o164">
+<a:ObjectID>6AE6BDED-823E-4455-9A9F-338EC6F7BDB9</a:ObjectID>
+<a:Name>last_access_time</a:Name>
+<a:Code>last_access_time</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>session最后访问时间</a:Comment>
+<a:DataType>timestamp</a:DataType>
+</o:Column>
+<o:Column Id="o165">
+<a:ObjectID>CE390924-4628-421C-979F-002C2952E99E</a:ObjectID>
+<a:Name>expire_time</a:Name>
+<a:Code>expire_time</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>超时时间,单位为分钟</a:Comment>
+<a:DefaultValue>0</a:DefaultValue>
+<a:DataType>int(5)</a:DataType>
+<a:Length>5</a:Length>
+</o:Column>
+</c:Columns>
+<c:Keys>
+<o:Key Id="o166">
+<a:ObjectID>365CC94D-6124-42C7-96BD-376B84B709F7</a:ObjectID>
+<a:Name>Key_1</a:Name>
+<a:Code>Key_1</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<c:Key.Columns>
+<o:Column Ref="o156"/>
+</c:Key.Columns>
+</o:Key>
+</c:Keys>
+<c:PrimaryKey>
+<o:Key Ref="o166"/>
+</c:PrimaryKey>
+</o:Table>
+<o:Table Id="o32">
+<a:ObjectID>AFCBF4DB-07EC-42D1-ACA7-56B5038F5AC5</a:ObjectID>
+<a:Name>sys_job</a:Name>
+<a:Code>sys_job</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<c:Columns>
+<o:Column Id="o167">
+<a:ObjectID>1658CED4-3885-4094-AB70-F35408EBCD5E</a:ObjectID>
+<a:Name>job_id</a:Name>
+<a:Code>job_id</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>任务ID</a:Comment>
+<a:DataType>int(11)</a:DataType>
+<a:Length>11</a:Length>
+<a:Identity>1</a:Identity>
+<a:Mandatory>1</a:Mandatory>
+</o:Column>
+<o:Column Id="o168">
+<a:ObjectID>731E7147-E3A4-4D93-8C7C-BB1C6D94DB9E</a:ObjectID>
+<a:Name>job_name</a:Name>
+<a:Code>job_name</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>任务名称</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(64)</a:DataType>
+<a:Length>64</a:Length>
+<a:Mandatory>1</a:Mandatory>
+</o:Column>
+<o:Column Id="o169">
+<a:ObjectID>C64B3655-C240-44F0-83B4-F42FB76C8BEA</a:ObjectID>
+<a:Name>job_group</a:Name>
+<a:Code>job_group</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>任务组名</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(64)</a:DataType>
+<a:Length>64</a:Length>
+<a:Mandatory>1</a:Mandatory>
+</o:Column>
+<o:Column Id="o170">
+<a:ObjectID>9F7E735D-B823-4ADA-BA3D-8FFFFEC92F5C</a:ObjectID>
+<a:Name>method_name</a:Name>
+<a:Code>method_name</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>任务方法</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(500)</a:DataType>
+<a:Length>500</a:Length>
+</o:Column>
+<o:Column Id="o171">
+<a:ObjectID>28EEE4F4-E8E7-4052-8F10-88D6C74C595D</a:ObjectID>
+<a:Name>params</a:Name>
+<a:Code>params</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>方法参数</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(200)</a:DataType>
+<a:Length>200</a:Length>
+</o:Column>
+<o:Column Id="o172">
+<a:ObjectID>C8986FAD-E2E7-4364-9E8B-B75366B9A4ED</a:ObjectID>
+<a:Name>cron_expression</a:Name>
+<a:Code>cron_expression</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>cron执行表达式</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(255)</a:DataType>
+<a:Length>255</a:Length>
+</o:Column>
+<o:Column Id="o173">
+<a:ObjectID>2D4B6C8F-EEE8-4474-9D20-8206A7E80362</a:ObjectID>
+<a:Name>status</a:Name>
+<a:Code>status</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>状态(0正常 1暂停)</a:Comment>
+<a:DefaultValue>0</a:DefaultValue>
+<a:DataType>int(1)</a:DataType>
+<a:Length>1</a:Length>
+</o:Column>
+<o:Column Id="o174">
+<a:ObjectID>CA78AC7F-19E7-47BC-BF7B-9F31EFB02702</a:ObjectID>
+<a:Name>create_by</a:Name>
+<a:Code>create_by</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>创建者</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(64)</a:DataType>
+<a:Length>64</a:Length>
+</o:Column>
+<o:Column Id="o175">
+<a:ObjectID>B8F807AE-9F19-4FCA-BA98-7BF71DD0CA02</a:ObjectID>
+<a:Name>create_time</a:Name>
+<a:Code>create_time</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>创建时间</a:Comment>
+<a:DataType>timestamp</a:DataType>
+</o:Column>
+<o:Column Id="o176">
+<a:ObjectID>3FBB42FA-ED0F-4D7C-99D0-5F7AF7B0F1DD</a:ObjectID>
+<a:Name>update_by</a:Name>
+<a:Code>update_by</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>更新者</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(64)</a:DataType>
+<a:Length>64</a:Length>
+</o:Column>
+<o:Column Id="o177">
+<a:ObjectID>1C5863D2-A8B9-43DB-AA06-F8BE3E01093B</a:ObjectID>
+<a:Name>update_time</a:Name>
+<a:Code>update_time</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>更新时间</a:Comment>
+<a:DataType>timestamp</a:DataType>
+</o:Column>
+<o:Column Id="o178">
+<a:ObjectID>889C3FF9-BB1E-4EB1-AFE9-1D1155984915</a:ObjectID>
+<a:Name>remark</a:Name>
+<a:Code>remark</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>备注信息</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(500)</a:DataType>
+<a:Length>500</a:Length>
+</o:Column>
+</c:Columns>
+<c:Keys>
+<o:Key Id="o179">
+<a:ObjectID>38106F1A-4FFB-4EC0-B979-55BD6C6C6FF7</a:ObjectID>
+<a:Name>Key_1</a:Name>
+<a:Code>Key_1</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<c:Key.Columns>
+<o:Column Ref="o167"/>
+<o:Column Ref="o168"/>
+<o:Column Ref="o169"/>
+</c:Key.Columns>
+</o:Key>
+</c:Keys>
+<c:PrimaryKey>
+<o:Key Ref="o179"/>
+</c:PrimaryKey>
+</o:Table>
+<o:Table Id="o34">
+<a:ObjectID>CF7C8958-5494-48C6-BE05-83F2CF8C7513</a:ObjectID>
+<a:Name>sys_job_log</a:Name>
+<a:Code>sys_job_log</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<c:Columns>
+<o:Column Id="o180">
+<a:ObjectID>308F32A1-A8EC-4002-9993-DF9234A303B7</a:ObjectID>
+<a:Name>job_log_id</a:Name>
+<a:Code>job_log_id</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>任务日志ID</a:Comment>
+<a:DataType>int(11)</a:DataType>
+<a:Length>11</a:Length>
+<a:Identity>1</a:Identity>
+<a:Mandatory>1</a:Mandatory>
+</o:Column>
+<o:Column Id="o181">
+<a:ObjectID>F4D55B65-BB6B-4182-A6D6-F9CAABC19110</a:ObjectID>
+<a:Name>job_name</a:Name>
+<a:Code>job_name</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>任务名称</a:Comment>
+<a:DataType>varchar(64)</a:DataType>
+<a:Length>64</a:Length>
+<a:Mandatory>1</a:Mandatory>
+</o:Column>
+<o:Column Id="o182">
+<a:ObjectID>8AF383A0-01C0-4947-8384-FF0F13AC00AE</a:ObjectID>
+<a:Name>job_group</a:Name>
+<a:Code>job_group</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>任务组名</a:Comment>
+<a:DataType>varchar(64)</a:DataType>
+<a:Length>64</a:Length>
+<a:Mandatory>1</a:Mandatory>
+</o:Column>
+<o:Column Id="o183">
+<a:ObjectID>96582B76-F1E9-4473-BA51-01B87B5F459E</a:ObjectID>
+<a:Name>method_name</a:Name>
+<a:Code>method_name</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>任务方法</a:Comment>
+<a:DataType>varchar(500)</a:DataType>
+<a:Length>500</a:Length>
+</o:Column>
+<o:Column Id="o184">
+<a:ObjectID>2AB02ABA-02E3-4F72-95BA-4261A7F5729A</a:ObjectID>
+<a:Name>params</a:Name>
+<a:Code>params</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>方法参数</a:Comment>
+<a:DefaultValue>&#39;&#39;</a:DefaultValue>
+<a:DataType>varchar(200)</a:DataType>
+<a:Length>200</a:Length>
+</o:Column>
+<o:Column Id="o185">
+<a:ObjectID>8EB39444-CBFF-43AA-AA37-49217EF545B6</a:ObjectID>
+<a:Name>job_message</a:Name>
+<a:Code>job_message</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>日志信息</a:Comment>
+<a:DataType>varchar(500)</a:DataType>
+<a:Length>500</a:Length>
+</o:Column>
+<o:Column Id="o186">
+<a:ObjectID>18CD263C-0F57-4EDF-999E-1B5A7EE2BFF9</a:ObjectID>
+<a:Name>is_exception</a:Name>
+<a:Code>is_exception</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>是否异常</a:Comment>
+<a:DefaultValue>0</a:DefaultValue>
+<a:DataType>int(1)</a:DataType>
+<a:Length>1</a:Length>
+</o:Column>
+<o:Column Id="o187">
+<a:ObjectID>634ECD78-2251-43EB-B6CF-DF7FA9DA4354</a:ObjectID>
+<a:Name>exception_info</a:Name>
+<a:Code>exception_info</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>异常信息</a:Comment>
+<a:DataType>text</a:DataType>
+</o:Column>
+<o:Column Id="o188">
+<a:ObjectID>4EC075CC-507B-43D7-860F-34DAAEB1DBBF</a:ObjectID>
+<a:Name>create_time</a:Name>
+<a:Code>create_time</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:Comment>创建时间</a:Comment>
+<a:DataType>timestamp</a:DataType>
+</o:Column>
+</c:Columns>
+<c:Keys>
+<o:Key Id="o189">
+<a:ObjectID>A87DCE10-894A-4CF7-B39C-AF18202C7F86</a:ObjectID>
+<a:Name>Key_1</a:Name>
+<a:Code>Key_1</a:Code>
+<a:CreationDate>1524449375</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449375</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<c:Key.Columns>
+<o:Column Ref="o180"/>
+</c:Key.Columns>
+</o:Key>
+</c:Keys>
+<c:PrimaryKey>
+<o:Key Ref="o189"/>
+</c:PrimaryKey>
+</o:Table>
+</c:Tables>
+<c:DefaultGroups>
+<o:Group Id="o190">
+<a:ObjectID>F2EBEA5B-F352-45CB-B349-39158064CEE8</a:ObjectID>
+<a:Name>PUBLIC</a:Name>
+<a:Code>PUBLIC</a:Code>
+<a:CreationDate>1524449325</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449325</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+</o:Group>
+</c:DefaultGroups>
+<c:TargetModels>
+<o:TargetModel Id="o191">
+<a:ObjectID>41740AEF-D7FB-4738-ABDF-47C3287A6AF6</a:ObjectID>
+<a:Name>MySQL 5.0</a:Name>
+<a:Code>MYSQL50</a:Code>
+<a:CreationDate>1524449337</a:CreationDate>
+<a:Creator>Administrator</a:Creator>
+<a:ModificationDate>1524449337</a:ModificationDate>
+<a:Modifier>Administrator</a:Modifier>
+<a:TargetModelURL>file:///%_DBMS%/mysql50.xdb</a:TargetModelURL>
+<a:TargetModelID>F4F16ECD-F2F1-4006-AF6F-638D5C65F35E</a:TargetModelID>
+<a:TargetModelClassID>4BA9F647-DAB1-11D1-9944-006097355D9B</a:TargetModelClassID>
+<c:SessionShortcuts>
+<o:Shortcut Ref="o3"/>
+</c:SessionShortcuts>
+</o:TargetModel>
+</c:TargetModels>
+</o:Model>
+</c:Children>
+</o:RootObject>
+
+</Model>

+ 594 - 0
sql/ry_20180701.sql

@@ -0,0 +1,594 @@
+-- ----------------------------
+-- 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',  '操作日志', '2', '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('109',  '登录日志', '2', '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('110',  '在线用户', '2', '3', '/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('111',  '定时任务', '2', '4', '/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('112',  '数据监控', '2', '5', '/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('113',  '表单构建', '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('114',  '代码生成', '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('115',  '系统接口', '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('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', '操作查询', '108', '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', '操作删除', '108', '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', '详细信息', '108', '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', '登录查询', '109', '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', '登录删除', '109', '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', '在线查询', '110', '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', '批量强退', '110', '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', '单条强退', '110', '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', '任务查询', '111', '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', '任务新增', '111', '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', '任务修改', '111', '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', '任务删除', '111', '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', '任务保存', '111', '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', '状态修改', '111', '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', '生成查询', '114', '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', '生成代码', '114', '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', '115');
+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', '管理员');

+ 33 - 0
src/main/java/com/ruoyi/RuoYiApplication.java

@@ -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" +
+                " ''-'   `'-'    `-..-'              ");
+    }
+}

+ 19 - 0
src/main/java/com/ruoyi/RuoYiServletInitializer.java

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

+ 46 - 0
src/main/java/com/ruoyi/common/constant/CommonMap.java

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

+ 65 - 0
src/main/java/com/ruoyi/common/constant/Constants.java

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

+ 39 - 0
src/main/java/com/ruoyi/common/constant/ScheduleConstants.java

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

+ 65 - 0
src/main/java/com/ruoyi/common/constant/ShiroConstants.java

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

+ 80 - 0
src/main/java/com/ruoyi/common/constant/UserConstants.java

@@ -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])))\\.?";
+
+}

+ 15 - 0
src/main/java/com/ruoyi/common/exception/DemoModeException.java

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

+ 105 - 0
src/main/java/com/ruoyi/common/exception/base/BaseException.java

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

+ 41 - 0
src/main/java/com/ruoyi/common/exception/file/FileNameLengthLimitExceededException.java

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

+ 74 - 0
src/main/java/com/ruoyi/common/exception/file/InvalidExtensionException.java

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

+ 16 - 0
src/main/java/com/ruoyi/common/exception/user/CaptchaException.java

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

+ 18 - 0
src/main/java/com/ruoyi/common/exception/user/RoleBlockedException.java

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

+ 16 - 0
src/main/java/com/ruoyi/common/exception/user/UserBlockedException.java

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

+ 20 - 0
src/main/java/com/ruoyi/common/exception/user/UserException.java

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

+ 17 - 0
src/main/java/com/ruoyi/common/exception/user/UserNotExistsException.java

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

+ 17 - 0
src/main/java/com/ruoyi/common/exception/user/UserPasswordNotMatchException.java

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

+ 16 - 0
src/main/java/com/ruoyi/common/exception/user/UserPasswordRetryLimitCountException.java

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

+ 16 - 0
src/main/java/com/ruoyi/common/exception/user/UserPasswordRetryLimitExceedException.java

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

+ 88 - 0
src/main/java/com/ruoyi/common/support/CharsetKit.java

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

+ 1002 - 0
src/main/java/com/ruoyi/common/support/Convert.java

@@ -0,0 +1,1002 @@
+package com.ruoyi.common.support;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.text.NumberFormat;
+import java.util.Set;
+import com.ruoyi.common.utils.StringUtils;
+
+/**
+ * 类型转换器
+ * 
+ * @author ruoyi
+ * 
+ */
+public class Convert
+{
+    /**
+     * 转换为字符串<br>
+     * 如果给定的值为null,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static String toStr(Object value, String defaultValue)
+    {
+        if (null == value)
+        {
+            return defaultValue;
+        }
+        if (value instanceof String)
+        {
+            return (String) value;
+        }
+        return value.toString();
+    }
+
+    /**
+     * 转换为字符串<br>
+     * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static String toStr(Object value)
+    {
+        return toStr(value, null);
+    }
+
+    /**
+     * 转换为字符<br>
+     * 如果给定的值为null,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static Character toChar(Object value, Character defaultValue)
+    {
+        if (null == value)
+        {
+            return defaultValue;
+        }
+        if (value instanceof Character)
+        {
+            return (Character) value;
+        }
+
+        final String valueStr = toStr(value, null);
+        return StringUtils.isEmpty(valueStr) ? defaultValue : valueStr.charAt(0);
+    }
+
+    /**
+     * 转换为字符<br>
+     * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static Character toChar(Object value)
+    {
+        return toChar(value, null);
+    }
+
+    /**
+     * 转换为byte<br>
+     * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static Byte toByte(Object value, Byte defaultValue)
+    {
+        if (value == null)
+        {
+            return defaultValue;
+        }
+        if (value instanceof Byte)
+        {
+            return (Byte) value;
+        }
+        if (value instanceof Number)
+        {
+            return ((Number) value).byteValue();
+        }
+        final String valueStr = toStr(value, null);
+        if (StringUtils.isEmpty(valueStr))
+        {
+            return defaultValue;
+        }
+        try
+        {
+            return Byte.parseByte(valueStr);
+        }
+        catch (Exception e)
+        {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * 转换为byte<br>
+     * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static Byte toByte(Object value)
+    {
+        return toByte(value, null);
+    }
+
+    /**
+     * 转换为Short<br>
+     * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static Short toShort(Object value, Short defaultValue)
+    {
+        if (value == null)
+        {
+            return defaultValue;
+        }
+        if (value instanceof Short)
+        {
+            return (Short) value;
+        }
+        if (value instanceof Number)
+        {
+            return ((Number) value).shortValue();
+        }
+        final String valueStr = toStr(value, null);
+        if (StringUtils.isEmpty(valueStr))
+        {
+            return defaultValue;
+        }
+        try
+        {
+            return Short.parseShort(valueStr.trim());
+        }
+        catch (Exception e)
+        {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * 转换为Short<br>
+     * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static Short toShort(Object value)
+    {
+        return toShort(value, null);
+    }
+
+    /**
+     * 转换为Number<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static Number toNumber(Object value, Number defaultValue)
+    {
+        if (value == null)
+        {
+            return defaultValue;
+        }
+        if (value instanceof Number)
+        {
+            return (Number) value;
+        }
+        final String valueStr = toStr(value, null);
+        if (StringUtils.isEmpty(valueStr))
+        {
+            return defaultValue;
+        }
+        try
+        {
+            return NumberFormat.getInstance().parse(valueStr);
+        }
+        catch (Exception e)
+        {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * 转换为Number<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static Number toNumber(Object value)
+    {
+        return toNumber(value, null);
+    }
+
+    /**
+     * 转换为int<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static Integer toInt(Object value, Integer defaultValue)
+    {
+        if (value == null)
+        {
+            return defaultValue;
+        }
+        if (value instanceof Integer)
+        {
+            return (Integer) value;
+        }
+        if (value instanceof Number)
+        {
+            return ((Number) value).intValue();
+        }
+        final String valueStr = toStr(value, null);
+        if (StringUtils.isEmpty(valueStr))
+        {
+            return defaultValue;
+        }
+        try
+        {
+            return Integer.parseInt(valueStr.trim());
+        }
+        catch (Exception e)
+        {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * 转换为int<br>
+     * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static Integer toInt(Object value)
+    {
+        return toInt(value, null);
+    }
+
+    /**
+     * 转换为Integer数组<br>
+     * 
+     * @param split 被转换的值
+     * @return 结果
+     */
+    public static Integer[] toIntArray(String str)
+    {
+        return toIntArray(",", str);
+    }
+
+    /**
+     * 转换为Long数组<br>
+     * 
+     * @param split 被转换的值
+     * @return 结果
+     */
+    public static Long[] toLongArray(String str)
+    {
+        return toLongArray(",", str);
+    }
+
+    /**
+     * 转换为Integer数组<br>
+     * 
+     * @param split 分隔符
+     * @param split 被转换的值
+     * @return 结果
+     */
+    public static Integer[] toIntArray(String split, String str)
+    {
+        if (StringUtils.isEmpty(str))
+        {
+            return new Integer[] {};
+        }
+        String[] arr = str.split(split);
+        final Integer[] ints = new Integer[arr.length];
+        for (int i = 0; i < arr.length; i++)
+        {
+            final Integer v = toInt(arr[i], 0);
+            ints[i] = v;
+        }
+        return ints;
+    }
+
+    /**
+     * 转换为Long数组<br>
+     * 
+     * @param isIgnoreConvertError 是否忽略转换错误,忽略则给值null
+     * @param values 被转换的值
+     * @return 结果
+     */
+    public static Long[] toLongArray(String split, String str)
+    {
+        if (StringUtils.isEmpty(str))
+        {
+            return new Long[] {};
+        }
+        String[] arr = str.split(split);
+        final Long[] longs = new Long[arr.length];
+        for (int i = 0; i < arr.length; i++)
+        {
+            final Long v = toLong(arr[i], null);
+            longs[i] = v;
+        }
+        return longs;
+    }
+
+    /**
+     * 转换为String数组<br>
+     * 
+     * @param split 被转换的值
+     * @return 结果
+     */
+    public static String[] toStrArray(String str)
+    {
+        return toStrArray(",", str);
+    }
+
+    /**
+     * 转换为String数组<br>
+     * 
+     * @param split 分隔符
+     * @param split 被转换的值
+     * @return 结果
+     */
+    public static String[] toStrArray(String split, String str)
+    {
+        return str.split(split);
+    }
+
+    /**
+     * 转换为long<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static Long toLong(Object value, Long defaultValue)
+    {
+        if (value == null)
+        {
+            return defaultValue;
+        }
+        if (value instanceof Long)
+        {
+            return (Long) value;
+        }
+        if (value instanceof Number)
+        {
+            return ((Number) value).longValue();
+        }
+        final String valueStr = toStr(value, null);
+        if (StringUtils.isEmpty(valueStr))
+        {
+            return defaultValue;
+        }
+        try
+        {
+            // 支持科学计数法
+            return new BigDecimal(valueStr.trim()).longValue();
+        }
+        catch (Exception e)
+        {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * 转换为long<br>
+     * 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static Long toLong(Object value)
+    {
+        return toLong(value, null);
+    }
+
+    /**
+     * 转换为double<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static Double toDouble(Object value, Double defaultValue)
+    {
+        if (value == null)
+        {
+            return defaultValue;
+        }
+        if (value instanceof Double)
+        {
+            return (Double) value;
+        }
+        if (value instanceof Number)
+        {
+            return ((Number) value).doubleValue();
+        }
+        final String valueStr = toStr(value, null);
+        if (StringUtils.isEmpty(valueStr))
+        {
+            return defaultValue;
+        }
+        try
+        {
+            // 支持科学计数法
+            return new BigDecimal(valueStr.trim()).doubleValue();
+        }
+        catch (Exception e)
+        {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * 转换为double<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static Double toDouble(Object value)
+    {
+        return toDouble(value, null);
+    }
+
+    /**
+     * 转换为Float<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static Float toFloat(Object value, Float defaultValue)
+    {
+        if (value == null)
+        {
+            return defaultValue;
+        }
+        if (value instanceof Float)
+        {
+            return (Float) value;
+        }
+        if (value instanceof Number)
+        {
+            return ((Number) value).floatValue();
+        }
+        final String valueStr = toStr(value, null);
+        if (StringUtils.isEmpty(valueStr))
+        {
+            return defaultValue;
+        }
+        try
+        {
+            return Float.parseFloat(valueStr.trim());
+        }
+        catch (Exception e)
+        {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * 转换为Float<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static Float toFloat(Object value)
+    {
+        return toFloat(value, null);
+    }
+
+    /**
+     * 转换为boolean<br>
+     * String支持的值为:true、false、yes、ok、no,1,0 如果给定的值为空,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static Boolean toBool(Object value, Boolean defaultValue)
+    {
+        if (value == null)
+        {
+            return defaultValue;
+        }
+        if (value instanceof Boolean)
+        {
+            return (Boolean) value;
+        }
+        String valueStr = toStr(value, null);
+        if (StringUtils.isEmpty(valueStr))
+        {
+            return defaultValue;
+        }
+        valueStr = valueStr.trim().toLowerCase();
+        switch (valueStr)
+        {
+            case "true":
+                return true;
+            case "false":
+                return false;
+            case "yes":
+                return true;
+            case "ok":
+                return true;
+            case "no":
+                return false;
+            case "1":
+                return true;
+            case "0":
+                return false;
+            default:
+                return defaultValue;
+        }
+    }
+
+    /**
+     * 转换为boolean<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static Boolean toBool(Object value)
+    {
+        return toBool(value, null);
+    }
+
+    /**
+     * 转换为Enum对象<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<br>
+     * 
+     * @param clazz Enum的Class
+     * @param value 值
+     * @param defaultValue 默认值
+     * @return Enum
+     */
+    public static <E extends Enum<E>> E toEnum(Class<E> clazz, Object value, E defaultValue)
+    {
+        if (value == null)
+        {
+            return defaultValue;
+        }
+        if (clazz.isAssignableFrom(value.getClass()))
+        {
+            @SuppressWarnings("unchecked")
+            E myE = (E) value;
+            return myE;
+        }
+        final String valueStr = toStr(value, null);
+        if (StringUtils.isEmpty(valueStr))
+        {
+            return defaultValue;
+        }
+        try
+        {
+            return Enum.valueOf(clazz, valueStr);
+        }
+        catch (Exception e)
+        {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * 转换为Enum对象<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
+     * 
+     * @param clazz Enum的Class
+     * @param value 值
+     * @return Enum
+     */
+    public static <E extends Enum<E>> E toEnum(Class<E> clazz, Object value)
+    {
+        return toEnum(clazz, value, null);
+    }
+
+    /**
+     * 转换为BigInteger<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static BigInteger toBigInteger(Object value, BigInteger defaultValue)
+    {
+        if (value == null)
+        {
+            return defaultValue;
+        }
+        if (value instanceof BigInteger)
+        {
+            return (BigInteger) value;
+        }
+        if (value instanceof Long)
+        {
+            return BigInteger.valueOf((Long) value);
+        }
+        final String valueStr = toStr(value, null);
+        if (StringUtils.isEmpty(valueStr))
+        {
+            return defaultValue;
+        }
+        try
+        {
+            return new BigInteger(valueStr);
+        }
+        catch (Exception e)
+        {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * 转换为BigInteger<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static BigInteger toBigInteger(Object value)
+    {
+        return toBigInteger(value, null);
+    }
+
+    /**
+     * 转换为BigDecimal<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @param defaultValue 转换错误时的默认值
+     * @return 结果
+     */
+    public static BigDecimal toBigDecimal(Object value, BigDecimal defaultValue)
+    {
+        if (value == null)
+        {
+            return defaultValue;
+        }
+        if (value instanceof BigDecimal)
+        {
+            return (BigDecimal) value;
+        }
+        if (value instanceof Long)
+        {
+            return new BigDecimal((Long) value);
+        }
+        if (value instanceof Double)
+        {
+            return new BigDecimal((Double) value);
+        }
+        if (value instanceof Integer)
+        {
+            return new BigDecimal((Integer) value);
+        }
+        final String valueStr = toStr(value, null);
+        if (StringUtils.isEmpty(valueStr))
+        {
+            return defaultValue;
+        }
+        try
+        {
+            return new BigDecimal(valueStr);
+        }
+        catch (Exception e)
+        {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * 转换为BigDecimal<br>
+     * 如果给定的值为空,或者转换失败,返回默认值<br>
+     * 转换失败不会报错
+     * 
+     * @param value 被转换的值
+     * @return 结果
+     */
+    public static BigDecimal toBigDecimal(Object value)
+    {
+        return toBigDecimal(value, null);
+    }
+
+    /**
+     * 将对象转为字符串<br>
+     * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法
+     * 
+     * @param obj 对象
+     * @return 字符串
+     */
+    public static String utf8Str(Object obj)
+    {
+        return str(obj, CharsetKit.CHARSET_UTF_8);
+    }
+
+    /**
+     * 将对象转为字符串<br>
+     * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法
+     * 
+     * @param obj 对象
+     * @param charsetName 字符集
+     * @return 字符串
+     */
+    public static String str(Object obj, String charsetName)
+    {
+        return str(obj, Charset.forName(charsetName));
+    }
+
+    /**
+     * 将对象转为字符串<br>
+     * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法
+     * 
+     * @param obj 对象
+     * @param charset 字符集
+     * @return 字符串
+     */
+    public static String str(Object obj, Charset charset)
+    {
+        if (null == obj)
+        {
+            return null;
+        }
+
+        if (obj instanceof String)
+        {
+            return (String) obj;
+        }
+        else if (obj instanceof byte[] || obj instanceof Byte[])
+        {
+            return str((Byte[]) obj, charset);
+        }
+        else if (obj instanceof ByteBuffer)
+        {
+            return str((ByteBuffer) obj, charset);
+        }
+        return obj.toString();
+    }
+
+    /**
+     * 将byte数组转为字符串
+     * 
+     * @param bytes byte数组
+     * @param charset 字符集
+     * @return 字符串
+     */
+    public static String str(byte[] bytes, String charset)
+    {
+        return str(bytes, StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset));
+    }
+
+    /**
+     * 解码字节码
+     * 
+     * @param data 字符串
+     * @param charset 字符集,如果此字段为空,则解码的结果取决于平台
+     * @return 解码后的字符串
+     */
+    public static String str(byte[] data, Charset charset)
+    {
+        if (data == null)
+        {
+            return null;
+        }
+
+        if (null == charset)
+        {
+            return new String(data);
+        }
+        return new String(data, charset);
+    }
+
+    /**
+     * 将编码的byteBuffer数据转换为字符串
+     * 
+     * @param data 数据
+     * @param charset 字符集,如果为空使用当前系统字符集
+     * @return 字符串
+     */
+    public static String str(ByteBuffer data, String charset)
+    {
+        if (data == null)
+        {
+            return null;
+        }
+
+        return str(data, Charset.forName(charset));
+    }
+
+    /**
+     * 将编码的byteBuffer数据转换为字符串
+     * 
+     * @param data 数据
+     * @param charset 字符集,如果为空使用当前系统字符集
+     * @return 字符串
+     */
+    public static String str(ByteBuffer data, Charset charset)
+    {
+        if (null == charset)
+        {
+            charset = Charset.defaultCharset();
+        }
+        return charset.decode(data).toString();
+    }
+
+    // ----------------------------------------------------------------------- 全角半角转换
+    /**
+     * 半角转全角
+     * 
+     * @param input String.
+     * @return 全角字符串.
+     */
+    public static String toSBC(String input)
+    {
+        return toSBC(input, null);
+    }
+
+    /**
+     * 半角转全角
+     * 
+     * @param input String
+     * @param notConvertSet 不替换的字符集合
+     * @return 全角字符串.
+     */
+    public static String toSBC(String input, Set<Character> notConvertSet)
+    {
+        char c[] = input.toCharArray();
+        for (int i = 0; i < c.length; i++)
+        {
+            if (null != notConvertSet && notConvertSet.contains(c[i]))
+            {
+                // 跳过不替换的字符
+                continue;
+            }
+
+            if (c[i] == ' ')
+            {
+                c[i] = '\u3000';
+            }
+            else if (c[i] < '\177')
+            {
+                c[i] = (char) (c[i] + 65248);
+
+            }
+        }
+        return new String(c);
+    }
+
+    /**
+     * 全角转半角
+     * 
+     * @param input String.
+     * @return 半角字符串
+     */
+    public static String toDBC(String input)
+    {
+        return toDBC(input, null);
+    }
+
+    /**
+     * 替换全角为半角
+     * 
+     * @param text 文本
+     * @param notConvertSet 不替换的字符集合
+     * @return 替换后的字符
+     */
+    public static String toDBC(String text, Set<Character> notConvertSet)
+    {
+        char c[] = text.toCharArray();
+        for (int i = 0; i < c.length; i++)
+        {
+            if (null != notConvertSet && notConvertSet.contains(c[i]))
+            {
+                // 跳过不替换的字符
+                continue;
+            }
+
+            if (c[i] == '\u3000')
+            {
+                c[i] = ' ';
+            }
+            else if (c[i] > '\uFF00' && c[i] < '\uFF5F')
+            {
+                c[i] = (char) (c[i] - 65248);
+            }
+        }
+        String returnString = new String(c);
+
+        return returnString;
+    }
+
+    /**
+     * 数字金额大写转换 先写个完整的然后将如零拾替换成零
+     * 
+     * @param n 数字
+     * @return 中文大写数字
+     */
+    public static String digitUppercase(double n)
+    {
+        String[] fraction = { "角", "分" };
+        String[] digit = { "零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖" };
+        String[][] unit = { { "元", "万", "亿" }, { "", "拾", "佰", "仟" } };
+
+        String head = n < 0 ? "负" : "";
+        n = Math.abs(n);
+
+        String s = "";
+        for (int i = 0; i < fraction.length; i++)
+        {
+            s += (digit[(int) (Math.floor(n * 10 * Math.pow(10, i)) % 10)] + fraction[i]).replaceAll("(零.)+", "");
+        }
+        if (s.length() < 1)
+        {
+            s = "整";
+        }
+        int integerPart = (int) Math.floor(n);
+
+        for (int i = 0; i < unit[0].length && integerPart > 0; i++)
+        {
+            String p = "";
+            for (int j = 0; j < unit[1].length && n > 0; j++)
+            {
+                p = digit[integerPart % 10] + unit[1][j] + p;
+                integerPart = integerPart / 10;
+            }
+            s = p.replaceAll("(零.)*零$", "").replaceAll("^$", "零") + unit[0][i] + s;
+        }
+        return head + s.replaceAll("(零.)*零元", "元").replaceFirst("(零.)+", "").replaceAll("(零.)+", "零").replaceAll("^整$",
+                "零元整");
+    }
+
+}

+ 95 - 0
src/main/java/com/ruoyi/common/support/StrFormatter.java

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

+ 96 - 0
src/main/java/com/ruoyi/common/utils/AddressUtils.java

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

+ 100 - 0
src/main/java/com/ruoyi/common/utils/DateUtils.java

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

+ 155 - 0
src/main/java/com/ruoyi/common/utils/FileUploadUtils.java

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

+ 43 - 0
src/main/java/com/ruoyi/common/utils/IpUtils.java

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

+ 136 - 0
src/main/java/com/ruoyi/common/utils/LogUtils.java

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

+ 51 - 0
src/main/java/com/ruoyi/common/utils/MapDataUtil.java

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

+ 27 - 0
src/main/java/com/ruoyi/common/utils/MessageUtils.java

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

+ 89 - 0
src/main/java/com/ruoyi/common/utils/ServletUtils.java

@@ -0,0 +1,89 @@
+package com.ruoyi.common.utils;
+
+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;
+    }
+
+    /**
+     * 是否ajax
+     */
+    public boolean isAjax()
+    {
+        String header = getRequest().getHeader("X-Requested-With");
+        boolean isAjax = "XMLHttpRequest".equalsIgnoreCase(header);
+        return isAjax;
+    }
+}

+ 373 - 0
src/main/java/com/ruoyi/common/utils/StringUtils.java

@@ -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是否为空, 包含List,Set,Queue
+     * 
+     * @param coll 要判断的Collection
+     * @return true:为空 false:非空
+     */
+    public static boolean isEmpty(Collection<?> coll)
+    {
+        return isNull(coll) || coll.isEmpty();
+    }
+
+    /**
+     * * 判断一个Collection是否非空,包含List,Set,Queue
+     * 
+     * @param coll 要判断的Collection
+     * @return true:非空 false:空
+     */
+    public static boolean isNotEmpty(Collection<?> coll)
+    {
+        return !isEmpty(coll);
+    }
+
+    /**
+     * * 判断一个对象数组是否为空
+     * 
+     * @param objects 要判断的对象数组
+     ** @return true:为空 false:非空
+     */
+    public static boolean isEmpty(Object[] objects)
+    {
+        return isNull(objects) || (objects.length == 0);
+    }
+
+    /**
+     * * 判断一个对象数组是否非空
+     * 
+     * @param objects 要判断的对象数组
+     * @return true:非空 false:空
+     */
+    public static boolean isNotEmpty(Object[] objects)
+    {
+        return !isEmpty(objects);
+    }
+
+    /**
+     * * 判断一个Map是否为空
+     * 
+     * @param map 要判断的Map
+     * @return true:为空 false:非空
+     */
+    public static boolean isEmpty(Map<?, ?> map)
+    {
+        return isNull(map) || map.isEmpty();
+    }
+
+    /**
+     * * 判断一个Map是否为空
+     * 
+     * @param map 要判断的Map
+     * @return true:非空 false:空
+     */
+    public static boolean isNotEmpty(Map<?, ?> map)
+    {
+        return !isEmpty(map);
+    }
+
+    /**
+     * * 判断一个字符串是否为空串
+     * 
+     * @param str String
+     * @return true:为空 false:非空
+     */
+    public static boolean isEmpty(String str)
+    {
+        return isNull(str) || NULLSTR.equals(str.trim());
+    }
+
+    /**
+     * * 判断一个字符串是否为非空串
+     * 
+     * @param str String
+     * @return true:非空串 false:空串
+     */
+    public static boolean isNotEmpty(String str)
+    {
+        return !isEmpty(str);
+    }
+
+    /**
+     * * 判断一个对象是否为空
+     * 
+     * @param object Object
+     * @return true:为空 false:非空
+     */
+    public static boolean isNull(Object object)
+    {
+        return object == null;
+    }
+
+    /**
+     * * 判断一个对象是否非空
+     * 
+     * @param object Object
+     * @return true:非空 false:空
+     */
+    public static boolean isNotNull(Object object)
+    {
+        return !isNull(object);
+    }
+
+    /**
+     * * 判断一个对象是否是数组类型(Java基本型别的数组)
+     * 
+     * @param object 对象
+     * @return true:是数组 false:不是数组
+     */
+    public static boolean isArray(Object object)
+    {
+        return isNotNull(object) && object.getClass().isArray();
+    }
+
+    /**
+     * 去空格
+     */
+    public static String trim(String str)
+    {
+        return (str == null ? "" : str.trim());
+    }
+
+    /**
+     * 截取字符串
+     * 
+     * @param str 字符串
+     * @param 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();
+    }
+}

+ 71 - 0
src/main/java/com/ruoyi/common/utils/SystemLogUtils.java

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

+ 145 - 0
src/main/java/com/ruoyi/common/utils/TreeUtils.java

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

+ 409 - 0
src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java

@@ -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/";
+    }
+
+}

+ 76 - 0
src/main/java/com/ruoyi/common/utils/security/ShiroUtils.java

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

+ 102 - 0
src/main/java/com/ruoyi/common/utils/spring/SpringUtils.java

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

+ 86 - 0
src/main/java/com/ruoyi/common/xss/XssFilter.java

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

+ 41 - 0
src/main/java/com/ruoyi/common/xss/XssHttpServletRequestWrapper.java

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

+ 178 - 0
src/main/java/com/ruoyi/framework/aspectj/LogAspect.java

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

+ 41 - 0
src/main/java/com/ruoyi/framework/aspectj/lang/annotation/Excel.java

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

+ 33 - 0
src/main/java/com/ruoyi/framework/aspectj/lang/annotation/Log.java

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

+ 19 - 0
src/main/java/com/ruoyi/framework/aspectj/lang/constant/BusinessStatus.java

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

+ 33 - 0
src/main/java/com/ruoyi/framework/aspectj/lang/constant/BusinessType.java

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

+ 22 - 0
src/main/java/com/ruoyi/framework/aspectj/lang/constant/OperatorType.java

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

+ 61 - 0
src/main/java/com/ruoyi/framework/config/CaptchaConfig.java

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

+ 161 - 0
src/main/java/com/ruoyi/framework/config/DruidConfig.java

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

+ 36 - 0
src/main/java/com/ruoyi/framework/config/FilterConfig.java

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

+ 71 - 0
src/main/java/com/ruoyi/framework/config/GenConfig.java

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

+ 44 - 0
src/main/java/com/ruoyi/framework/config/I18nConfig.java

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

+ 77 - 0
src/main/java/com/ruoyi/framework/config/KaptchaTextCreator.java

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

+ 42 - 0
src/main/java/com/ruoyi/framework/config/ResourcesConfig.java

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

+ 64 - 0
src/main/java/com/ruoyi/framework/config/RuoYiConfig.java

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

+ 56 - 0
src/main/java/com/ruoyi/framework/config/ScheduleConfig.java

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

+ 360 - 0
src/main/java/com/ruoyi/framework/config/ShiroConfig.java

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

+ 58 - 0
src/main/java/com/ruoyi/framework/config/SwaggerConfig.java

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

+ 126 - 0
src/main/java/com/ruoyi/framework/shiro/realm/UserRealm.java

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

+ 128 - 0
src/main/java/com/ruoyi/framework/shiro/service/LoginService.java

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

+ 101 - 0
src/main/java/com/ruoyi/framework/shiro/service/PasswordService.java

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

+ 31 - 0
src/main/java/com/ruoyi/framework/shiro/service/PermissionService.java

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

+ 115 - 0
src/main/java/com/ruoyi/framework/shiro/session/OnlineSessionDAO.java

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

+ 56 - 0
src/main/java/com/ruoyi/framework/shiro/session/OnlineSessionFactory.java

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

+ 86 - 0
src/main/java/com/ruoyi/framework/shiro/web/filter/LogoutFilter.java

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

+ 78 - 0
src/main/java/com/ruoyi/framework/shiro/web/filter/captcha/CaptchaValidateFilter.java

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

+ 97 - 0
src/main/java/com/ruoyi/framework/shiro/web/filter/online/OnlineSessionFilter.java

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

+ 42 - 0
src/main/java/com/ruoyi/framework/shiro/web/filter/sync/SyncOnlineSessionFilter.java

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

+ 155 - 0
src/main/java/com/ruoyi/framework/shiro/web/session/OnlineWebSessionManager.java

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

+ 141 - 0
src/main/java/com/ruoyi/framework/shiro/web/session/SpringSessionValidationScheduler.java

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

+ 131 - 0
src/main/java/com/ruoyi/framework/web/controller/BaseController.java

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

+ 94 - 0
src/main/java/com/ruoyi/framework/web/domain/AjaxResult.java

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

+ 98 - 0
src/main/java/com/ruoyi/framework/web/domain/BaseEntity.java

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

+ 72 - 0
src/main/java/com/ruoyi/framework/web/exception/DefaultExceptionHandler.java

@@ -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("演示模式,不允许操作");
+    }
+
+}

+ 70 - 0
src/main/java/com/ruoyi/framework/web/page/PageDomain.java

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

+ 58 - 0
src/main/java/com/ruoyi/framework/web/page/TableDataInfo.java

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

+ 31 - 0
src/main/java/com/ruoyi/framework/web/page/TableSupport.java

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

+ 29 - 0
src/main/java/com/ruoyi/framework/web/service/ConfigService.java

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

+ 30 - 0
src/main/java/com/ruoyi/framework/web/service/DictService.java

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

+ 84 - 0
src/main/java/com/ruoyi/project/common/CommonController.java

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

+ 26 - 0
src/main/java/com/ruoyi/project/monitor/druid/DruidController.java

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

+ 143 - 0
src/main/java/com/ruoyi/project/monitor/job/controller/JobController.java

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

+ 85 - 0
src/main/java/com/ruoyi/project/monitor/job/controller/JobLogController.java

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

+ 122 - 0
src/main/java/com/ruoyi/project/monitor/job/domain/Job.java

@@ -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 + "]";
+    }
+
+}

+ 135 - 0
src/main/java/com/ruoyi/project/monitor/job/domain/JobLog.java

@@ -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 + "]";
+    }
+
+}

+ 54 - 0
src/main/java/com/ruoyi/project/monitor/job/mapper/JobLogMapper.java

@@ -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(Long[] ids);
+
+    /**
+     * 删除任务日志
+     * 
+     * @param jobId 调度日志ID
+     * @return 结果
+     */
+    public int deleteJobLogById(Long jobId);
+
+}

+ 69 - 0
src/main/java/com/ruoyi/project/monitor/job/mapper/JobMapper.java

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

+ 53 - 0
src/main/java/com/ruoyi/project/monitor/job/service/IJobLogService.java

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

+ 101 - 0
src/main/java/com/ruoyi/project/monitor/job/service/IJobService.java

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

+ 80 - 0
src/main/java/com/ruoyi/project/monitor/job/service/JobLogServiceImpl.java

@@ -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.toLongArray(ids));
+    }
+
+    /**
+     * 删除任务日志
+     * 
+     * @param jobId 调度日志ID
+     */
+    @Override
+    public int deleteJobLogById(Long jobId)
+    {
+        return jobLogMapper.deleteJobLogById(jobId);
+    }
+
+}

+ 241 - 0
src/main/java/com/ruoyi/project/monitor/job/service/JobServiceImpl.java

@@ -0,0 +1,241 @@
+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 triggerJob(Job job)
+    {
+        int rows = jobMapper.updateJob(job);
+        if (rows > 0)
+        {
+            ScheduleUtils.run(scheduler, job);
+        }
+        return rows;
+    }
+
+    /**
+     * 新增任务
+     * 
+     * @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;
+    }
+
+}

+ 24 - 0
src/main/java/com/ruoyi/project/monitor/job/task/RyTask.java

@@ -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("执行无参方法");
+    }
+
+}

+ 77 - 0
src/main/java/com/ruoyi/project/monitor/job/util/ScheduleJob.java

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

+ 59 - 0
src/main/java/com/ruoyi/project/monitor/job/util/ScheduleRunnable.java

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

+ 193 - 0
src/main/java/com/ruoyi/project/monitor/job/util/ScheduleUtils.java

@@ -0,0 +1,193 @@
+package com.ruoyi.project.monitor.job.util;
+
+import org.quartz.CronScheduleBuilder;
+import org.quartz.CronTrigger;
+import org.quartz.JobBuilder;
+import org.quartz.JobDataMap;
+import org.quartz.JobDetail;
+import org.quartz.JobKey;
+import org.quartz.Scheduler;
+import org.quartz.SchedulerException;
+import org.quartz.TriggerBuilder;
+import org.quartz.TriggerKey;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import com.ruoyi.common.constant.ScheduleConstants;
+import com.ruoyi.project.monitor.job.domain.Job;
+
+/**
+ * 定时任务工具类
+ * 
+ * @author ruoyi
+ *
+ */
+public class ScheduleUtils
+{
+    private static final Logger log = LoggerFactory.getLogger(ScheduleUtils.class);
+    
+    private final static String JOB_NAME = "TASK_";
+
+    /**
+     * 获取触发器key
+     */
+    public static TriggerKey getTriggerKey(Long jobId)
+    {
+        return TriggerKey.triggerKey(JOB_NAME + jobId);
+    }
+
+    /**
+     * 获取jobKey
+     */
+    public static JobKey getJobKey(Long jobId)
+    {
+        return JobKey.jobKey(JOB_NAME + jobId);
+    }
+
+    /**
+     * 获取表达式触发器
+     */
+    public static CronTrigger getCronTrigger(Scheduler scheduler, Long jobId)
+    {
+        try
+        {
+            return (CronTrigger) scheduler.getTrigger(getTriggerKey(jobId));
+        }
+        catch (SchedulerException e)
+        {
+            log.error(e.getMessage());
+        }
+        return null;
+    }
+
+    /**
+     * 创建定时任务
+     */
+    public static void createScheduleJob(Scheduler scheduler, Job job)
+    {
+        try
+        {
+            // 构建job信息
+            JobDetail jobDetail = JobBuilder.newJob(ScheduleJob.class).withIdentity(getJobKey(job.getJobId())).build();
+
+            // 表达式调度构建器
+            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression());
+
+            // 按新的cronExpression表达式构建一个新的trigger
+            CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(getTriggerKey(job.getJobId())).withSchedule(scheduleBuilder).build();
+
+            // 放入参数,运行时的方法可以获取
+            jobDetail.getJobDataMap().put(ScheduleConstants.JOB_PARAM_KEY, job);
+
+            scheduler.scheduleJob(jobDetail, trigger);
+
+            // 暂停任务
+            if (job.getStatus().equals(ScheduleConstants.Status.PAUSE.getValue()))
+            {
+                pauseJob(scheduler, job.getJobId());
+            }
+        }
+        catch (SchedulerException e)
+        {
+            log.error(e.getMessage());
+        }
+    }
+
+    /**
+     * 更新定时任务
+     */
+    public static void updateScheduleJob(Scheduler scheduler, Job job)
+    {
+        try
+        {
+            TriggerKey triggerKey = getTriggerKey(job.getJobId());
+
+            // 表达式调度构建器
+            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression());
+
+            CronTrigger trigger = getCronTrigger(scheduler, job.getJobId());
+
+            // 按新的cronExpression表达式重新构建trigger
+            trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
+
+            // 参数
+            trigger.getJobDataMap().put(ScheduleConstants.JOB_PARAM_KEY, job);
+
+            scheduler.rescheduleJob(triggerKey, trigger);
+
+            // 暂停任务
+            if (job.getStatus().equals(ScheduleConstants.Status.PAUSE.getValue()))
+            {
+                pauseJob(scheduler, job.getJobId());
+            }
+
+        }
+        catch (SchedulerException e)
+        {
+            log.error(e.getMessage());
+        }
+    }
+
+    /**
+     * 立即执行任务
+     */
+    public static void run(Scheduler scheduler, Job job)
+    {
+        try
+        {
+            // 参数
+            JobDataMap dataMap = new JobDataMap();
+            dataMap.put(ScheduleConstants.JOB_PARAM_KEY, job);
+
+            scheduler.triggerJob(getJobKey(job.getJobId()), dataMap);
+        }
+        catch (SchedulerException e)
+        {
+            log.error(e.getMessage());
+        }
+    }
+
+    /**
+     * 暂停任务
+     */
+    public static void pauseJob(Scheduler scheduler, Long jobId)
+    {
+        try
+        {
+            scheduler.pauseJob(getJobKey(jobId));
+        }
+        catch (SchedulerException e)
+        {
+            log.error(e.getMessage());
+        }
+    }
+
+    /**
+     * 恢复任务
+     */
+    public static void resumeJob(Scheduler scheduler, Long jobId)
+    {
+        try
+        {
+            scheduler.resumeJob(getJobKey(jobId));
+        }
+        catch (SchedulerException e)
+        {
+            log.error(e.getMessage());
+        }
+    }
+
+    /**
+     * 删除定时任务
+     */
+    public static void deleteScheduleJob(Scheduler scheduler, Long jobId)
+    {
+        try
+        {
+            scheduler.deleteJob(getJobKey(jobId));
+        }
+        catch (SchedulerException e)
+        {
+            log.error(e.getMessage());
+        }
+    }
+}

Някои файлове не бяха показани, защото твърде много файлове са промени