Rgx 1 ano atrás
commit
6108546474
100 arquivos alterados com 8188 adições e 0 exclusões
  1. 25 0
      .gitignore
  2. 7 0
      Dockerfile
  3. 191 0
      LICENSE
  4. 116 0
      README.md
  5. 356 0
      db/mysql.sql
  6. 346 0
      db/oracle.sql
  7. 364 0
      db/postgresql.sql
  8. 512 0
      db/sqlserver.sql
  9. 8 0
      docker-compose.yml
  10. 337 0
      pom.xml
  11. 22 0
      src/main/java/io/renren/RenrenApplication.java
  12. 28 0
      src/main/java/io/renren/common/annotation/SysLog.java
  13. 46 0
      src/main/java/io/renren/common/aspect/RedisAspect.java
  14. 101 0
      src/main/java/io/renren/common/aspect/SysLogAspect.java
  15. 61 0
      src/main/java/io/renren/common/exception/RRException.java
  16. 64 0
      src/main/java/io/renren/common/exception/RRExceptionHandler.java
  17. 21 0
      src/main/java/io/renren/common/utils/ConfigConstant.java
  18. 152 0
      src/main/java/io/renren/common/utils/Constant.java
  19. 166 0
      src/main/java/io/renren/common/utils/DateUtils.java
  20. 138 0
      src/main/java/io/renren/common/utils/GsonUtil.java
  21. 32 0
      src/main/java/io/renren/common/utils/HttpContextUtils.java
  22. 64 0
      src/main/java/io/renren/common/utils/IPUtils.java
  23. 104 0
      src/main/java/io/renren/common/utils/MapTypeAdapter.java
  24. 26 0
      src/main/java/io/renren/common/utils/MapUtils.java
  25. 110 0
      src/main/java/io/renren/common/utils/PageUtils.java
  26. 77 0
      src/main/java/io/renren/common/utils/Query.java
  27. 64 0
      src/main/java/io/renren/common/utils/R.java
  28. 21 0
      src/main/java/io/renren/common/utils/RedisKeys.java
  29. 99 0
      src/main/java/io/renren/common/utils/RedisUtils.java
  30. 61 0
      src/main/java/io/renren/common/utils/ShiroUtils.java
  31. 51 0
      src/main/java/io/renren/common/utils/SpringContextUtils.java
  32. 32 0
      src/main/java/io/renren/common/validator/Assert.java
  33. 54 0
      src/main/java/io/renren/common/validator/ValidatorUtils.java
  34. 17 0
      src/main/java/io/renren/common/validator/group/AddGroup.java
  35. 17 0
      src/main/java/io/renren/common/validator/group/AliyunGroup.java
  36. 21 0
      src/main/java/io/renren/common/validator/group/Group.java
  37. 17 0
      src/main/java/io/renren/common/validator/group/QcloudGroup.java
  38. 17 0
      src/main/java/io/renren/common/validator/group/QiniuGroup.java
  39. 19 0
      src/main/java/io/renren/common/validator/group/UpdateGroup.java
  40. 530 0
      src/main/java/io/renren/common/xss/HTMLFilter.java
  41. 50 0
      src/main/java/io/renren/common/xss/SQLFilter.java
  42. 37 0
      src/main/java/io/renren/common/xss/XssFilter.java
  43. 147 0
      src/main/java/io/renren/common/xss/XssHttpServletRequestWrapper.java
  44. 26 0
      src/main/java/io/renren/config/CorsConfig.java
  45. 49 0
      src/main/java/io/renren/config/FilterConfig.java
  46. 39 0
      src/main/java/io/renren/config/KaptchaConfig.java
  47. 31 0
      src/main/java/io/renren/config/MybatisPlusConfig.java
  48. 63 0
      src/main/java/io/renren/config/RedisConfig.java
  49. 81 0
      src/main/java/io/renren/config/ShiroConfig.java
  50. 61 0
      src/main/java/io/renren/config/SwaggerConfig.java
  51. 24 0
      src/main/java/io/renren/datasource/annotation/DataSource.java
  52. 71 0
      src/main/java/io/renren/datasource/aspect/DataSourceAspect.java
  53. 57 0
      src/main/java/io/renren/datasource/config/DynamicContextHolder.java
  54. 25 0
      src/main/java/io/renren/datasource/config/DynamicDataSource.java
  55. 63 0
      src/main/java/io/renren/datasource/config/DynamicDataSourceConfig.java
  56. 54 0
      src/main/java/io/renren/datasource/config/DynamicDataSourceFactory.java
  57. 202 0
      src/main/java/io/renren/datasource/properties/DataSourceProperties.java
  58. 33 0
      src/main/java/io/renren/datasource/properties/DynamicDataSourceProperties.java
  59. 22 0
      src/main/java/io/renren/modules/app/annotation/Login.java
  60. 25 0
      src/main/java/io/renren/modules/app/annotation/LoginUser.java
  61. 42 0
      src/main/java/io/renren/modules/app/config/WebMvcConfig.java
  62. 64 0
      src/main/java/io/renren/modules/app/controller/AppLoginController.java
  63. 55 0
      src/main/java/io/renren/modules/app/controller/AppRegisterController.java
  64. 53 0
      src/main/java/io/renren/modules/app/controller/AppTestController.java
  65. 23 0
      src/main/java/io/renren/modules/app/dao/UserDao.java
  66. 59 0
      src/main/java/io/renren/modules/app/entity/UserEntity.java
  67. 33 0
      src/main/java/io/renren/modules/app/form/LoginForm.java
  68. 33 0
      src/main/java/io/renren/modules/app/form/RegisterForm.java
  69. 72 0
      src/main/java/io/renren/modules/app/interceptor/AuthorizationInterceptor.java
  70. 53 0
      src/main/java/io/renren/modules/app/resolver/LoginUserHandlerMethodArgumentResolver.java
  71. 31 0
      src/main/java/io/renren/modules/app/service/UserService.java
  72. 44 0
      src/main/java/io/renren/modules/app/service/impl/UserServiceImpl.java
  73. 95 0
      src/main/java/io/renren/modules/app/utils/JwtUtils.java
  74. 66 0
      src/main/java/io/renren/modules/job/config/ScheduleConfig.java
  75. 132 0
      src/main/java/io/renren/modules/job/controller/ScheduleJobController.java
  76. 55 0
      src/main/java/io/renren/modules/job/controller/ScheduleJobLogController.java
  77. 29 0
      src/main/java/io/renren/modules/job/dao/ScheduleJobDao.java
  78. 23 0
      src/main/java/io/renren/modules/job/dao/ScheduleJobLogDao.java
  79. 74 0
      src/main/java/io/renren/modules/job/entity/ScheduleJobEntity.java
  80. 71 0
      src/main/java/io/renren/modules/job/entity/ScheduleJobLogEntity.java
  81. 26 0
      src/main/java/io/renren/modules/job/service/ScheduleJobLogService.java
  82. 60 0
      src/main/java/io/renren/modules/job/service/ScheduleJobService.java
  83. 39 0
      src/main/java/io/renren/modules/job/service/impl/ScheduleJobLogServiceImpl.java
  84. 131 0
      src/main/java/io/renren/modules/job/service/impl/ScheduleJobServiceImpl.java
  85. 24 0
      src/main/java/io/renren/modules/job/task/ITask.java
  86. 30 0
      src/main/java/io/renren/modules/job/task/TestTask.java
  87. 81 0
      src/main/java/io/renren/modules/job/utils/ScheduleJob.java
  88. 156 0
      src/main/java/io/renren/modules/job/utils/ScheduleUtils.java
  89. 149 0
      src/main/java/io/renren/modules/kubesphere/config/KubeSphereConfig.java
  90. 53 0
      src/main/java/io/renren/modules/kubesphere/interceptor/KubeSphereTokenManger.java
  91. 51 0
      src/main/java/io/renren/modules/kubesphere/service/OauthTokenService.java
  92. 62 0
      src/main/java/io/renren/modules/oss/cloud/AliyunCloudStorageService.java
  93. 94 0
      src/main/java/io/renren/modules/oss/cloud/CloudStorageConfig.java
  94. 78 0
      src/main/java/io/renren/modules/oss/cloud/CloudStorageService.java
  95. 44 0
      src/main/java/io/renren/modules/oss/cloud/OSSFactory.java
  96. 88 0
      src/main/java/io/renren/modules/oss/cloud/QcloudCloudStorageService.java
  97. 77 0
      src/main/java/io/renren/modules/oss/cloud/QiniuCloudStorageService.java
  98. 125 0
      src/main/java/io/renren/modules/oss/controller/SysOssController.java
  99. 23 0
      src/main/java/io/renren/modules/oss/dao/SysOssDao.java
  100. 36 0
      src/main/java/io/renren/modules/oss/entity/SysOssEntity.java

+ 25 - 0
.gitignore

@@ -0,0 +1,25 @@
+# Compiled class file
+*.class
+target
+
+# Log file
+*.log
+
+# BlueJ files
+*.ctxt
+
+# Mobile Tools for Java (J2ME)
+.mtj.tmp/
+
+# Package Files #
+*.jar
+*.war
+*.ear
+*.zip
+*.tar.gz
+*.rar
+*.iml
+.idea
+
+# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
+hs_err_pid*

+ 7 - 0
Dockerfile

@@ -0,0 +1,7 @@
+FROM java:8
+EXPOSE 8080
+
+VOLUME /tmp
+ADD renren-fast.jar  /app.jar
+RUN bash -c 'touch /app.jar'
+ENTRYPOINT ["java","-jar","/app.jar"]

+ 191 - 0
LICENSE

@@ -0,0 +1,191 @@
+Apache License
+Version 2.0, January 2004
+http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+"License" shall mean the terms and conditions for use, reproduction, and
+distribution as defined by Sections 1 through 9 of this document.
+
+"Licensor" shall mean the copyright owner or entity authorized by the copyright
+owner that is granting the License.
+
+"Legal Entity" shall mean the union of the acting entity and all other entities
+that control, are controlled by, or are under common control with that entity.
+For the purposes of this definition, "control" means (i) the power, direct or
+indirect, to cause the direction or management of such entity, whether by
+contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
+outstanding shares, or (iii) beneficial ownership of such entity.
+
+"You" (or "Your") shall mean an individual or Legal Entity exercising
+permissions granted by this License.
+
+"Source" form shall mean the preferred form for making modifications, including
+but not limited to software source code, documentation source, and configuration
+files.
+
+"Object" form shall mean any form resulting from mechanical transformation or
+translation of a Source form, including but not limited to compiled object code,
+generated documentation, and conversions to other media types.
+
+"Work" shall mean the work of authorship, whether in Source or Object form, made
+available under the License, as indicated by a copyright notice that is included
+in or attached to the work (an example is provided in the Appendix below).
+
+"Derivative Works" shall mean any work, whether in Source or Object form, that
+is based on (or derived from) the Work and for which the editorial revisions,
+annotations, elaborations, or other modifications represent, as a whole, an
+original work of authorship. For the purposes of this License, Derivative Works
+shall not include works that remain separable from, or merely link (or bind by
+name) to the interfaces of, the Work and Derivative Works thereof.
+
+"Contribution" shall mean any work of authorship, including the original version
+of the Work and any modifications or additions to that Work or Derivative Works
+thereof, that is intentionally submitted to Licensor for inclusion in the Work
+by the copyright owner or by an individual or Legal Entity authorized to submit
+on behalf of the copyright owner. For the purposes of this definition,
+"submitted" means any form of electronic, verbal, or written communication sent
+to the Licensor or its representatives, including but not limited to
+communication on electronic mailing lists, source code control systems, and
+issue tracking systems that are managed by, or on behalf of, the Licensor for
+the purpose of discussing and improving the Work, but excluding communication
+that is conspicuously marked or otherwise designated in writing by the copyright
+owner as "Not a Contribution."
+
+"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
+of whom a Contribution has been received by Licensor and subsequently
+incorporated within the Work.
+
+2. Grant of Copyright License.
+
+Subject to the terms and conditions of this License, each Contributor hereby
+grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
+irrevocable copyright license to reproduce, prepare Derivative Works of,
+publicly display, publicly perform, sublicense, and distribute the Work and such
+Derivative Works in Source or Object form.
+
+3. Grant of Patent License.
+
+Subject to the terms and conditions of this License, each Contributor hereby
+grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
+irrevocable (except as stated in this section) patent license to make, have
+made, use, offer to sell, sell, import, and otherwise transfer the Work, where
+such license applies only to those patent claims licensable by such Contributor
+that are necessarily infringed by their Contribution(s) alone or by combination
+of their Contribution(s) with the Work to which such Contribution(s) was
+submitted. If You institute patent litigation against any entity (including a
+cross-claim or counterclaim in a lawsuit) alleging that the Work or a
+Contribution incorporated within the Work constitutes direct or contributory
+patent infringement, then any patent licenses granted to You under this License
+for that Work shall terminate as of the date such litigation is filed.
+
+4. Redistribution.
+
+You may reproduce and distribute copies of the Work or Derivative Works thereof
+in any medium, with or without modifications, and in Source or Object form,
+provided that You meet the following conditions:
+
+You must give any other recipients of the Work or Derivative Works a copy of
+this License; and
+You must cause any modified files to carry prominent notices stating that You
+changed the files; and
+You must retain, in the Source form of any Derivative Works that You distribute,
+all copyright, patent, trademark, and attribution notices from the Source form
+of the Work, excluding those notices that do not pertain to any part of the
+Derivative Works; and
+If the Work includes a "NOTICE" text file as part of its distribution, then any
+Derivative Works that You distribute must include a readable copy of the
+attribution notices contained within such NOTICE file, excluding those notices
+that do not pertain to any part of the Derivative Works, in at least one of the
+following places: within a NOTICE text file distributed as part of the
+Derivative Works; within the Source form or documentation, if provided along
+with the Derivative Works; or, within a display generated by the Derivative
+Works, if and wherever such third-party notices normally appear. The contents of
+the NOTICE file are for informational purposes only and do not modify the
+License. You may add Your own attribution notices within Derivative Works that
+You distribute, alongside or as an addendum to the NOTICE text from the Work,
+provided that such additional attribution notices cannot be construed as
+modifying the License.
+You may add Your own copyright statement to Your modifications and may provide
+additional or different license terms and conditions for use, reproduction, or
+distribution of Your modifications, or for any such Derivative Works as a whole,
+provided Your use, reproduction, and distribution of the Work otherwise complies
+with the conditions stated in this License.
+
+5. Submission of Contributions.
+
+Unless You explicitly state otherwise, any Contribution intentionally submitted
+for inclusion in the Work by You to the Licensor shall be under the terms and
+conditions of this License, without any additional terms or conditions.
+Notwithstanding the above, nothing herein shall supersede or modify the terms of
+any separate license agreement you may have executed with Licensor regarding
+such Contributions.
+
+6. Trademarks.
+
+This License does not grant permission to use the trade names, trademarks,
+service marks, or product names of the Licensor, except as required for
+reasonable and customary use in describing the origin of the Work and
+reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty.
+
+Unless required by applicable law or agreed to in writing, Licensor provides the
+Work (and each Contributor provides its Contributions) on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
+including, without limitation, any warranties or conditions of TITLE,
+NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
+solely responsible for determining the appropriateness of using or
+redistributing the Work and assume any risks associated with Your exercise of
+permissions under this License.
+
+8. Limitation of Liability.
+
+In no event and under no legal theory, whether in tort (including negligence),
+contract, or otherwise, unless required by applicable law (such as deliberate
+and grossly negligent acts) or agreed to in writing, shall any Contributor be
+liable to You for damages, including any direct, indirect, special, incidental,
+or consequential damages of any character arising as a result of this License or
+out of the use or inability to use the Work (including but not limited to
+damages for loss of goodwill, work stoppage, computer failure or malfunction, or
+any and all other commercial damages or losses), even if such Contributor has
+been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability.
+
+While redistributing the Work or Derivative Works thereof, You may choose to
+offer, and charge a fee for, acceptance of support, warranty, indemnity, or
+other liability obligations and/or rights consistent with this License. However,
+in accepting such obligations, You may act only on Your own behalf and on Your
+sole responsibility, not on behalf of any other Contributor, and only if You
+agree to indemnify, defend, and hold each Contributor harmless for any liability
+incurred by, or claims asserted against, such Contributor by reason of your
+accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work
+
+To apply the Apache License to your work, attach the following boilerplate
+notice, with the fields enclosed by brackets "{}" replaced with your own
+identifying information. (Don't include the brackets!) The text should be
+enclosed in the appropriate comment syntax for the file format. We also
+recommend that a file or class name and description of purpose be included on
+the same "printed page" as the copyright notice for easier identification within
+third-party archives.
+
+   Copyright 2019 人人开源
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.

+ 116 - 0
README.md

@@ -0,0 +1,116 @@
+**项目说明** 
+- renren-fast是一个轻量级的,前后端分离的Java快速开发平台,能快速开发项目并交付【接私活利器】
+- 支持MySQL、Oracle、SQL Server、PostgreSQL等主流数据库
+- 前端地址:https://gitee.com/renrenio/renren-fast-vue
+- 代码生成器:https://gitee.com/renrenio/renren-generator
+
+<br>
+ 
+
+**具有如下特点** 
+- 友好的代码结构及注释,便于阅读及二次开发
+- 实现前后端分离,通过token进行数据交互,前端再也不用关注后端技术
+- 灵活的权限控制,可控制到页面或按钮,满足绝大部分的权限需求
+- 页面交互使用Vue2.x,极大的提高了开发效率
+- 完善的代码生成机制,可在线生成entity、xml、dao、service、vue、sql代码,减少70%以上的开发任务
+- 引入quartz定时任务,可动态完成任务的添加、修改、删除、暂停、恢复及日志查看等功能
+- 引入API模板,根据token作为登录令牌,极大的方便了APP接口开发
+- 引入Hibernate Validator校验框架,轻松实现后端校验
+- 引入云存储服务,已支持:七牛云、阿里云、腾讯云等
+- 引入swagger文档支持,方便编写API接口文档
+<br> 
+
+**项目结构** 
+```
+renren-fast
+├─db  项目SQL语句
+│
+├─common 公共模块
+│  ├─aspect 系统日志
+│  ├─exception 异常处理
+│  ├─validator 后台校验
+│  └─xss XSS过滤
+│ 
+├─config 配置信息
+│ 
+├─modules 功能模块
+│  ├─app API接口模块(APP调用)
+│  ├─job 定时任务模块
+│  ├─oss 文件服务模块
+│  └─sys 权限模块
+│ 
+├─RenrenApplication 项目启动类
+│  
+├──resources 
+│  ├─mapper SQL对应的XML文件
+│  └─static 静态资源
+
+```
+<br> 
+
+**如何交流、反馈、参与贡献?** 
+- 开发文档:https://www.renren.io/guide
+- Git仓库:https://gitee.com/renrenio/renren-fast
+- [人人开源社区](https://www.renren.io/community):https://www.renren.io/community
+- 官方QQ群:324780204、145799952
+- 技术讨论、二次开发等咨询、问题和建议,请移步到人人开源社区,我会在第一时间进行解答和回复!
+- 如需关注项目最新动态,请Watch、Star项目,同时也是对项目最好的支持
+- 微信扫码并关注【人人开源】,获得项目最新动态及更新提醒
+
+<br>
+
+![输入图片说明](https://images.gitee.com/uploads/images/2019/0307/090140_260d672d_63154.jpeg "在这里输入图片标题")
+
+<br> 
+
+
+**技术选型:** 
+- 核心框架:Spring Boot 2.1
+- 安全框架:Apache Shiro 1.4
+- 视图框架:Spring MVC 5.0
+- 持久层框架:MyBatis 3.3
+- 定时器:Quartz 2.3
+- 数据库连接池:Druid 1.0
+- 日志管理:SLF4J 1.7、Log4j
+- 页面交互:Vue2.x 
+<br> 
+
+
+ **后端部署**
+- 通过git下载源码
+- idea、eclipse需安装lombok插件,不然会提示找不到entity的get set方法
+- 创建数据库renren_fast,数据库编码为UTF-8
+- 执行db/mysql.sql文件,初始化数据
+- 修改application-dev.yml,更新MySQL账号和密码
+- Eclipse、IDEA运行RenrenApplication.java,则可启动项目
+- Swagger文档路径:http://localhost:8080/renren-fast/swagger/index.html
+- Swagger注解路径:http://localhost:8080/renren-fast/swagger-ui.html
+
+<br> 
+
+ **前端部署**
+ - 本项目是前后端分离的,还需要部署前端,才能运行起来
+ - 前端下载地址:https://gitee.com/renrenio/renren-fast-vue
+ - 前端部署文档:https://gitee.com/renrenio/renren-fast-vue/wikis/Home
+ - 前端部署完毕,就可以访问项目了,账号:admin,密码:admin
+ 
+ <br>
+
+ **项目演示**
+- 演示地址:http://demo.open.renren.io/renren-fast
+- 账号密码:admin/admin
+<br> 
+
+**接口文档效果图:**
+![输入图片说明](https://images.gitee.com/uploads/images/2018/0728/145341_73ba6f75_63154.jpeg "在这里输入图片标题")
+
+<br> <br> <br> 
+
+
+**效果图:**
+![输入图片说明](https://gitee.com/uploads/images/2018/0505/173115_d3c045ef_63154.jpeg "在这里输入图片标题")
+![输入图片说明](https://gitee.com/uploads/images/2018/0624/225728_b06f72b3_63154.jpeg "在这里输入图片标题")
+![输入图片说明](https://gitee.com/uploads/images/2018/0505/173140_79928d91_63154.jpeg "在这里输入图片标题")
+![输入图片说明](https://gitee.com/uploads/images/2018/0505/173151_12d065db_63154.jpeg "在这里输入图片标题")
+
+<br>

+ 356 - 0
db/mysql.sql

@@ -0,0 +1,356 @@
+-- 菜单
+CREATE TABLE `sys_menu` (
+  `menu_id` bigint NOT NULL AUTO_INCREMENT,
+  `parent_id` bigint COMMENT '父菜单ID,一级菜单为0',
+  `name` varchar(50) COMMENT '菜单名称',
+  `url` varchar(200) COMMENT '菜单URL',
+  `perms` varchar(500) COMMENT '授权(多个用逗号分隔,如:user:list,user:create)',
+  `type` int COMMENT '类型   0:目录   1:菜单   2:按钮',
+  `icon` varchar(50) COMMENT '菜单图标',
+  `order_num` int COMMENT '排序',
+  PRIMARY KEY (`menu_id`)
+) ENGINE=`InnoDB` DEFAULT CHARACTER SET utf8mb4 COMMENT='菜单管理';
+
+-- 系统用户
+CREATE TABLE `sys_user` (
+  `user_id` bigint NOT NULL AUTO_INCREMENT,
+  `username` varchar(50) NOT NULL COMMENT '用户名',
+  `password` varchar(100) COMMENT '密码',
+  `salt` varchar(20) COMMENT '盐',
+  `email` varchar(100) COMMENT '邮箱',
+  `mobile` varchar(100) COMMENT '手机号',
+  `status` tinyint COMMENT '状态  0:禁用   1:正常',
+  `create_user_id` bigint(20) COMMENT '创建者ID',
+  `create_time` datetime COMMENT '创建时间',
+  PRIMARY KEY (`user_id`),
+  UNIQUE INDEX (`username`)
+) ENGINE=`InnoDB` DEFAULT CHARACTER SET utf8mb4 COMMENT='系统用户';
+
+-- 系统用户Token
+CREATE TABLE `sys_user_token` (
+  `user_id` bigint(20) NOT NULL,
+  `token` varchar(100) NOT NULL COMMENT 'token',
+  `expire_time` datetime DEFAULT NULL COMMENT '过期时间',
+  `update_time` datetime DEFAULT NULL COMMENT '更新时间',
+  PRIMARY KEY (`user_id`),
+  UNIQUE KEY `token` (`token`)
+) ENGINE=`InnoDB` DEFAULT CHARACTER SET utf8mb4 COMMENT='系统用户Token';
+
+-- 系统验证码
+CREATE TABLE `sys_captcha` (
+  `uuid` char(36) NOT NULL COMMENT 'uuid',
+  `code` varchar(6) NOT NULL COMMENT '验证码',
+  `expire_time` datetime DEFAULT NULL COMMENT '过期时间',
+  PRIMARY KEY (`uuid`)
+) ENGINE=`InnoDB` DEFAULT CHARACTER SET utf8mb4 COMMENT='系统验证码';
+
+-- 角色
+CREATE TABLE `sys_role` (
+  `role_id` bigint NOT NULL AUTO_INCREMENT,
+  `role_name` varchar(100) COMMENT '角色名称',
+  `remark` varchar(100) COMMENT '备注',
+  `create_user_id` bigint(20) COMMENT '创建者ID',
+  `create_time` datetime COMMENT '创建时间',
+  PRIMARY KEY (`role_id`)
+) ENGINE=`InnoDB` DEFAULT CHARACTER SET utf8mb4 COMMENT='角色';
+
+-- 用户与角色对应关系
+CREATE TABLE `sys_user_role` (
+  `id` bigint NOT NULL AUTO_INCREMENT,
+  `user_id` bigint COMMENT '用户ID',
+  `role_id` bigint COMMENT '角色ID',
+  PRIMARY KEY (`id`)
+) ENGINE=`InnoDB` DEFAULT CHARACTER SET utf8mb4 COMMENT='用户与角色对应关系';
+
+-- 角色与菜单对应关系
+CREATE TABLE `sys_role_menu` (
+  `id` bigint NOT NULL AUTO_INCREMENT,
+  `role_id` bigint COMMENT '角色ID',
+  `menu_id` bigint COMMENT '菜单ID',
+  PRIMARY KEY (`id`)
+) ENGINE=`InnoDB` DEFAULT CHARACTER SET utf8mb4 COMMENT='角色与菜单对应关系';
+
+-- 系统配置信息
+CREATE TABLE `sys_config` (
+	`id` bigint NOT NULL AUTO_INCREMENT,
+	`param_key` varchar(50) COMMENT 'key',
+	`param_value` varchar(2000) COMMENT 'value',
+	`status` tinyint DEFAULT 1 COMMENT '状态   0:隐藏   1:显示',
+	`remark` varchar(500) COMMENT '备注',
+	PRIMARY KEY (`id`),
+	UNIQUE INDEX (`param_key`)
+) ENGINE=`InnoDB` DEFAULT CHARACTER SET utf8mb4 COMMENT='系统配置信息表';
+
+
+-- 系统日志
+CREATE TABLE `sys_log` (
+  `id` bigint(20) NOT NULL AUTO_INCREMENT,
+  `username` varchar(50) COMMENT '用户名',
+  `operation` varchar(50) COMMENT '用户操作',
+  `method` varchar(200) COMMENT '请求方法',
+  `params` varchar(5000) COMMENT '请求参数',
+  `time` bigint NOT NULL COMMENT '执行时长(毫秒)',
+  `ip` varchar(64) COMMENT 'IP地址',
+  `create_date` datetime COMMENT '创建时间',
+  PRIMARY KEY (`id`)
+) ENGINE=`InnoDB` DEFAULT CHARACTER SET utf8mb4 COMMENT='系统日志';
+
+
+-- 文件上传
+CREATE TABLE `sys_oss` (
+  `id` bigint(20) NOT NULL AUTO_INCREMENT,
+  `url` varchar(200) COMMENT 'URL地址',
+  `create_date` datetime COMMENT '创建时间',
+  PRIMARY KEY (`id`)
+) ENGINE=`InnoDB` DEFAULT CHARACTER SET utf8mb4 COMMENT='文件上传';
+
+
+-- 定时任务
+CREATE TABLE `schedule_job` (
+  `job_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '任务id',
+  `bean_name` varchar(200) DEFAULT NULL COMMENT 'spring bean名称',
+  `params` varchar(2000) DEFAULT NULL COMMENT '参数',
+  `cron_expression` varchar(100) DEFAULT NULL COMMENT 'cron表达式',
+  `status` tinyint(4) DEFAULT NULL COMMENT '任务状态  0:正常  1:暂停',
+  `remark` varchar(255) DEFAULT NULL COMMENT '备注',
+  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
+  PRIMARY KEY (`job_id`)
+) ENGINE=`InnoDB` DEFAULT CHARACTER SET utf8mb4 COMMENT='定时任务';
+
+-- 定时任务日志
+CREATE TABLE `schedule_job_log` (
+  `log_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '任务日志id',
+  `job_id` bigint(20) NOT NULL COMMENT '任务id',
+  `bean_name` varchar(200) DEFAULT NULL COMMENT 'spring bean名称',
+  `params` varchar(2000) DEFAULT NULL COMMENT '参数',
+  `status` tinyint(4) NOT NULL COMMENT '任务状态    0:成功    1:失败',
+  `error` varchar(2000) DEFAULT NULL COMMENT '失败信息',
+  `times` int(11) NOT NULL COMMENT '耗时(单位:毫秒)',
+  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
+  PRIMARY KEY (`log_id`),
+  KEY `job_id` (`job_id`)
+) ENGINE=`InnoDB` DEFAULT CHARACTER SET utf8mb4 COMMENT='定时任务日志';
+
+
+
+-- 用户表
+CREATE TABLE `tb_user` (
+  `user_id` bigint NOT NULL AUTO_INCREMENT,
+  `username` varchar(50) NOT NULL COMMENT '用户名',
+  `mobile` varchar(20) NOT NULL COMMENT '手机号',
+  `password` varchar(64) COMMENT '密码',
+  `create_time` datetime COMMENT '创建时间',
+  PRIMARY KEY (`user_id`),
+  UNIQUE INDEX (`username`)
+) ENGINE=`InnoDB` DEFAULT CHARACTER SET utf8mb4 COMMENT='用户';
+
+
+
+
+
+
+-- 初始数据 
+INSERT INTO `sys_user` (`user_id`, `username`, `password`, `salt`, `email`, `mobile`, `status`, `create_user_id`, `create_time`) VALUES ('1', 'admin', '9ec9750e709431dad22365cabc5c625482e574c74adaebba7dd02f1129e4ce1d', 'YzcmCZNvbXocrsz9dm8e', 'root@renren.io', '13612345678', '1', '1', '2016-11-11 11:11:11');
+
+INSERT INTO `sys_menu`(`menu_id`, `parent_id`, `name`, `url`, `perms`, `type`, `icon`, `order_num`) VALUES (1, 0, '系统管理', NULL, NULL, 0, 'system', 0);
+INSERT INTO `sys_menu`(`menu_id`, `parent_id`, `name`, `url`, `perms`, `type`, `icon`, `order_num`) VALUES (2, 1, '管理员列表', 'sys/user', NULL, 1, 'admin', 1);
+INSERT INTO `sys_menu`(`menu_id`, `parent_id`, `name`, `url`, `perms`, `type`, `icon`, `order_num`) VALUES (3, 1, '角色管理', 'sys/role', NULL, 1, 'role', 2);
+INSERT INTO `sys_menu`(`menu_id`, `parent_id`, `name`, `url`, `perms`, `type`, `icon`, `order_num`) VALUES (4, 1, '菜单管理', 'sys/menu', NULL, 1, 'menu', 3);
+INSERT INTO `sys_menu`(`menu_id`, `parent_id`, `name`, `url`, `perms`, `type`, `icon`, `order_num`) VALUES (5, 1, 'SQL监控', 'http://localhost:8080/renren-fast/druid/sql.html', NULL, 1, 'sql', 4);
+INSERT INTO `sys_menu`(`menu_id`, `parent_id`, `name`, `url`, `perms`, `type`, `icon`, `order_num`) VALUES (6, 1, '定时任务', 'job/schedule', NULL, 1, 'job', 5);
+INSERT INTO `sys_menu`(`menu_id`, `parent_id`, `name`, `url`, `perms`, `type`, `icon`, `order_num`) VALUES (7, 6, '查看', NULL, 'sys:schedule:list,sys:schedule:info', 2, NULL, 0);
+INSERT INTO `sys_menu`(`menu_id`, `parent_id`, `name`, `url`, `perms`, `type`, `icon`, `order_num`) VALUES (8, 6, '新增', NULL, 'sys:schedule:save', 2, NULL, 0);
+INSERT INTO `sys_menu`(`menu_id`, `parent_id`, `name`, `url`, `perms`, `type`, `icon`, `order_num`) VALUES (9, 6, '修改', NULL, 'sys:schedule:update', 2, NULL, 0);
+INSERT INTO `sys_menu`(`menu_id`, `parent_id`, `name`, `url`, `perms`, `type`, `icon`, `order_num`) VALUES (10, 6, '删除', NULL, 'sys:schedule:delete', 2, NULL, 0);
+INSERT INTO `sys_menu`(`menu_id`, `parent_id`, `name`, `url`, `perms`, `type`, `icon`, `order_num`) VALUES (11, 6, '暂停', NULL, 'sys:schedule:pause', 2, NULL, 0);
+INSERT INTO `sys_menu`(`menu_id`, `parent_id`, `name`, `url`, `perms`, `type`, `icon`, `order_num`) VALUES (12, 6, '恢复', NULL, 'sys:schedule:resume', 2, NULL, 0);
+INSERT INTO `sys_menu`(`menu_id`, `parent_id`, `name`, `url`, `perms`, `type`, `icon`, `order_num`) VALUES (13, 6, '立即执行', NULL, 'sys:schedule:run', 2, NULL, 0);
+INSERT INTO `sys_menu`(`menu_id`, `parent_id`, `name`, `url`, `perms`, `type`, `icon`, `order_num`) VALUES (14, 6, '日志列表', NULL, 'sys:schedule:log', 2, NULL, 0);
+INSERT INTO `sys_menu`(`menu_id`, `parent_id`, `name`, `url`, `perms`, `type`, `icon`, `order_num`) VALUES (15, 2, '查看', NULL, 'sys:user:list,sys:user:info', 2, NULL, 0);
+INSERT INTO `sys_menu`(`menu_id`, `parent_id`, `name`, `url`, `perms`, `type`, `icon`, `order_num`) VALUES (16, 2, '新增', NULL, 'sys:user:save,sys:role:select', 2, NULL, 0);
+INSERT INTO `sys_menu`(`menu_id`, `parent_id`, `name`, `url`, `perms`, `type`, `icon`, `order_num`) VALUES (17, 2, '修改', NULL, 'sys:user:update,sys:role:select', 2, NULL, 0);
+INSERT INTO `sys_menu`(`menu_id`, `parent_id`, `name`, `url`, `perms`, `type`, `icon`, `order_num`) VALUES (18, 2, '删除', NULL, 'sys:user:delete', 2, NULL, 0);
+INSERT INTO `sys_menu`(`menu_id`, `parent_id`, `name`, `url`, `perms`, `type`, `icon`, `order_num`) VALUES (19, 3, '查看', NULL, 'sys:role:list,sys:role:info', 2, NULL, 0);
+INSERT INTO `sys_menu`(`menu_id`, `parent_id`, `name`, `url`, `perms`, `type`, `icon`, `order_num`) VALUES (20, 3, '新增', NULL, 'sys:role:save,sys:menu:list', 2, NULL, 0);
+INSERT INTO `sys_menu`(`menu_id`, `parent_id`, `name`, `url`, `perms`, `type`, `icon`, `order_num`) VALUES (21, 3, '修改', NULL, 'sys:role:update,sys:menu:list', 2, NULL, 0);
+INSERT INTO `sys_menu`(`menu_id`, `parent_id`, `name`, `url`, `perms`, `type`, `icon`, `order_num`) VALUES (22, 3, '删除', NULL, 'sys:role:delete', 2, NULL, 0);
+INSERT INTO `sys_menu`(`menu_id`, `parent_id`, `name`, `url`, `perms`, `type`, `icon`, `order_num`) VALUES (23, 4, '查看', NULL, 'sys:menu:list,sys:menu:info', 2, NULL, 0);
+INSERT INTO `sys_menu`(`menu_id`, `parent_id`, `name`, `url`, `perms`, `type`, `icon`, `order_num`) VALUES (24, 4, '新增', NULL, 'sys:menu:save,sys:menu:select', 2, NULL, 0);
+INSERT INTO `sys_menu`(`menu_id`, `parent_id`, `name`, `url`, `perms`, `type`, `icon`, `order_num`) VALUES (25, 4, '修改', NULL, 'sys:menu:update,sys:menu:select', 2, NULL, 0);
+INSERT INTO `sys_menu`(`menu_id`, `parent_id`, `name`, `url`, `perms`, `type`, `icon`, `order_num`) VALUES (26, 4, '删除', NULL, 'sys:menu:delete', 2, NULL, 0);
+INSERT INTO `sys_menu`(`menu_id`, `parent_id`, `name`, `url`, `perms`, `type`, `icon`, `order_num`) VALUES (27, 1, '参数管理', 'sys/config', 'sys:config:list,sys:config:info,sys:config:save,sys:config:update,sys:config:delete', 1, 'config', 6);
+INSERT INTO `sys_menu`(`menu_id`, `parent_id`, `name`, `url`, `perms`, `type`, `icon`, `order_num`) VALUES (29, 1, '系统日志', 'sys/log', 'sys:log:list', 1, 'log', 7);
+INSERT INTO `sys_menu`(`menu_id`, `parent_id`, `name`, `url`, `perms`, `type`, `icon`, `order_num`) VALUES (30, 1, '文件上传', 'oss/oss', 'sys:oss:all', 1, 'oss', 6);
+
+INSERT INTO `sys_config` (`param_key`, `param_value`, `status`, `remark`) VALUES ('CLOUD_STORAGE_CONFIG_KEY', '{\"aliyunAccessKeyId\":\"\",\"aliyunAccessKeySecret\":\"\",\"aliyunBucketName\":\"\",\"aliyunDomain\":\"\",\"aliyunEndPoint\":\"\",\"aliyunPrefix\":\"\",\"qcloudBucketName\":\"\",\"qcloudDomain\":\"\",\"qcloudPrefix\":\"\",\"qcloudSecretId\":\"\",\"qcloudSecretKey\":\"\",\"qiniuAccessKey\":\"NrgMfABZxWLo5B-YYSjoE8-AZ1EISdi1Z3ubLOeZ\",\"qiniuBucketName\":\"ios-app\",\"qiniuDomain\":\"http://7xqbwh.dl1.z0.glb.clouddn.com\",\"qiniuPrefix\":\"upload\",\"qiniuSecretKey\":\"uIwJHevMRWU0VLxFvgy0tAcOdGqasdtVlJkdy6vV\",\"type\":1}', '0', '云存储配置信息');
+INSERT INTO `schedule_job` (`bean_name`, `params`, `cron_expression`, `status`, `remark`, `create_time`) VALUES ('testTask', 'renren', '0 0/30 * * * ?', '0', '参数测试', now());
+
+
+-- 账号:13612345678  密码:admin
+INSERT INTO `tb_user` (`username`, `mobile`, `password`, `create_time`) VALUES ('mark', '13612345678', '8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918', '2017-03-23 22:37:41');
+
+
+
+
+
+
+
+
+--  quartz自带表结构
+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;
+
+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;
+
+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;
+
+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(120) 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;
+
+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;
+
+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),
+INDEX (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;
+
+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;
+
+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;
+
+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;
+
+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;
+
+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;
+
+CREATE INDEX IDX_QRTZ_J_REQ_RECOVERY ON QRTZ_JOB_DETAILS(SCHED_NAME,REQUESTS_RECOVERY);
+CREATE INDEX IDX_QRTZ_J_GRP ON QRTZ_JOB_DETAILS(SCHED_NAME,JOB_GROUP);
+
+CREATE INDEX IDX_QRTZ_T_J ON QRTZ_TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP);
+CREATE INDEX IDX_QRTZ_T_JG ON QRTZ_TRIGGERS(SCHED_NAME,JOB_GROUP);
+CREATE INDEX IDX_QRTZ_T_C ON QRTZ_TRIGGERS(SCHED_NAME,CALENDAR_NAME);
+CREATE INDEX IDX_QRTZ_T_G ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_GROUP);
+CREATE INDEX IDX_QRTZ_T_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE);
+CREATE INDEX IDX_QRTZ_T_N_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP,TRIGGER_STATE);
+CREATE INDEX IDX_QRTZ_T_N_G_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_GROUP,TRIGGER_STATE);
+CREATE INDEX IDX_QRTZ_T_NEXT_FIRE_TIME ON QRTZ_TRIGGERS(SCHED_NAME,NEXT_FIRE_TIME);
+CREATE INDEX IDX_QRTZ_T_NFT_ST ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE,NEXT_FIRE_TIME);
+CREATE INDEX IDX_QRTZ_T_NFT_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME);
+CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_STATE);
+CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE_GRP ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_GROUP,TRIGGER_STATE);
+
+CREATE INDEX IDX_QRTZ_FT_TRIG_INST_NAME ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME);
+CREATE INDEX IDX_QRTZ_FT_INST_JOB_REQ_RCVRY ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME,REQUESTS_RECOVERY);
+CREATE INDEX IDX_QRTZ_FT_J_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP);
+CREATE INDEX IDX_QRTZ_FT_JG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,JOB_GROUP);
+CREATE INDEX IDX_QRTZ_FT_T_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP);
+CREATE INDEX IDX_QRTZ_FT_TG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_GROUP);

+ 346 - 0
db/oracle.sql

@@ -0,0 +1,346 @@
+-- 菜单
+CREATE TABLE sys_menu (
+  menu_id NUMBER(20, 0) NOT NULL,
+  parent_id NUMBER(20, 0) NOT NULL,
+  name varchar2(50),
+  url varchar2(200),
+  perms varchar2(500),
+  type NUMBER(2, 0),
+  icon varchar2(50),
+  order_num NUMBER(8, 0),
+  PRIMARY KEY (menu_id)
+);
+
+-- 系统用户
+CREATE TABLE sys_user (
+  user_id NUMBER(20, 0) NOT NULL,
+  username varchar2(50) NOT NULL,
+  password varchar2(100),
+  salt varchar2(20),
+  email varchar2(100),
+  mobile varchar2(100),
+  status NUMBER(2, 0) NOT NULL,
+  create_user_id NUMBER(20, 0) NOT NULL,
+  create_time timestamp,
+  PRIMARY KEY (user_id)
+);
+CREATE UNIQUE INDEX index_sys_user_username on sys_user(username);
+
+-- 系统用户Token
+CREATE TABLE sys_user_token (
+  user_id NUMBER(20, 0) NOT NULL,
+  token varchar2(100) NOT NULL,
+  expire_time timestamp,
+  update_time timestamp,
+  PRIMARY KEY (user_id)
+);
+CREATE UNIQUE INDEX index_token on sys_user_token(token);
+
+-- 系统验证码
+CREATE TABLE sys_captcha (
+  uuid varchar2(36) NOT NULL,
+  code varchar2(6) NOT NULL,
+  expire_time timestamp,
+  PRIMARY KEY (uuid)
+);
+
+-- 角色
+CREATE TABLE sys_role (
+  role_id NUMBER(20, 0) NOT NULL,
+  role_name varchar2(100),
+  remark varchar2(100),
+  create_user_id NUMBER(20, 0) NOT NULL,
+  create_time timestamp,
+  PRIMARY KEY (role_id)
+);
+
+-- 用户与角色对应关系
+CREATE TABLE sys_user_role (
+  id NUMBER(20, 0) NOT NULL,
+  user_id NUMBER(20, 0) NOT NULL,
+  role_id NUMBER(20, 0) NOT NULL,
+  PRIMARY KEY (id)
+);
+
+-- 角色与菜单对应关系
+CREATE TABLE sys_role_menu (
+  id NUMBER(20, 0) NOT NULL,
+  role_id NUMBER(20, 0) NOT NULL,
+  menu_id NUMBER(20, 0) NOT NULL,
+  PRIMARY KEY (id)
+);
+
+-- 系统配置信息
+CREATE TABLE sys_config (
+  id NUMBER(20, 0) NOT NULL,
+  param_key varchar2(50),
+  param_value varchar2(4000),
+  status NUMBER(2, 0) DEFAULT 1 NOT NULL,
+  remark varchar2(500),
+  PRIMARY KEY (id)
+);
+CREATE UNIQUE INDEX index_param_key on sys_config(param_key);
+
+
+-- 系统日志
+CREATE TABLE sys_log (
+  id NUMBER(20, 0) NOT NULL,
+  username varchar2(50),
+  operation varchar2(50),
+  method varchar2(200),
+  params clob,
+  time NUMBER(20, 0) NOT NULL,
+  ip varchar2(64),
+  create_date timestamp,
+  PRIMARY KEY (id)
+);
+
+-- 文件上传
+CREATE TABLE sys_oss (
+  id NUMBER(20, 0) NOT NULL,
+  url varchar2(200),
+  create_date timestamp,
+  PRIMARY KEY (id)
+);
+
+-- 定时任务
+CREATE TABLE schedule_job (
+  job_id NUMBER(20, 0) NOT NULL,
+  bean_name varchar2(200),
+  params varchar2(2000),
+  cron_expression varchar2(100),
+  status NUMBER(2, 0) NOT NULL,
+  remark varchar2(255),
+  create_time timestamp,
+  PRIMARY KEY (job_id)
+);
+
+-- 定时任务日志
+CREATE TABLE schedule_job_log (
+  log_id NUMBER(20, 0) NOT NULL,
+  job_id NUMBER(20, 0) NOT NULL,
+  bean_name varchar2(200),
+  params varchar2(2000),
+  status NUMBER(2, 0) NOT NULL,
+  error varchar2(2000),
+  times NUMBER(10, 0) NOT NULL,
+  create_time timestamp,
+  PRIMARY KEY (log_id)
+);
+CREATE INDEX index_job_id on schedule_job_log(job_id);
+
+-- 用户表
+CREATE TABLE tb_user (
+  user_id NUMBER(20, 0) NOT NULL,
+  username varchar2(50) NOT NULL,
+  mobile varchar2(20) NOT NULL,
+  password varchar2(64),
+  create_time timestamp,
+  PRIMARY KEY (user_id)
+);
+CREATE UNIQUE INDEX index_tb_user_username on tb_user(username);
+
+INSERT INTO sys_user (user_id, username, password, salt, email, mobile, status, create_user_id, create_time) VALUES ('1', 'admin', '9ec9750e709431dad22365cabc5c625482e574c74adaebba7dd02f1129e4ce1d', 'YzcmCZNvbXocrsz9dm8e', 'root@renren.io', '13612345678', '1', '1', CURRENT_DATE);
+
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (1, 0, '系统管理', NULL, NULL, 0, 'system', 0);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (2, 1, '管理员列表', 'sys/user', NULL, 1, 'admin', 1);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (3, 1, '角色管理', 'sys/role', NULL, 1, 'role', 2);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (4, 1, '菜单管理', 'sys/menu', NULL, 1, 'menu', 3);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (5, 1, 'SQL监控', 'http://localhost:8080/renren-fast/druid/sql.html', NULL, 1, 'sql', 4);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (6, 1, '定时任务', 'job/schedule', NULL, 1, 'job', 5);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (7, 6, '查看', NULL, 'sys:schedule:list,sys:schedule:info', 2, NULL, 0);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (8, 6, '新增', NULL, 'sys:schedule:save', 2, NULL, 0);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (9, 6, '修改', NULL, 'sys:schedule:update', 2, NULL, 0);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (10, 6, '删除', NULL, 'sys:schedule:delete', 2, NULL, 0);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (11, 6, '暂停', NULL, 'sys:schedule:pause', 2, NULL, 0);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (12, 6, '恢复', NULL, 'sys:schedule:resume', 2, NULL, 0);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (13, 6, '立即执行', NULL, 'sys:schedule:run', 2, NULL, 0);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (14, 6, '日志列表', NULL, 'sys:schedule:log', 2, NULL, 0);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (15, 2, '查看', NULL, 'sys:user:list,sys:user:info', 2, NULL, 0);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (16, 2, '新增', NULL, 'sys:user:save,sys:role:select', 2, NULL, 0);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (17, 2, '修改', NULL, 'sys:user:update,sys:role:select', 2, NULL, 0);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (18, 2, '删除', NULL, 'sys:user:delete', 2, NULL, 0);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (19, 3, '查看', NULL, 'sys:role:list,sys:role:info', 2, NULL, 0);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (20, 3, '新增', NULL, 'sys:role:save,sys:menu:list', 2, NULL, 0);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (21, 3, '修改', NULL, 'sys:role:update,sys:menu:list', 2, NULL, 0);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (22, 3, '删除', NULL, 'sys:role:delete', 2, NULL, 0);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (23, 4, '查看', NULL, 'sys:menu:list,sys:menu:info', 2, NULL, 0);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (24, 4, '新增', NULL, 'sys:menu:save,sys:menu:select', 2, NULL, 0);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (25, 4, '修改', NULL, 'sys:menu:update,sys:menu:select', 2, NULL, 0);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (26, 4, '删除', NULL, 'sys:menu:delete', 2, NULL, 0);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (27, 1, '参数管理', 'sys/config', 'sys:config:list,sys:config:info,sys:config:save,sys:config:update,sys:config:delete', 1, 'config', 6);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (29, 1, '系统日志', 'sys/log', 'sys:log:list', 1, 'log', 7);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (30, 1, '文件上传', 'oss/oss', 'sys:oss:all', 1, 'oss', 6);
+
+
+
+INSERT INTO sys_config (id, param_key, param_value, status, remark) VALUES (1, 'CLOUD_STORAGE_CONFIG_KEY',  '{"aliyunAccessKeyId":"","aliyunAccessKeySecret":"","aliyunBucketName":"","aliyunDomain":"","aliyunEndPoint":"","aliyunPrefix":"","qcloudBucketName":"","qcloudDomain":"","qcloudPrefix":"","qcloudSecretId":"","qcloudSecretKey":"","qiniuAccessKey":"NrgMfABZxWLo5B-YYSjoE8-AZ1EISdi1Z3ubLOeZ","qiniuBucketName":"ios-app","qiniuDomain":"http://7xlij2.com1.z0.glb.clouddn.com","qiniuPrefix":"upload","qiniuSecretKey":"uIwJHevMRWU0VLxFvgy0tAcOdGqasdtVlJkdy6vV","type":1}', '0', '云存储配置信息');
+
+INSERT INTO schedule_job (job_id, bean_name, params, cron_expression, status, remark, create_time) VALUES (1, 'testTask', 'renren', '0 0/30 * * * ?', '0', '参数测试', CURRENT_DATE);
+
+
+-- 账号:13612345678  密码:admin
+INSERT INTO tb_user (user_id, username, mobile, password, create_time) VALUES (1, 'mark', '13612345678', '8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918', CURRENT_DATE);
+
+
+
+
+--  quartz自带表结构
+CREATE TABLE qrtz_job_details
+(
+  SCHED_NAME VARCHAR2(120) NOT NULL,
+  JOB_NAME  VARCHAR2(200) NOT NULL,
+  JOB_GROUP VARCHAR2(200) NOT NULL,
+  DESCRIPTION VARCHAR2(250) NULL,
+  JOB_CLASS_NAME   VARCHAR2(250) NOT NULL,
+  IS_DURABLE VARCHAR2(1) NOT NULL,
+  IS_NONCONCURRENT VARCHAR2(1) NOT NULL,
+  IS_UPDATE_DATA VARCHAR2(1) NOT NULL,
+  REQUESTS_RECOVERY VARCHAR2(1) NOT NULL,
+  JOB_DATA BLOB NULL,
+  CONSTRAINT QRTZ_JOB_DETAILS_PK PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)
+);
+CREATE TABLE qrtz_triggers
+(
+  SCHED_NAME VARCHAR2(120) NOT NULL,
+  TRIGGER_NAME VARCHAR2(200) NOT NULL,
+  TRIGGER_GROUP VARCHAR2(200) NOT NULL,
+  JOB_NAME  VARCHAR2(200) NOT NULL,
+  JOB_GROUP VARCHAR2(200) NOT NULL,
+  DESCRIPTION VARCHAR2(250) NULL,
+  NEXT_FIRE_TIME NUMBER(13) NULL,
+  PREV_FIRE_TIME NUMBER(13) NULL,
+  PRIORITY NUMBER(13) NULL,
+  TRIGGER_STATE VARCHAR2(16) NOT NULL,
+  TRIGGER_TYPE VARCHAR2(8) NOT NULL,
+  START_TIME NUMBER(13) NOT NULL,
+  END_TIME NUMBER(13) NULL,
+  CALENDAR_NAME VARCHAR2(200) NULL,
+  MISFIRE_INSTR NUMBER(2) NULL,
+  JOB_DATA BLOB NULL,
+  CONSTRAINT QRTZ_TRIGGERS_PK PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
+  CONSTRAINT QRTZ_TRIGGER_TO_JOBS_FK FOREIGN KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)
+  REFERENCES QRTZ_JOB_DETAILS(SCHED_NAME,JOB_NAME,JOB_GROUP)
+);
+CREATE TABLE qrtz_simple_triggers
+(
+  SCHED_NAME VARCHAR2(120) NOT NULL,
+  TRIGGER_NAME VARCHAR2(200) NOT NULL,
+  TRIGGER_GROUP VARCHAR2(200) NOT NULL,
+  REPEAT_COUNT NUMBER(7) NOT NULL,
+  REPEAT_INTERVAL NUMBER(12) NOT NULL,
+  TIMES_TRIGGERED NUMBER(10) NOT NULL,
+  CONSTRAINT QRTZ_SIMPLE_TRIG_PK PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
+  CONSTRAINT QRTZ_SIMPLE_TRIG_TO_TRIG_FK FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
+  REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
+);
+CREATE TABLE qrtz_cron_triggers
+(
+  SCHED_NAME VARCHAR2(120) NOT NULL,
+  TRIGGER_NAME VARCHAR2(200) NOT NULL,
+  TRIGGER_GROUP VARCHAR2(200) NOT NULL,
+  CRON_EXPRESSION VARCHAR2(120) NOT NULL,
+  TIME_ZONE_ID VARCHAR2(80),
+  CONSTRAINT QRTZ_CRON_TRIG_PK PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
+  CONSTRAINT QRTZ_CRON_TRIG_TO_TRIG_FK FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
+  REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
+);
+CREATE TABLE qrtz_simprop_triggers
+(
+  SCHED_NAME VARCHAR2(120) NOT NULL,
+  TRIGGER_NAME VARCHAR2(200) NOT NULL,
+  TRIGGER_GROUP VARCHAR2(200) NOT NULL,
+  STR_PROP_1 VARCHAR2(512) NULL,
+  STR_PROP_2 VARCHAR2(512) NULL,
+  STR_PROP_3 VARCHAR2(512) NULL,
+  INT_PROP_1 NUMBER(10) NULL,
+  INT_PROP_2 NUMBER(10) NULL,
+  LONG_PROP_1 NUMBER(13) NULL,
+  LONG_PROP_2 NUMBER(13) NULL,
+  DEC_PROP_1 NUMERIC(13,4) NULL,
+  DEC_PROP_2 NUMERIC(13,4) NULL,
+  BOOL_PROP_1 VARCHAR2(1) NULL,
+  BOOL_PROP_2 VARCHAR2(1) NULL,
+  CONSTRAINT QRTZ_SIMPROP_TRIG_PK PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
+  CONSTRAINT QRTZ_SIMPROP_TRIG_TO_TRIG_FK FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
+  REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
+);
+CREATE TABLE qrtz_blob_triggers
+(
+  SCHED_NAME VARCHAR2(120) NOT NULL,
+  TRIGGER_NAME VARCHAR2(200) NOT NULL,
+  TRIGGER_GROUP VARCHAR2(200) NOT NULL,
+  BLOB_DATA BLOB NULL,
+  CONSTRAINT QRTZ_BLOB_TRIG_PK PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
+  CONSTRAINT QRTZ_BLOB_TRIG_TO_TRIG_FK FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
+  REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
+);
+CREATE TABLE qrtz_calendars
+(
+  SCHED_NAME VARCHAR2(120) NOT NULL,
+  CALENDAR_NAME  VARCHAR2(200) NOT NULL,
+  CALENDAR BLOB NOT NULL,
+  CONSTRAINT QRTZ_CALENDARS_PK PRIMARY KEY (SCHED_NAME,CALENDAR_NAME)
+);
+CREATE TABLE qrtz_paused_trigger_grps
+(
+  SCHED_NAME VARCHAR2(120) NOT NULL,
+  TRIGGER_GROUP  VARCHAR2(200) NOT NULL,
+  CONSTRAINT QRTZ_PAUSED_TRIG_GRPS_PK PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP)
+);
+CREATE TABLE qrtz_fired_triggers
+(
+  SCHED_NAME VARCHAR2(120) NOT NULL,
+  ENTRY_ID VARCHAR2(95) NOT NULL,
+  TRIGGER_NAME VARCHAR2(200) NOT NULL,
+  TRIGGER_GROUP VARCHAR2(200) NOT NULL,
+  INSTANCE_NAME VARCHAR2(200) NOT NULL,
+  FIRED_TIME NUMBER(13) NOT NULL,
+  SCHED_TIME NUMBER(13) NOT NULL,
+  PRIORITY NUMBER(13) NOT NULL,
+  STATE VARCHAR2(16) NOT NULL,
+  JOB_NAME VARCHAR2(200) NULL,
+  JOB_GROUP VARCHAR2(200) NULL,
+  IS_NONCONCURRENT VARCHAR2(1) NULL,
+  REQUESTS_RECOVERY VARCHAR2(1) NULL,
+  CONSTRAINT QRTZ_FIRED_TRIGGER_PK PRIMARY KEY (SCHED_NAME,ENTRY_ID)
+);
+CREATE TABLE qrtz_scheduler_state
+(
+  SCHED_NAME VARCHAR2(120) NOT NULL,
+  INSTANCE_NAME VARCHAR2(200) NOT NULL,
+  LAST_CHECKIN_TIME NUMBER(13) NOT NULL,
+  CHECKIN_INTERVAL NUMBER(13) NOT NULL,
+  CONSTRAINT QRTZ_SCHEDULER_STATE_PK PRIMARY KEY (SCHED_NAME,INSTANCE_NAME)
+);
+CREATE TABLE qrtz_locks
+(
+  SCHED_NAME VARCHAR2(120) NOT NULL,
+  LOCK_NAME  VARCHAR2(40) NOT NULL,
+  CONSTRAINT QRTZ_LOCKS_PK PRIMARY KEY (SCHED_NAME,LOCK_NAME)
+);
+
+create index idx_qrtz_j_req_recovery on qrtz_job_details(SCHED_NAME,REQUESTS_RECOVERY);
+create index idx_qrtz_j_grp on qrtz_job_details(SCHED_NAME,JOB_GROUP);
+
+create index idx_qrtz_t_j on qrtz_triggers(SCHED_NAME,JOB_NAME,JOB_GROUP);
+create index idx_qrtz_t_jg on qrtz_triggers(SCHED_NAME,JOB_GROUP);
+create index idx_qrtz_t_c on qrtz_triggers(SCHED_NAME,CALENDAR_NAME);
+create index idx_qrtz_t_g on qrtz_triggers(SCHED_NAME,TRIGGER_GROUP);
+create index idx_qrtz_t_state on qrtz_triggers(SCHED_NAME,TRIGGER_STATE);
+create index idx_qrtz_t_n_state on qrtz_triggers(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP,TRIGGER_STATE);
+create index idx_qrtz_t_n_g_state on qrtz_triggers(SCHED_NAME,TRIGGER_GROUP,TRIGGER_STATE);
+create index idx_qrtz_t_next_fire_time on qrtz_triggers(SCHED_NAME,NEXT_FIRE_TIME);
+create index idx_qrtz_t_nft_st on qrtz_triggers(SCHED_NAME,TRIGGER_STATE,NEXT_FIRE_TIME);
+create index idx_qrtz_t_nft_misfire on qrtz_triggers(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME);
+create index idx_qrtz_t_nft_st_misfire on qrtz_triggers(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_STATE);
+create index idx_qrtz_t_nft_st_misfire_grp on qrtz_triggers(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_GROUP,TRIGGER_STATE);
+
+create index idx_qrtz_ft_trig_inst_name on qrtz_fired_triggers(SCHED_NAME,INSTANCE_NAME);
+create index idx_qrtz_ft_inst_job_req_rcvry on qrtz_fired_triggers(SCHED_NAME,INSTANCE_NAME,REQUESTS_RECOVERY);
+create index idx_qrtz_ft_j_g on qrtz_fired_triggers(SCHED_NAME,JOB_NAME,JOB_GROUP);
+create index idx_qrtz_ft_jg on qrtz_fired_triggers(SCHED_NAME,JOB_GROUP);
+create index idx_qrtz_ft_t_g on qrtz_fired_triggers(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP);
+create index idx_qrtz_ft_tg on qrtz_fired_triggers(SCHED_NAME,TRIGGER_GROUP);
+
+

+ 364 - 0
db/postgresql.sql

@@ -0,0 +1,364 @@
+-- 菜单
+CREATE TABLE sys_menu (
+  menu_id bigserial,
+  parent_id int8,
+  name varchar(50),
+  url varchar(200),
+  perms varchar(500),
+  type int,
+  icon varchar(50),
+  order_num int,
+  PRIMARY KEY (menu_id)
+);
+
+
+-- 系统用户
+CREATE TABLE sys_user (
+  user_id bigserial,
+  username varchar(50) NOT NULL,
+  password varchar(100),
+  salt varchar(20),
+  email varchar(100),
+  mobile varchar(100),
+  status int,
+  create_user_id int8,
+  create_time timestamp,
+  PRIMARY KEY (user_id),
+  UNIQUE (username)
+);
+
+
+-- 系统用户Token
+CREATE TABLE sys_user_token (
+  user_id bigserial,
+  token varchar(100) NOT NULL,
+  expire_time timestamp,
+  update_time timestamp,
+  PRIMARY KEY (user_id),
+  UNIQUE (token)
+);
+
+-- 系统验证码
+CREATE TABLE sys_captcha (
+  uuid varchar(36) NOT NULL,
+  code varchar(6) NOT NULL,
+  expire_time timestamp,
+  PRIMARY KEY (uuid)
+);
+
+-- 角色
+CREATE TABLE sys_role (
+  role_id bigserial,
+  role_name varchar(100),
+  remark varchar(100),
+  create_user_id int8,
+  create_time timestamp,
+  PRIMARY KEY (role_id)
+);
+
+-- 用户与角色对应关系
+CREATE TABLE sys_user_role (
+  id bigserial,
+  user_id int8,
+  role_id int8,
+  PRIMARY KEY (id)
+);
+
+-- 角色与菜单对应关系
+CREATE TABLE sys_role_menu (
+  id bigserial,
+  role_id int8,
+  menu_id int8,
+  PRIMARY KEY (id)
+);
+
+-- 系统配置信息
+CREATE TABLE sys_config (
+  id bigserial,
+  param_key varchar(50),
+  param_value varchar(2000),
+  status int DEFAULT 1,
+  remark varchar(500),
+  PRIMARY KEY (id),
+  UNIQUE (param_key)
+);
+
+
+-- 系统日志
+CREATE TABLE sys_log (
+  id bigserial,
+  username varchar(50),
+  operation varchar(50),
+  method varchar(200),
+  params varchar(5000),
+  time int8 NOT NULL,
+  ip varchar(64),
+  create_date timestamp,
+  PRIMARY KEY (id)
+);
+
+-- 文件上传
+CREATE TABLE sys_oss (
+  id bigserial,
+  url varchar(200),
+  create_date timestamp,
+  PRIMARY KEY (id)
+);
+
+
+-- 定时任务
+CREATE TABLE schedule_job (
+  job_id bigserial,
+  bean_name varchar(200),
+  params varchar(2000),
+  cron_expression varchar(100),
+  status int,
+  remark varchar(255),
+  create_time timestamp,
+  PRIMARY KEY (job_id)
+);
+
+-- 定时任务日志
+CREATE TABLE schedule_job_log (
+  log_id bigserial,
+  job_id int8 NOT NULL,
+  bean_name varchar(200),
+  params varchar(2000),
+  status int NOT NULL,
+  error varchar(2000),
+  times int NOT NULL,
+  create_time timestamp,
+  PRIMARY KEY (log_id)
+);
+CREATE INDEX index_job_id on schedule_job_log(job_id);
+
+-- 用户表
+CREATE TABLE tb_user (
+  user_id bigserial,
+  username varchar(50) NOT NULL,
+  mobile varchar(20) NOT NULL,
+  password varchar(64),
+  create_time timestamp,
+  PRIMARY KEY (user_id),
+  UNIQUE (username)
+);
+
+
+INSERT INTO sys_user (user_id, username, password, salt, email, mobile, status, create_user_id, create_time) VALUES ('1', 'admin', '9ec9750e709431dad22365cabc5c625482e574c74adaebba7dd02f1129e4ce1d', 'YzcmCZNvbXocrsz9dm8e', 'root@renren.io', '13612345678', '1', '1', '2016-11-11 11:11:11');
+
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (1, 0, '系统管理', NULL, NULL, 0, 'system', 0);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (2, 1, '管理员列表', 'sys/user', NULL, 1, 'admin', 1);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (3, 1, '角色管理', 'sys/role', NULL, 1, 'role', 2);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (4, 1, '菜单管理', 'sys/menu', NULL, 1, 'menu', 3);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (5, 1, 'SQL监控', 'http://localhost:8080/renren-fast/druid/sql.html', NULL, 1, 'sql', 4);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (6, 1, '定时任务', 'job/schedule', NULL, 1, 'job', 5);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (7, 6, '查看', NULL, 'sys:schedule:list,sys:schedule:info', 2, NULL, 0);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (8, 6, '新增', NULL, 'sys:schedule:save', 2, NULL, 0);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (9, 6, '修改', NULL, 'sys:schedule:update', 2, NULL, 0);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (10, 6, '删除', NULL, 'sys:schedule:delete', 2, NULL, 0);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (11, 6, '暂停', NULL, 'sys:schedule:pause', 2, NULL, 0);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (12, 6, '恢复', NULL, 'sys:schedule:resume', 2, NULL, 0);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (13, 6, '立即执行', NULL, 'sys:schedule:run', 2, NULL, 0);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (14, 6, '日志列表', NULL, 'sys:schedule:log', 2, NULL, 0);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (15, 2, '查看', NULL, 'sys:user:list,sys:user:info', 2, NULL, 0);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (16, 2, '新增', NULL, 'sys:user:save,sys:role:select', 2, NULL, 0);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (17, 2, '修改', NULL, 'sys:user:update,sys:role:select', 2, NULL, 0);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (18, 2, '删除', NULL, 'sys:user:delete', 2, NULL, 0);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (19, 3, '查看', NULL, 'sys:role:list,sys:role:info', 2, NULL, 0);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (20, 3, '新增', NULL, 'sys:role:save,sys:menu:list', 2, NULL, 0);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (21, 3, '修改', NULL, 'sys:role:update,sys:menu:list', 2, NULL, 0);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (22, 3, '删除', NULL, 'sys:role:delete', 2, NULL, 0);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (23, 4, '查看', NULL, 'sys:menu:list,sys:menu:info', 2, NULL, 0);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (24, 4, '新增', NULL, 'sys:menu:save,sys:menu:select', 2, NULL, 0);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (25, 4, '修改', NULL, 'sys:menu:update,sys:menu:select', 2, NULL, 0);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (26, 4, '删除', NULL, 'sys:menu:delete', 2, NULL, 0);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (27, 1, '参数管理', 'sys/config', 'sys:config:list,sys:config:info,sys:config:save,sys:config:update,sys:config:delete', 1, 'config', 6);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (29, 1, '系统日志', 'sys/log', 'sys:log:list', 1, 'log', 7);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (30, 1, '文件上传', 'oss/oss', 'sys:oss:all', 1, 'oss', 6);
+
+
+INSERT INTO sys_config (param_key, param_value, status, remark) VALUES ('CLOUD_STORAGE_CONFIG_KEY',  '{"aliyunAccessKeyId":"","aliyunAccessKeySecret":"","aliyunBucketName":"","aliyunDomain":"","aliyunEndPoint":"","aliyunPrefix":"","qcloudBucketName":"","qcloudDomain":"","qcloudPrefix":"","qcloudSecretId":"","qcloudSecretKey":"","qiniuAccessKey":"NrgMfABZxWLo5B-YYSjoE8-AZ1EISdi1Z3ubLOeZ","qiniuBucketName":"ios-app","qiniuDomain":"http://7xlij2.com1.z0.glb.clouddn.com","qiniuPrefix":"upload","qiniuSecretKey":"uIwJHevMRWU0VLxFvgy0tAcOdGqasdtVlJkdy6vV","type":1}', '0', '云存储配置信息');
+
+INSERT INTO schedule_job (bean_name, params, cron_expression, status, remark, create_time) VALUES ('testTask',  'renren', '0 0/30 * * * ?', '0', '参数测试', '2016-12-01 23:16:46');
+
+
+-- 账号:13612345678  密码:admin
+INSERT INTO tb_user (username, mobile, password, create_time) VALUES ('mark', '13612345678', '8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918', '2017-03-23 22:37:41');
+
+
+alter sequence sys_menu_menu_id_seq restart with 31;
+alter sequence sys_user_user_id_seq restart with 2;
+
+
+--  quartz自带表结构
+
+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 BOOL NOT NULL,
+  IS_NONCONCURRENT BOOL NOT NULL,
+  IS_UPDATE_DATA BOOL NOT NULL,
+  REQUESTS_RECOVERY BOOL NOT NULL,
+  JOB_DATA BYTEA NULL,
+  PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)
+);
+
+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 NULL,
+  PREV_FIRE_TIME BIGINT NULL,
+  PRIORITY INTEGER NULL,
+  TRIGGER_STATE VARCHAR(16) NOT NULL,
+  TRIGGER_TYPE VARCHAR(8) NOT NULL,
+  START_TIME BIGINT NOT NULL,
+  END_TIME BIGINT NULL,
+  CALENDAR_NAME VARCHAR(200) NULL,
+  MISFIRE_INSTR SMALLINT NULL,
+  JOB_DATA BYTEA 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)
+);
+
+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 NOT NULL,
+  REPEAT_INTERVAL BIGINT NOT NULL,
+  TIMES_TRIGGERED BIGINT 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)
+);
+
+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(120) 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)
+);
+
+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 BOOL NULL,
+  BOOL_PROP_2 BOOL 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)
+);
+
+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 BYTEA 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)
+);
+
+CREATE TABLE qrtz_calendars
+(
+  SCHED_NAME VARCHAR(120) NOT NULL,
+  CALENDAR_NAME  VARCHAR(200) NOT NULL,
+  CALENDAR BYTEA NOT NULL,
+  PRIMARY KEY (SCHED_NAME,CALENDAR_NAME)
+);
+
+
+CREATE TABLE qrtz_paused_trigger_grps
+(
+  SCHED_NAME VARCHAR(120) NOT NULL,
+  TRIGGER_GROUP  VARCHAR(200) NOT NULL,
+  PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP)
+);
+
+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 NOT NULL,
+  SCHED_TIME BIGINT NOT NULL,
+  PRIORITY INTEGER NOT NULL,
+  STATE VARCHAR(16) NOT NULL,
+  JOB_NAME VARCHAR(200) NULL,
+  JOB_GROUP VARCHAR(200) NULL,
+  IS_NONCONCURRENT BOOL NULL,
+  REQUESTS_RECOVERY BOOL NULL,
+  PRIMARY KEY (SCHED_NAME,ENTRY_ID)
+);
+
+CREATE TABLE qrtz_scheduler_state
+(
+  SCHED_NAME VARCHAR(120) NOT NULL,
+  INSTANCE_NAME VARCHAR(200) NOT NULL,
+  LAST_CHECKIN_TIME BIGINT NOT NULL,
+  CHECKIN_INTERVAL BIGINT NOT NULL,
+  PRIMARY KEY (SCHED_NAME,INSTANCE_NAME)
+);
+
+CREATE TABLE qrtz_locks
+(
+  SCHED_NAME VARCHAR(120) NOT NULL,
+  LOCK_NAME  VARCHAR(40) NOT NULL,
+  PRIMARY KEY (SCHED_NAME,LOCK_NAME)
+);
+
+create index idx_qrtz_j_req_recovery on qrtz_job_details(SCHED_NAME,REQUESTS_RECOVERY);
+create index idx_qrtz_j_grp on qrtz_job_details(SCHED_NAME,JOB_GROUP);
+
+create index idx_qrtz_t_j on qrtz_triggers(SCHED_NAME,JOB_NAME,JOB_GROUP);
+create index idx_qrtz_t_jg on qrtz_triggers(SCHED_NAME,JOB_GROUP);
+create index idx_qrtz_t_c on qrtz_triggers(SCHED_NAME,CALENDAR_NAME);
+create index idx_qrtz_t_g on qrtz_triggers(SCHED_NAME,TRIGGER_GROUP);
+create index idx_qrtz_t_state on qrtz_triggers(SCHED_NAME,TRIGGER_STATE);
+create index idx_qrtz_t_n_state on qrtz_triggers(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP,TRIGGER_STATE);
+create index idx_qrtz_t_n_g_state on qrtz_triggers(SCHED_NAME,TRIGGER_GROUP,TRIGGER_STATE);
+create index idx_qrtz_t_next_fire_time on qrtz_triggers(SCHED_NAME,NEXT_FIRE_TIME);
+create index idx_qrtz_t_nft_st on qrtz_triggers(SCHED_NAME,TRIGGER_STATE,NEXT_FIRE_TIME);
+create index idx_qrtz_t_nft_misfire on qrtz_triggers(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME);
+create index idx_qrtz_t_nft_st_misfire on qrtz_triggers(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_STATE);
+create index idx_qrtz_t_nft_st_misfire_grp on qrtz_triggers(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_GROUP,TRIGGER_STATE);
+
+create index idx_qrtz_ft_trig_inst_name on qrtz_fired_triggers(SCHED_NAME,INSTANCE_NAME);
+create index idx_qrtz_ft_inst_job_req_rcvry on qrtz_fired_triggers(SCHED_NAME,INSTANCE_NAME,REQUESTS_RECOVERY);
+create index idx_qrtz_ft_j_g on qrtz_fired_triggers(SCHED_NAME,JOB_NAME,JOB_GROUP);
+create index idx_qrtz_ft_jg on qrtz_fired_triggers(SCHED_NAME,JOB_GROUP);
+create index idx_qrtz_ft_t_g on qrtz_fired_triggers(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP);
+create index idx_qrtz_ft_tg on qrtz_fired_triggers(SCHED_NAME,TRIGGER_GROUP);
+
+
+commit;

+ 512 - 0
db/sqlserver.sql

@@ -0,0 +1,512 @@
+-- 菜单
+CREATE TABLE sys_menu (
+  menu_id bigint NOT NULL IDENTITY(1,1),
+  parent_id bigint,
+  name varchar(50),
+  url varchar(200),
+  perms varchar(500),
+  type int,
+  icon varchar(50),
+  order_num int,
+  PRIMARY KEY (menu_id)
+);
+
+-- 系统用户
+CREATE TABLE sys_user (
+  user_id bigint NOT NULL IDENTITY(1,1),
+  username varchar(50) NOT NULL,
+  password varchar(100),
+  salt varchar(20),
+  email varchar(100),
+  mobile varchar(100),
+  status tinyint,
+  create_user_id bigint,
+  create_time datetime,
+  PRIMARY KEY (user_id),
+  UNIQUE (username)
+);
+
+-- 系统用户Token
+CREATE TABLE sys_user_token (
+  user_id bigint NOT NULL,
+  token varchar(100) NOT NULL,
+  expire_time datetime,
+  update_time datetime,
+  PRIMARY KEY (user_id),
+  UNIQUE (token)
+);
+
+-- 系统验证码
+CREATE TABLE sys_captcha (
+  uuid varchar(36) NOT NULL,
+  code varchar(6) NOT NULL,
+  expire_time datetime,
+  PRIMARY KEY (uuid)
+);
+
+-- 角色
+CREATE TABLE sys_role (
+  role_id bigint NOT NULL IDENTITY(1,1),
+  role_name varchar(100),
+  remark varchar(100),
+  create_user_id bigint,
+  create_time datetime,
+PRIMARY KEY (role_id)
+);
+
+-- 用户与角色对应关系
+CREATE TABLE sys_user_role (
+  id bigint NOT NULL IDENTITY(1,1),
+  user_id bigint,
+  role_id bigint,
+PRIMARY KEY (id)
+);
+
+-- 角色与菜单对应关系
+CREATE TABLE sys_role_menu (
+  id bigint NOT NULL IDENTITY(1,1),
+  role_id bigint,
+  menu_id bigint,
+PRIMARY KEY (id)
+);
+
+-- 系统配置信息
+CREATE TABLE sys_config (
+  id bigint NOT NULL IDENTITY(1,1),
+  param_key varchar(50),
+  param_value varchar(2000),
+  status tinyint DEFAULT 1,
+  remark varchar(500),
+  PRIMARY KEY (id),
+  UNIQUE (param_key)
+);
+
+-- 系统日志
+CREATE TABLE sys_log (
+  id bigint NOT NULL IDENTITY(1,1),
+  username varchar(50),
+  operation varchar(50),
+  method varchar(200),
+  params varchar(5000),
+  time bigint NOT NULL,
+  ip varchar(64),
+  create_date datetime,
+PRIMARY KEY (id)
+);
+
+-- 文件上传
+CREATE TABLE sys_oss (
+  id bigint NOT NULL IDENTITY(1,1),
+  url varchar(200),
+  create_date datetime,
+  PRIMARY KEY (id)
+);
+
+-- 定时任务
+CREATE TABLE schedule_job (
+  job_id bigint NOT NULL IDENTITY(1,1),
+  bean_name varchar(200),
+  params varchar(2000),
+  cron_expression varchar(100),
+  status tinyint,
+  remark varchar(255),
+  create_time datetime,
+  PRIMARY KEY (job_id)
+);
+
+-- 定时任务日志
+CREATE TABLE schedule_job_log (
+  log_id bigint NOT NULL IDENTITY(1,1),
+  job_id bigint NOT NULL,
+  bean_name varchar(200),
+  params varchar(2000),
+  status tinyint NOT NULL,
+  error varchar(2000),
+  times int NOT NULL,
+  create_time datetime,
+  PRIMARY KEY (log_id),
+  INDEX job_id (job_id)
+);
+
+-- 用户表
+CREATE TABLE tb_user (
+  user_id bigint NOT NULL IDENTITY(1,1),
+  username varchar(50) NOT NULL,
+  mobile varchar(20) NOT NULL,
+  password varchar(64),
+  create_time datetime,
+  PRIMARY KEY (user_id),
+  UNIQUE (username)
+);
+
+SET IDENTITY_INSERT sys_user ON;
+INSERT INTO sys_user (user_id, username, password, salt, email, mobile, status, create_user_id, create_time) VALUES ('1', 'admin', '9ec9750e709431dad22365cabc5c625482e574c74adaebba7dd02f1129e4ce1d', 'YzcmCZNvbXocrsz9dm8e', 'root@renren.io', '13612345678', '1', '1', '2016-11-11 11:11:11');
+SET IDENTITY_INSERT sys_user OFF;
+
+SET IDENTITY_INSERT sys_menu ON;
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (1, 0, '系统管理', NULL, NULL, 0, 'system', 0);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (2, 1, '管理员列表', 'sys/user', NULL, 1, 'admin', 1);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (3, 1, '角色管理', 'sys/role', NULL, 1, 'role', 2);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (4, 1, '菜单管理', 'sys/menu', NULL, 1, 'menu', 3);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (5, 1, 'SQL监控', 'http://localhost:8080/renren-fast/druid/sql.html', NULL, 1, 'sql', 4);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (6, 1, '定时任务', 'job/schedule', NULL, 1, 'job', 5);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (7, 6, '查看', NULL, 'sys:schedule:list,sys:schedule:info', 2, NULL, 0);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (8, 6, '新增', NULL, 'sys:schedule:save', 2, NULL, 0);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (9, 6, '修改', NULL, 'sys:schedule:update', 2, NULL, 0);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (10, 6, '删除', NULL, 'sys:schedule:delete', 2, NULL, 0);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (11, 6, '暂停', NULL, 'sys:schedule:pause', 2, NULL, 0);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (12, 6, '恢复', NULL, 'sys:schedule:resume', 2, NULL, 0);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (13, 6, '立即执行', NULL, 'sys:schedule:run', 2, NULL, 0);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (14, 6, '日志列表', NULL, 'sys:schedule:log', 2, NULL, 0);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (15, 2, '查看', NULL, 'sys:user:list,sys:user:info', 2, NULL, 0);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (16, 2, '新增', NULL, 'sys:user:save,sys:role:select', 2, NULL, 0);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (17, 2, '修改', NULL, 'sys:user:update,sys:role:select', 2, NULL, 0);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (18, 2, '删除', NULL, 'sys:user:delete', 2, NULL, 0);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (19, 3, '查看', NULL, 'sys:role:list,sys:role:info', 2, NULL, 0);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (20, 3, '新增', NULL, 'sys:role:save,sys:menu:list', 2, NULL, 0);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (21, 3, '修改', NULL, 'sys:role:update,sys:menu:list', 2, NULL, 0);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (22, 3, '删除', NULL, 'sys:role:delete', 2, NULL, 0);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (23, 4, '查看', NULL, 'sys:menu:list,sys:menu:info', 2, NULL, 0);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (24, 4, '新增', NULL, 'sys:menu:save,sys:menu:select', 2, NULL, 0);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (25, 4, '修改', NULL, 'sys:menu:update,sys:menu:select', 2, NULL, 0);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (26, 4, '删除', NULL, 'sys:menu:delete', 2, NULL, 0);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (27, 1, '参数管理', 'sys/config', 'sys:config:list,sys:config:info,sys:config:save,sys:config:update,sys:config:delete', 1, 'config', 6);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (29, 1, '系统日志', 'sys/log', 'sys:log:list', 1, 'log', 7);
+INSERT INTO sys_menu(menu_id, parent_id, name, url, perms, type, icon, order_num) VALUES (30, 1, '文件上传', 'oss/oss', 'sys:oss:all', 1, 'oss', 6);
+
+SET IDENTITY_INSERT sys_menu OFF;
+
+
+INSERT INTO sys_config (param_key, param_value, status, remark) VALUES ('CLOUD_STORAGE_CONFIG_KEY',  '{"aliyunAccessKeyId":"","aliyunAccessKeySecret":"","aliyunBucketName":"","aliyunDomain":"","aliyunEndPoint":"","aliyunPrefix":"","qcloudBucketName":"","qcloudDomain":"","qcloudPrefix":"","qcloudSecretId":"","qcloudSecretKey":"","qiniuAccessKey":"NrgMfABZxWLo5B-YYSjoE8-AZ1EISdi1Z3ubLOeZ","qiniuBucketName":"ios-app","qiniuDomain":"http://7xlij2.com1.z0.glb.clouddn.com","qiniuPrefix":"upload","qiniuSecretKey":"uIwJHevMRWU0VLxFvgy0tAcOdGqasdtVlJkdy6vV","type":1}', '0', '云存储配置信息');
+
+INSERT INTO schedule_job (bean_name, params, cron_expression, status, remark, create_time) VALUES ('testTask', 'renren', '0 0/30 * * * ?', '0', '参数测试', '2016-12-01 23:16:46');
+
+
+-- 账号:13612345678  密码:admin
+INSERT INTO tb_user (username, mobile, password, create_time) VALUES ('mark', '13612345678', '8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918', '2017-03-23 22:37:41');
+
+
+
+
+
+--  quartz自带表结构
+IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[FK_QRTZ_TRIGGERS_QRTZ_JOB_DETAILS]') AND OBJECTPROPERTY(id, N'ISFOREIGNKEY') = 1)
+  ALTER TABLE [dbo].[QRTZ_TRIGGERS] DROP CONSTRAINT FK_QRTZ_TRIGGERS_QRTZ_JOB_DETAILS
+GO
+
+IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[FK_QRTZ_CRON_TRIGGERS_QRTZ_TRIGGERS]') AND OBJECTPROPERTY(id, N'ISFOREIGNKEY') = 1)
+  ALTER TABLE [dbo].[QRTZ_CRON_TRIGGERS] DROP CONSTRAINT FK_QRTZ_CRON_TRIGGERS_QRTZ_TRIGGERS
+GO
+
+IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[FK_QRTZ_SIMPLE_TRIGGERS_QRTZ_TRIGGERS]') AND OBJECTPROPERTY(id, N'ISFOREIGNKEY') = 1)
+  ALTER TABLE [dbo].[QRTZ_SIMPLE_TRIGGERS] DROP CONSTRAINT FK_QRTZ_SIMPLE_TRIGGERS_QRTZ_TRIGGERS
+GO
+
+IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[FK_QRTZ_SIMPROP_TRIGGERS_QRTZ_TRIGGERS]') AND OBJECTPROPERTY(id, N'ISFOREIGNKEY') = 1)
+  ALTER TABLE [dbo].[QRTZ_SIMPROP_TRIGGERS] DROP CONSTRAINT FK_QRTZ_SIMPROP_TRIGGERS_QRTZ_TRIGGERS
+GO
+
+IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[QRTZ_CALENDARS]') AND OBJECTPROPERTY(id, N'ISUSERTABLE') = 1)
+  DROP TABLE [dbo].[QRTZ_CALENDARS]
+GO
+
+IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[QRTZ_CRON_TRIGGERS]') AND OBJECTPROPERTY(id, N'ISUSERTABLE') = 1)
+  DROP TABLE [dbo].[QRTZ_CRON_TRIGGERS]
+GO
+
+IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[QRTZ_BLOB_TRIGGERS]') AND OBJECTPROPERTY(id, N'ISUSERTABLE') = 1)
+  DROP TABLE [dbo].[QRTZ_BLOB_TRIGGERS]
+GO
+
+IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[QRTZ_FIRED_TRIGGERS]') AND OBJECTPROPERTY(id, N'ISUSERTABLE') = 1)
+  DROP TABLE [dbo].[QRTZ_FIRED_TRIGGERS]
+GO
+
+IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[QRTZ_PAUSED_TRIGGER_GRPS]') AND OBJECTPROPERTY(id, N'ISUSERTABLE') = 1)
+  DROP TABLE [dbo].[QRTZ_PAUSED_TRIGGER_GRPS]
+GO
+
+IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[QRTZ_SCHEDULER_STATE]') AND OBJECTPROPERTY(id, N'ISUSERTABLE') = 1)
+  DROP TABLE [dbo].[QRTZ_SCHEDULER_STATE]
+GO
+
+IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[QRTZ_LOCKS]') AND OBJECTPROPERTY(id, N'ISUSERTABLE') = 1)
+  DROP TABLE [dbo].[QRTZ_LOCKS]
+GO
+
+IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[QRTZ_JOB_DETAILS]') AND OBJECTPROPERTY(id, N'ISUSERTABLE') = 1)
+  DROP TABLE [dbo].[QRTZ_JOB_DETAILS]
+GO
+
+IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[QRTZ_SIMPLE_TRIGGERS]') AND OBJECTPROPERTY(id, N'ISUSERTABLE') = 1)
+  DROP TABLE [dbo].[QRTZ_SIMPLE_TRIGGERS]
+GO
+
+IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[QRTZ_SIMPROP_TRIGGERS]') AND OBJECTPROPERTY(id, N'ISUSERTABLE') = 1)
+  DROP TABLE [dbo].[QRTZ_SIMPROP_TRIGGERS]
+GO
+
+IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[QRTZ_TRIGGERS]') AND OBJECTPROPERTY(id, N'ISUSERTABLE') = 1)
+  DROP TABLE [dbo].[QRTZ_TRIGGERS]
+GO
+
+CREATE TABLE [dbo].[QRTZ_CALENDARS] (
+  [SCHED_NAME] [VARCHAR] (120)  NOT NULL ,
+  [CALENDAR_NAME] [VARCHAR] (200)  NOT NULL ,
+  [CALENDAR] [IMAGE] NOT NULL
+) ON [PRIMARY]
+GO
+
+CREATE TABLE [dbo].[QRTZ_CRON_TRIGGERS] (
+  [SCHED_NAME] [VARCHAR] (120)  NOT NULL ,
+  [TRIGGER_NAME] [VARCHAR] (200)  NOT NULL ,
+  [TRIGGER_GROUP] [VARCHAR] (200)  NOT NULL ,
+  [CRON_EXPRESSION] [VARCHAR] (120)  NOT NULL ,
+  [TIME_ZONE_ID] [VARCHAR] (80)
+) ON [PRIMARY]
+GO
+
+CREATE TABLE [dbo].[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] NOT NULL ,
+  [SCHED_TIME] [BIGINT] 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
+) ON [PRIMARY]
+GO
+
+CREATE TABLE [dbo].[QRTZ_PAUSED_TRIGGER_GRPS] (
+  [SCHED_NAME] [VARCHAR] (120)  NOT NULL ,
+  [TRIGGER_GROUP] [VARCHAR] (200)  NOT NULL
+) ON [PRIMARY]
+GO
+
+CREATE TABLE [dbo].[QRTZ_SCHEDULER_STATE] (
+  [SCHED_NAME] [VARCHAR] (120)  NOT NULL ,
+  [INSTANCE_NAME] [VARCHAR] (200)  NOT NULL ,
+  [LAST_CHECKIN_TIME] [BIGINT] NOT NULL ,
+  [CHECKIN_INTERVAL] [BIGINT] NOT NULL
+) ON [PRIMARY]
+GO
+
+CREATE TABLE [dbo].[QRTZ_LOCKS] (
+  [SCHED_NAME] [VARCHAR] (120)  NOT NULL ,
+  [LOCK_NAME] [VARCHAR] (40)  NOT NULL
+) ON [PRIMARY]
+GO
+
+CREATE TABLE [dbo].[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] [IMAGE] NULL
+) ON [PRIMARY]
+GO
+
+CREATE TABLE [dbo].[QRTZ_SIMPLE_TRIGGERS] (
+  [SCHED_NAME] [VARCHAR] (120)  NOT NULL ,
+  [TRIGGER_NAME] [VARCHAR] (200)  NOT NULL ,
+  [TRIGGER_GROUP] [VARCHAR] (200)  NOT NULL ,
+  [REPEAT_COUNT] [BIGINT] NOT NULL ,
+  [REPEAT_INTERVAL] [BIGINT] NOT NULL ,
+  [TIMES_TRIGGERED] [BIGINT] NOT NULL
+) ON [PRIMARY]
+GO
+
+CREATE TABLE [dbo].[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,
+) ON [PRIMARY]
+GO
+
+CREATE TABLE [dbo].[QRTZ_BLOB_TRIGGERS] (
+  [SCHED_NAME] [VARCHAR] (120)  NOT NULL ,
+  [TRIGGER_NAME] [VARCHAR] (200)  NOT NULL ,
+  [TRIGGER_GROUP] [VARCHAR] (200)  NOT NULL ,
+  [BLOB_DATA] [IMAGE] NULL
+) ON [PRIMARY]
+GO
+
+CREATE TABLE [dbo].[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] NULL ,
+  [PREV_FIRE_TIME] [BIGINT] NULL ,
+  [PRIORITY] [INTEGER] NULL ,
+  [TRIGGER_STATE] [VARCHAR] (16)  NOT NULL ,
+  [TRIGGER_TYPE] [VARCHAR] (8)  NOT NULL ,
+  [START_TIME] [BIGINT] NOT NULL ,
+  [END_TIME] [BIGINT] NULL ,
+  [CALENDAR_NAME] [VARCHAR] (200)  NULL ,
+  [MISFIRE_INSTR] [SMALLINT] NULL ,
+  [JOB_DATA] [IMAGE] NULL
+) ON [PRIMARY]
+GO
+
+ALTER TABLE [dbo].[QRTZ_CALENDARS] WITH NOCHECK ADD
+  CONSTRAINT [PK_QRTZ_CALENDARS] PRIMARY KEY  CLUSTERED
+    (
+      [SCHED_NAME],
+      [CALENDAR_NAME]
+    )  ON [PRIMARY]
+GO
+
+ALTER TABLE [dbo].[QRTZ_CRON_TRIGGERS] WITH NOCHECK ADD
+  CONSTRAINT [PK_QRTZ_CRON_TRIGGERS] PRIMARY KEY  CLUSTERED
+    (
+      [SCHED_NAME],
+      [TRIGGER_NAME],
+      [TRIGGER_GROUP]
+    )  ON [PRIMARY]
+GO
+
+ALTER TABLE [dbo].[QRTZ_FIRED_TRIGGERS] WITH NOCHECK ADD
+  CONSTRAINT [PK_QRTZ_FIRED_TRIGGERS] PRIMARY KEY  CLUSTERED
+    (
+      [SCHED_NAME],
+      [ENTRY_ID]
+    )  ON [PRIMARY]
+GO
+
+ALTER TABLE [dbo].[QRTZ_PAUSED_TRIGGER_GRPS] WITH NOCHECK ADD
+  CONSTRAINT [PK_QRTZ_PAUSED_TRIGGER_GRPS] PRIMARY KEY  CLUSTERED
+    (
+      [SCHED_NAME],
+      [TRIGGER_GROUP]
+    )  ON [PRIMARY]
+GO
+
+ALTER TABLE [dbo].[QRTZ_SCHEDULER_STATE] WITH NOCHECK ADD
+  CONSTRAINT [PK_QRTZ_SCHEDULER_STATE] PRIMARY KEY  CLUSTERED
+    (
+      [SCHED_NAME],
+      [INSTANCE_NAME]
+    )  ON [PRIMARY]
+GO
+
+ALTER TABLE [dbo].[QRTZ_LOCKS] WITH NOCHECK ADD
+  CONSTRAINT [PK_QRTZ_LOCKS] PRIMARY KEY  CLUSTERED
+    (
+      [SCHED_NAME],
+      [LOCK_NAME]
+    )  ON [PRIMARY]
+GO
+
+ALTER TABLE [dbo].[QRTZ_JOB_DETAILS] WITH NOCHECK ADD
+  CONSTRAINT [PK_QRTZ_JOB_DETAILS] PRIMARY KEY  CLUSTERED
+    (
+      [SCHED_NAME],
+      [JOB_NAME],
+      [JOB_GROUP]
+    )  ON [PRIMARY]
+GO
+
+ALTER TABLE [dbo].[QRTZ_SIMPLE_TRIGGERS] WITH NOCHECK ADD
+  CONSTRAINT [PK_QRTZ_SIMPLE_TRIGGERS] PRIMARY KEY  CLUSTERED
+    (
+      [SCHED_NAME],
+      [TRIGGER_NAME],
+      [TRIGGER_GROUP]
+    )  ON [PRIMARY]
+GO
+
+ALTER TABLE [dbo].[QRTZ_SIMPROP_TRIGGERS] WITH NOCHECK ADD
+  CONSTRAINT [PK_QRTZ_SIMPROP_TRIGGERS] PRIMARY KEY  CLUSTERED
+    (
+      [SCHED_NAME],
+      [TRIGGER_NAME],
+      [TRIGGER_GROUP]
+    )  ON [PRIMARY]
+GO
+
+ALTER TABLE [dbo].[QRTZ_TRIGGERS] WITH NOCHECK ADD
+  CONSTRAINT [PK_QRTZ_TRIGGERS] PRIMARY KEY  CLUSTERED
+    (
+      [SCHED_NAME],
+      [TRIGGER_NAME],
+      [TRIGGER_GROUP]
+    )  ON [PRIMARY]
+GO
+
+ALTER TABLE [dbo].[QRTZ_CRON_TRIGGERS] ADD
+  CONSTRAINT [FK_QRTZ_CRON_TRIGGERS_QRTZ_TRIGGERS] FOREIGN KEY
+    (
+      [SCHED_NAME],
+      [TRIGGER_NAME],
+      [TRIGGER_GROUP]
+    ) REFERENCES [dbo].[QRTZ_TRIGGERS] (
+    [SCHED_NAME],
+    [TRIGGER_NAME],
+    [TRIGGER_GROUP]
+  ) ON DELETE CASCADE
+GO
+
+ALTER TABLE [dbo].[QRTZ_SIMPLE_TRIGGERS] ADD
+  CONSTRAINT [FK_QRTZ_SIMPLE_TRIGGERS_QRTZ_TRIGGERS] FOREIGN KEY
+    (
+      [SCHED_NAME],
+      [TRIGGER_NAME],
+      [TRIGGER_GROUP]
+    ) REFERENCES [dbo].[QRTZ_TRIGGERS] (
+    [SCHED_NAME],
+    [TRIGGER_NAME],
+    [TRIGGER_GROUP]
+  ) ON DELETE CASCADE
+GO
+
+ALTER TABLE [dbo].[QRTZ_SIMPROP_TRIGGERS] ADD
+  CONSTRAINT [FK_QRTZ_SIMPROP_TRIGGERS_QRTZ_TRIGGERS] FOREIGN KEY
+    (
+      [SCHED_NAME],
+      [TRIGGER_NAME],
+      [TRIGGER_GROUP]
+    ) REFERENCES [dbo].[QRTZ_TRIGGERS] (
+    [SCHED_NAME],
+    [TRIGGER_NAME],
+    [TRIGGER_GROUP]
+  ) ON DELETE CASCADE
+GO
+
+ALTER TABLE [dbo].[QRTZ_TRIGGERS] ADD
+  CONSTRAINT [FK_QRTZ_TRIGGERS_QRTZ_JOB_DETAILS] FOREIGN KEY
+    (
+      [SCHED_NAME],
+      [JOB_NAME],
+      [JOB_GROUP]
+    ) REFERENCES [dbo].[QRTZ_JOB_DETAILS] (
+    [SCHED_NAME],
+    [JOB_NAME],
+    [JOB_GROUP]
+  )
+GO
+

+ 8 - 0
docker-compose.yml

@@ -0,0 +1,8 @@
+version: '2'
+services:
+  renren-fast:
+    image: renren/fast
+    ports:
+      - "8080:8080"
+    environment:
+      - spring.profiles.active=dev

+ 337 - 0
pom.xml

@@ -0,0 +1,337 @@
+<?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>io.renren</groupId>
+	<artifactId>renren-fast</artifactId>
+	<version>3.0.0</version>
+	<packaging>jar</packaging>
+	<description>renren-fast</description>
+
+	<parent>
+		<groupId>org.springframework.boot</groupId>
+		<artifactId>spring-boot-starter-parent</artifactId>
+		<version>2.6.6</version>
+	</parent>
+
+	<properties>
+		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+		<java.version>1.8</java.version>
+		<mybatisplus.version>3.3.1</mybatisplus.version>
+		<mysql.version>8.0.28</mysql.version>
+		<mssql.version>4.0</mssql.version>
+		<oracle.version>11.2.0.3</oracle.version>
+		<druid.version>1.1.13</druid.version>
+		<quartz.version>2.3.0</quartz.version>
+		<commons.lang.version>2.6</commons.lang.version>
+		<commons.fileupload.version>1.2.2</commons.fileupload.version>
+		<commons.io.version>2.5</commons.io.version>
+		<commons.codec.version>1.10</commons.codec.version>
+		<commons.configuration.version>1.10</commons.configuration.version>
+		<shiro.version>1.9.0</shiro.version>
+		<jwt.version>0.7.0</jwt.version>
+		<kaptcha.version>0.0.9</kaptcha.version>
+		<qiniu.version>7.2.23</qiniu.version>
+		<aliyun.oss.version>2.8.3</aliyun.oss.version>
+		<qcloud.cos.version>4.4</qcloud.cos.version>
+		<swagger.version>2.7.0</swagger.version>
+		<joda.time.version>2.9.9</joda.time.version>
+		<gson.version>2.8.6</gson.version>
+		<fastjson.version>1.2.79</fastjson.version>
+		<hutool.version>4.1.1</hutool.version>
+		<lombok.version>1.18.4</lombok.version>
+
+		<!--wagon plugin 配置-->
+		<service-path>/work/renren</service-path>
+		<pack-name>${project.artifactId}-${project.version}.jar</pack-name>
+		<remote-addr>192.168.1.10:22</remote-addr>
+		<remote-username>root</remote-username>
+		<remote-passwd>123456</remote-passwd>
+	</properties>
+
+	<dependencies>
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-test</artifactId>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-web</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-aop</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.springframework</groupId>
+			<artifactId>spring-context-support</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-data-redis</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-validation</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-configuration-processor</artifactId>
+			<optional>true</optional>
+		</dependency>
+		<!--<dependency>-->
+			<!--<groupId>org.springframework.boot</groupId>-->
+			<!--<artifactId>spring-boot-devtools</artifactId>-->
+			<!--<optional>true</optional>-->
+		<!--</dependency>-->
+		<dependency>
+			<groupId>com.baomidou</groupId>
+			<artifactId>mybatis-plus-boot-starter</artifactId>
+			<version>${mybatisplus.version}</version>
+			<exclusions>
+				<exclusion>
+					<groupId>com.baomidou</groupId>
+					<artifactId>mybatis-plus-generator</artifactId>
+				</exclusion>
+			</exclusions>
+		</dependency>
+		<dependency>
+			<groupId>mysql</groupId>
+			<artifactId>mysql-connector-java</artifactId>
+			<version>${mysql.version}</version>
+		</dependency>
+		 <!--oracle驱动-->
+		<dependency>
+			<groupId>com.oracle</groupId>
+			<artifactId>ojdbc6</artifactId>
+			<version>${oracle.version}</version>
+		</dependency>
+		 <!--mssql驱动-->
+		<dependency>
+			<groupId>com.microsoft.sqlserver</groupId>
+			<artifactId>sqljdbc4</artifactId>
+			<version>${mssql.version}</version>
+		</dependency>
+		 <!--postgresql驱动-->
+		<dependency>
+			<groupId>org.postgresql</groupId>
+			<artifactId>postgresql</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>com.alibaba</groupId>
+			<artifactId>druid-spring-boot-starter</artifactId>
+			<version>${druid.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.quartz-scheduler</groupId>
+			<artifactId>quartz</artifactId>
+			<version>${quartz.version}</version>
+			<exclusions>
+				<exclusion>
+					<groupId>com.mchange</groupId>
+					<artifactId>c3p0</artifactId>
+				</exclusion>
+			</exclusions>
+		</dependency>
+		<dependency>
+			<groupId>commons-lang</groupId>
+			<artifactId>commons-lang</artifactId>
+			<version>${commons.lang.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>commons-fileupload</groupId>
+			<artifactId>commons-fileupload</artifactId>
+			<version>${commons.fileupload.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>commons-io</groupId>
+			<artifactId>commons-io</artifactId>
+			<version>${commons.io.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>commons-codec</groupId>
+			<artifactId>commons-codec</artifactId>
+			<version>${commons.codec.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>commons-configuration</groupId>
+			<artifactId>commons-configuration</artifactId>
+			<version>${commons.configuration.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.shiro</groupId>
+			<artifactId>shiro-core</artifactId>
+			<version>${shiro.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.shiro</groupId>
+			<artifactId>shiro-spring</artifactId>
+			<version>${shiro.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>io.jsonwebtoken</groupId>
+			<artifactId>jjwt</artifactId>
+			<version>${jwt.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>com.github.axet</groupId>
+			<artifactId>kaptcha</artifactId>
+			<version>${kaptcha.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>io.springfox</groupId>
+			<artifactId>springfox-swagger2</artifactId>
+			<version>${swagger.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>io.springfox</groupId>
+			<artifactId>springfox-swagger-ui</artifactId>
+			<version>${swagger.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>com.qiniu</groupId>
+			<artifactId>qiniu-java-sdk</artifactId>
+			<version>${qiniu.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>com.aliyun.oss</groupId>
+			<artifactId>aliyun-sdk-oss</artifactId>
+			<version>${aliyun.oss.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>com.qcloud</groupId>
+			<artifactId>cos_api</artifactId>
+			<version>${qcloud.cos.version}</version>
+			<exclusions>
+				<exclusion>
+					<groupId>org.slf4j</groupId>
+					<artifactId>slf4j-log4j12</artifactId>
+				</exclusion>
+			</exclusions>
+		</dependency>
+		<dependency>
+			<groupId>joda-time</groupId>
+			<artifactId>joda-time</artifactId>
+			<version>${joda.time.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>com.google.code.gson</groupId>
+			<artifactId>gson</artifactId>
+			<version>${gson.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>com.alibaba</groupId>
+			<artifactId>fastjson</artifactId>
+			<version>${fastjson.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>cn.hutool</groupId>
+			<artifactId>hutool-all</artifactId>
+			<version>${hutool.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.projectlombok</groupId>
+			<artifactId>lombok</artifactId>
+			<version>${lombok.version}</version>
+		</dependency>
+	</dependencies>
+
+	<build>
+		<finalName>${project.artifactId}</finalName>
+		<extensions>
+			<extension>
+				<groupId>org.apache.maven.wagon</groupId>
+				<artifactId>wagon-ssh</artifactId>
+				<version>2.8</version>
+			</extension>
+		</extensions>
+		<plugins>
+			<plugin>
+				<groupId>org.springframework.boot</groupId>
+				<artifactId>spring-boot-maven-plugin</artifactId>
+				<configuration>
+				</configuration>
+			</plugin>
+			<!-- 跳过单元测试 -->
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-surefire-plugin</artifactId>
+				<configuration>
+					<skipTests>true</skipTests>
+				</configuration>
+			</plugin>
+			<plugin>
+				<groupId>org.codehaus.mojo</groupId>
+				<artifactId>wagon-maven-plugin</artifactId>
+				<version>1.0</version>
+				<configuration>
+					<fromFile>target/${pack-name}</fromFile>
+					<url><![CDATA[scp://${remote-username}:${remote-passwd}@${remote-addr}${service-path}]]></url>
+					<commands>
+						<!-- Kill Old Process -->
+						<command>kill -9 `ps -ef |grep ${project.artifactId}.jar|grep -v "grep" |awk '{print $2}'`</command>
+						<!-- Restart jar package,write result into renren.log -->
+						<command><![CDATA[nohup java -jar ${service-path}/${pack-name} --spring.profiles.active=test > ${service-path}/renren.log 2>&1 & ]]></command>
+						<command><![CDATA[netstat -nptl]]></command>
+						<command><![CDATA[ps -ef | grep java | grep -v grep]]></command>
+					</commands>
+					<!-- 运行命令 mvn clean package wagon:upload-single wagon:sshexec-->
+					<displayCommandOutputs>true</displayCommandOutputs>
+				</configuration>
+			</plugin>
+
+			<plugin>
+				<groupId>com.spotify</groupId>
+				<artifactId>docker-maven-plugin</artifactId>
+				<version>0.4.14</version>
+				<!--<executions>-->
+					<!--<execution>-->
+						<!--<phase>package</phase>-->
+						<!--<goals>-->
+							<!--<goal>build</goal>-->
+						<!--</goals>-->
+					<!--</execution>-->
+				<!--</executions>-->
+				<configuration>
+					<imageName>renren/fast</imageName>
+					<dockerDirectory>${project.basedir}</dockerDirectory>
+					<resources>
+						<resource>
+							<targetPath>/</targetPath>
+							<directory>${project.build.directory}</directory>
+							<include>${project.build.finalName}.jar</include>
+						</resource>
+					</resources>
+				</configuration>
+				<!-- 运行命令 mvn clean package docker:build 打包并生成docker镜像 -->
+			</plugin>
+		</plugins>
+	</build>
+
+	<repositories>
+		<repository>
+			<id>public</id>
+			<name>aliyun nexus</name>
+			<url>https://maven.aliyun.com/repository/public/</url>
+			<releases>
+				<enabled>true</enabled>
+			</releases>
+		</repository>
+	</repositories>
+	<pluginRepositories>
+		<pluginRepository>
+			<id>public</id>
+			<name>aliyun nexus</name>
+			<url>https://maven.aliyun.com/repository/public/</url>
+			<releases>
+				<enabled>true</enabled>
+			</releases>
+			<snapshots>
+				<enabled>false</enabled>
+			</snapshots>
+		</pluginRepository>
+	</pluginRepositories>
+
+</project>

+ 22 - 0
src/main/java/io/renren/RenrenApplication.java

@@ -0,0 +1,22 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+
+@SpringBootApplication
+public class RenrenApplication {
+
+	public static void main(String[] args) {
+		SpringApplication.run(RenrenApplication.class, args);
+	}
+
+}

+ 28 - 0
src/main/java/io/renren/common/annotation/SysLog.java

@@ -0,0 +1,28 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.common.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 系统日志注解
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface SysLog {
+
+	String value() default "";
+}

+ 46 - 0
src/main/java/io/renren/common/aspect/RedisAspect.java

@@ -0,0 +1,46 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.common.aspect;
+
+import io.renren.common.exception.RRException;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * Redis切面处理类
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+@Aspect
+@Configuration
+public class RedisAspect {
+    private Logger logger = LoggerFactory.getLogger(getClass());
+    //是否开启redis缓存  true开启   false关闭
+    @Value("${spring.redis.open: false}")
+    private boolean open;
+
+    @Around("execution(* io.renren.common.utils.RedisUtils.*(..))")
+    public Object around(ProceedingJoinPoint point) throws Throwable {
+        Object result = null;
+        if(open){
+            try{
+                result = point.proceed();
+            }catch (Exception e){
+                logger.error("redis error", e);
+                throw new RRException("Redis服务异常");
+            }
+        }
+        return result;
+    }
+}

+ 101 - 0
src/main/java/io/renren/common/aspect/SysLogAspect.java

@@ -0,0 +1,101 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.common.aspect;
+
+import com.google.gson.Gson;
+import io.renren.common.annotation.SysLog;
+import io.renren.common.utils.HttpContextUtils;
+import io.renren.common.utils.IPUtils;
+import io.renren.modules.sys.entity.SysLogEntity;
+import io.renren.modules.sys.entity.SysUserEntity;
+import io.renren.modules.sys.service.SysLogService;
+import org.apache.shiro.SecurityUtils;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Pointcut;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.http.HttpServletRequest;
+import java.lang.reflect.Method;
+import java.util.Date;
+
+
+/**
+ * 系统日志,切面处理类
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+@Aspect
+@Component
+public class SysLogAspect {
+	@Autowired
+	private SysLogService sysLogService;
+	
+	@Pointcut("@annotation(io.renren.common.annotation.SysLog)")
+	public void logPointCut() { 
+		
+	}
+
+	@Around("logPointCut()")
+	public Object around(ProceedingJoinPoint point) throws Throwable {
+		long beginTime = System.currentTimeMillis();
+		//执行方法
+		Object result = point.proceed();
+		//执行时长(毫秒)
+		long time = System.currentTimeMillis() - beginTime;
+
+		//保存日志
+		saveSysLog(point, time);
+
+		return result;
+	}
+
+	private void saveSysLog(ProceedingJoinPoint joinPoint, long time) {
+		MethodSignature signature = (MethodSignature) joinPoint.getSignature();
+		Method method = signature.getMethod();
+
+		SysLogEntity sysLog = new SysLogEntity();
+		SysLog syslog = method.getAnnotation(SysLog.class);
+		if(syslog != null){
+			//注解上的描述
+			sysLog.setOperation(syslog.value());
+		}
+
+		//请求的方法名
+		String className = joinPoint.getTarget().getClass().getName();
+		String methodName = signature.getName();
+		sysLog.setMethod(className + "." + methodName + "()");
+
+		//请求的参数
+		Object[] args = joinPoint.getArgs();
+		try{
+			String params = new Gson().toJson(args);
+			sysLog.setParams(params);
+		}catch (Exception e){
+
+		}
+
+		//获取request
+		HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
+		//设置IP地址
+		sysLog.setIp(IPUtils.getIpAddr(request));
+
+		//用户名
+		String username = ((SysUserEntity) SecurityUtils.getSubject().getPrincipal()).getUsername();
+		sysLog.setUsername(username);
+
+		sysLog.setTime(time);
+		sysLog.setCreateDate(new Date());
+		//保存系统日志
+		sysLogService.save(sysLog);
+	}
+}

+ 61 - 0
src/main/java/io/renren/common/exception/RRException.java

@@ -0,0 +1,61 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.common.exception;
+
+/**
+ * 自定义异常
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+public class RRException extends RuntimeException {
+	private static final long serialVersionUID = 1L;
+	
+    private String msg;
+    private int code = 500;
+    
+    public RRException(String msg) {
+		super(msg);
+		this.msg = msg;
+	}
+	
+	public RRException(String msg, Throwable e) {
+		super(msg, e);
+		this.msg = msg;
+	}
+	
+	public RRException(String msg, int code) {
+		super(msg);
+		this.msg = msg;
+		this.code = code;
+	}
+	
+	public RRException(String msg, int code, Throwable e) {
+		super(msg, e);
+		this.msg = msg;
+		this.code = code;
+	}
+
+	public String getMsg() {
+		return msg;
+	}
+
+	public void setMsg(String msg) {
+		this.msg = msg;
+	}
+
+	public int getCode() {
+		return code;
+	}
+
+	public void setCode(int code) {
+		this.code = code;
+	}
+	
+	
+}

+ 64 - 0
src/main/java/io/renren/common/exception/RRExceptionHandler.java

@@ -0,0 +1,64 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.common.exception;
+
+import io.renren.common.utils.R;
+import org.apache.shiro.authz.AuthorizationException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.dao.DuplicateKeyException;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+import org.springframework.web.servlet.NoHandlerFoundException;
+
+/**
+ * 异常处理器
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+@RestControllerAdvice
+public class RRExceptionHandler {
+	private Logger logger = LoggerFactory.getLogger(getClass());
+
+	/**
+	 * 处理自定义异常
+	 */
+	@ExceptionHandler(RRException.class)
+	public R handleRRException(RRException e){
+		R r = new R();
+		r.put("code", e.getCode());
+		r.put("msg", e.getMessage());
+
+		return r;
+	}
+
+	@ExceptionHandler(NoHandlerFoundException.class)
+	public R handlerNoFoundException(Exception e) {
+		logger.error(e.getMessage(), e);
+		return R.error(404, "路径不存在,请检查路径是否正确");
+	}
+
+	@ExceptionHandler(DuplicateKeyException.class)
+	public R handleDuplicateKeyException(DuplicateKeyException e){
+		logger.error(e.getMessage(), e);
+		return R.error("数据库中已存在该记录");
+	}
+
+	@ExceptionHandler(AuthorizationException.class)
+	public R handleAuthorizationException(AuthorizationException e){
+		logger.error(e.getMessage(), e);
+		return R.error("没有权限,请联系管理员授权");
+	}
+
+	@ExceptionHandler(Exception.class)
+	public R handleException(Exception e){
+		logger.error(e.getMessage(), e);
+		return R.error();
+	}
+}

+ 21 - 0
src/main/java/io/renren/common/utils/ConfigConstant.java

@@ -0,0 +1,21 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.common.utils;
+
+/**
+ * 系统参数相关Key
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+public class ConfigConstant {
+    /**
+     * 云存储配置KEY
+     */
+    public final static String CLOUD_STORAGE_CONFIG_KEY = "CLOUD_STORAGE_CONFIG_KEY";
+}

+ 152 - 0
src/main/java/io/renren/common/utils/Constant.java

@@ -0,0 +1,152 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ * <p>
+ * https://www.renren.io
+ * <p>
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.common.utils;
+
+import io.renren.common.validator.group.AliyunGroup;
+import io.renren.common.validator.group.QcloudGroup;
+import io.renren.common.validator.group.QiniuGroup;
+
+import java.util.Optional;
+import java.util.stream.Stream;
+
+/**
+ * 常量
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+public class Constant {
+    /**
+     * 超级管理员ID
+     */
+    public static final int SUPER_ADMIN = 1;
+    /**
+     * 当前页码
+     */
+    public static final String PAGE = "page";
+    /**
+     * 每页显示记录数
+     */
+    public static final String LIMIT = "limit";
+    /**
+     * 排序字段
+     */
+    public static final String ORDER_FIELD = "sidx";
+    /**
+     * 排序方式
+     */
+    public static final String ORDER = "order";
+    /**
+     * 升序
+     */
+    public static final String ASC = "asc";
+
+    /**
+     * 菜单类型
+     *
+     * @author chenshun
+     * @email sunlightcs@gmail.com
+     * @date 2016年11月15日 下午1:24:29
+     */
+    public enum MenuType {
+        /**
+         * 目录
+         */
+        CATALOG(0),
+        /**
+         * 菜单
+         */
+        MENU(1),
+        /**
+         * 按钮
+         */
+        BUTTON(2);
+
+        private int value;
+
+        MenuType(int value) {
+            this.value = value;
+        }
+
+        public int getValue() {
+            return value;
+        }
+    }
+
+    /**
+     * 定时任务状态
+     *
+     * @author chenshun
+     * @email sunlightcs@gmail.com
+     * @date 2016年12月3日 上午12:07:22
+     */
+    public enum ScheduleStatus {
+        /**
+         * 正常
+         */
+        NORMAL(0),
+        /**
+         * 暂停
+         */
+        PAUSE(1);
+
+        private int value;
+
+        ScheduleStatus(int value) {
+            this.value = value;
+        }
+
+        public int getValue() {
+            return value;
+        }
+    }
+
+    /**
+     * 云服务商
+     */
+    public enum CloudService {
+        /**
+         * 七牛云
+         */
+        QINIU(1, QiniuGroup.class),
+        /**
+         * 阿里云
+         */
+        ALIYUN(2, AliyunGroup.class),
+        /**
+         * 腾讯云
+         */
+        QCLOUD(3, QcloudGroup.class);
+
+        private int value;
+
+        private Class<?> validatorGroupClass;
+
+        CloudService(int value, Class<?> validatorGroupClass) {
+            this.value = value;
+            this.validatorGroupClass = validatorGroupClass;
+        }
+
+        public int getValue() {
+            return value;
+        }
+
+        public Class<?> getValidatorGroupClass() {
+            return this.validatorGroupClass;
+        }
+
+        public static CloudService getByValue(Integer value) {
+            Optional<CloudService> first = Stream.of(CloudService.values()).filter(cs -> value.equals(cs.value)).findFirst();
+            if (!first.isPresent()) {
+                throw new IllegalArgumentException("非法的枚举值:" + value);
+            }
+            return first.get();
+        }
+    }
+
+}

+ 166 - 0
src/main/java/io/renren/common/utils/DateUtils.java

@@ -0,0 +1,166 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.common.utils;
+
+import org.apache.commons.lang.StringUtils;
+import org.joda.time.DateTime;
+import org.joda.time.LocalDate;
+import org.joda.time.format.DateTimeFormat;
+import org.joda.time.format.DateTimeFormatter;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * 日期处理
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+public class DateUtils {
+	/** 时间格式(yyyy-MM-dd) */
+	public final static String DATE_PATTERN = "yyyy-MM-dd";
+	/** 时间格式(yyyy-MM-dd HH:mm:ss) */
+	public final static String DATE_TIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
+
+    /**
+     * 日期格式化 日期格式为:yyyy-MM-dd
+     * @param date  日期
+     * @return  返回yyyy-MM-dd格式日期
+     */
+	public static String format(Date date) {
+        return format(date, DATE_PATTERN);
+    }
+
+    /**
+     * 日期格式化 日期格式为:yyyy-MM-dd
+     * @param date  日期
+     * @param pattern  格式,如:DateUtils.DATE_TIME_PATTERN
+     * @return  返回yyyy-MM-dd格式日期
+     */
+    public static String format(Date date, String pattern) {
+        if(date != null){
+            SimpleDateFormat df = new SimpleDateFormat(pattern);
+            return df.format(date);
+        }
+        return null;
+    }
+
+    /**
+     * 字符串转换成日期
+     * @param strDate 日期字符串
+     * @param pattern 日期的格式,如:DateUtils.DATE_TIME_PATTERN
+     */
+    public static Date stringToDate(String strDate, String pattern) {
+        if (StringUtils.isBlank(strDate)){
+            return null;
+        }
+
+        DateTimeFormatter fmt = DateTimeFormat.forPattern(pattern);
+        return fmt.parseLocalDateTime(strDate).toDate();
+    }
+
+    /**
+     * 根据周数,获取开始日期、结束日期
+     * @param week  周期  0本周,-1上周,-2上上周,1下周,2下下周
+     * @return  返回date[0]开始日期、date[1]结束日期
+     */
+    public static Date[] getWeekStartAndEnd(int week) {
+        DateTime dateTime = new DateTime();
+        LocalDate date = new LocalDate(dateTime.plusWeeks(week));
+
+        date = date.dayOfWeek().withMinimumValue();
+        Date beginDate = date.toDate();
+        Date endDate = date.plusDays(6).toDate();
+        return new Date[]{beginDate, endDate};
+    }
+
+    /**
+     * 对日期的【秒】进行加/减
+     *
+     * @param date 日期
+     * @param seconds 秒数,负数为减
+     * @return 加/减几秒后的日期
+     */
+    public static Date addDateSeconds(Date date, int seconds) {
+        DateTime dateTime = new DateTime(date);
+        return dateTime.plusSeconds(seconds).toDate();
+    }
+
+    /**
+     * 对日期的【分钟】进行加/减
+     *
+     * @param date 日期
+     * @param minutes 分钟数,负数为减
+     * @return 加/减几分钟后的日期
+     */
+    public static Date addDateMinutes(Date date, int minutes) {
+        DateTime dateTime = new DateTime(date);
+        return dateTime.plusMinutes(minutes).toDate();
+    }
+
+    /**
+     * 对日期的【小时】进行加/减
+     *
+     * @param date 日期
+     * @param hours 小时数,负数为减
+     * @return 加/减几小时后的日期
+     */
+    public static Date addDateHours(Date date, int hours) {
+        DateTime dateTime = new DateTime(date);
+        return dateTime.plusHours(hours).toDate();
+    }
+
+    /**
+     * 对日期的【天】进行加/减
+     *
+     * @param date 日期
+     * @param days 天数,负数为减
+     * @return 加/减几天后的日期
+     */
+    public static Date addDateDays(Date date, int days) {
+        DateTime dateTime = new DateTime(date);
+        return dateTime.plusDays(days).toDate();
+    }
+
+    /**
+     * 对日期的【周】进行加/减
+     *
+     * @param date 日期
+     * @param weeks 周数,负数为减
+     * @return 加/减几周后的日期
+     */
+    public static Date addDateWeeks(Date date, int weeks) {
+        DateTime dateTime = new DateTime(date);
+        return dateTime.plusWeeks(weeks).toDate();
+    }
+
+    /**
+     * 对日期的【月】进行加/减
+     *
+     * @param date 日期
+     * @param months 月数,负数为减
+     * @return 加/减几月后的日期
+     */
+    public static Date addDateMonths(Date date, int months) {
+        DateTime dateTime = new DateTime(date);
+        return dateTime.plusMonths(months).toDate();
+    }
+
+    /**
+     * 对日期的【年】进行加/减
+     *
+     * @param date 日期
+     * @param years 年数,负数为减
+     * @return 加/减几年后的日期
+     */
+    public static Date addDateYears(Date date, int years) {
+        DateTime dateTime = new DateTime(date);
+        return dateTime.plusYears(years).toDate();
+    }
+}

+ 138 - 0
src/main/java/io/renren/common/utils/GsonUtil.java

@@ -0,0 +1,138 @@
+package io.renren.common.utils;
+
+
+import com.google.gson.*;
+import com.google.gson.internal.bind.ObjectTypeAdapter;
+import com.google.gson.reflect.TypeToken;
+
+import java.lang.reflect.Field;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Gson 处理
+ *
+ * @author chenrj
+ * @email 1412736935@qq.com
+ * @since 2021/7/22
+ */
+public class GsonUtil {
+
+    private static Gson gson;
+    private static Gson prettyGson;
+
+    static {
+        gson = getGson();
+        prettyGson = new GsonBuilder().setPrettyPrinting().create();
+    }
+
+    /**
+     * 私有方法
+     */
+    private GsonUtil() {
+
+    }
+
+    /**
+     * 使用自定义工厂方法取代 Gson 实例中的工厂方法
+     *
+     * @return
+     */
+    public static Gson getGson() {
+        Gson gson = new GsonBuilder().create();
+        try {
+            Field factories = Gson.class.getDeclaredField("factories");
+            factories.setAccessible(true);
+            Object o = factories.get(gson);
+            Class<?>[] declaredClasses = Collections.class.getDeclaredClasses();
+            for (Class c : declaredClasses) {
+                if ("java.util.Collections$UnmodifiableList".equals(c.getName())) {
+                    Field listField = c.getDeclaredField("list");
+                    listField.setAccessible(true);
+                    List<TypeAdapterFactory> list = (List<TypeAdapterFactory>) listField.get(o);
+                    int i = list.indexOf(ObjectTypeAdapter.FACTORY);
+                    list.set(i, MapTypeAdapter.FACTORY);
+                    break;
+                }
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return gson;
+    }
+
+    /**
+     * 将object对象转成json字符串
+     *
+     * @param object model 类
+     */
+    public static String toString(Object object) {
+        String result = null;
+        if (gson != null) {
+            result = gson.toJson(object);
+        }
+        return result;
+    }
+
+    /**
+     * 将object对象转成json字符串,带有格式化
+     *
+     * @param object model 类
+     */
+    public static String toPrettyString(Object object) {
+        String result = null;
+        if (prettyGson != null) {
+            result = prettyGson.toJson(object);
+        }
+        return result;
+    }
+
+
+    /**
+     * 将gsonString转成泛型bean
+     *
+     * @param content string 内容
+     * @param cls     类
+     */
+    public static <T> T convertJson(String content, Class<T> cls) {
+        T t = null;
+        if (gson != null) {
+            t = gson.fromJson(content, cls);
+        }
+        return t;
+    }
+
+    /**
+     * 转成map的
+     *
+     * @param content string 内容
+     * @return Map
+     */
+    public static <T> Map<String, T> convertJsonToMap(String content) {
+        Map<String, T> map = null;
+        if (gson != null) {
+            map = gson.fromJson(content, new TypeToken<Map<String, T>>() {
+            }.getType());
+        }
+        return map;
+    }
+
+    /**
+     * 转换为JsonObject
+     *
+     * @param content 文本内容
+     */
+    public static JsonObject convertJsonObject(String content) {
+        return JsonParser.parseString(content).getAsJsonObject();
+    }
+
+    /**
+     * 转换为JsonArray
+     *
+     * @param content 文本内容
+     */
+    public static JsonArray convertJsonArray(String content) {
+        return JsonParser.parseString(content).getAsJsonArray();
+    }
+}

+ 32 - 0
src/main/java/io/renren/common/utils/HttpContextUtils.java

@@ -0,0 +1,32 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.common.utils;
+
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.servlet.http.HttpServletRequest;
+
+public class HttpContextUtils {
+
+	public static HttpServletRequest getHttpServletRequest() {
+		return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
+	}
+
+	public static String getDomain(){
+		HttpServletRequest request = getHttpServletRequest();
+		StringBuffer url = request.getRequestURL();
+		return url.delete(url.length() - request.getRequestURI().length(), url.length()).toString();
+	}
+
+	public static String getOrigin(){
+		HttpServletRequest request = getHttpServletRequest();
+		return request.getHeader("Origin");
+	}
+}

+ 64 - 0
src/main/java/io/renren/common/utils/IPUtils.java

@@ -0,0 +1,64 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.common.utils;
+
+import com.alibaba.druid.util.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * IP地址
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+public class IPUtils {
+	private static Logger logger = LoggerFactory.getLogger(IPUtils.class);
+
+	/**
+	 * 获取IP地址
+	 * 
+	 * 使用Nginx等反向代理软件, 则不能通过request.getRemoteAddr()获取IP地址
+	 * 如果使用了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP地址,X-Forwarded-For中第一个非unknown的有效IP字符串,则为真实IP地址
+	 */
+	public static String getIpAddr(HttpServletRequest request) {
+    	String ip = null;
+        try {
+            ip = request.getHeader("x-forwarded-for");
+            if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
+                ip = request.getHeader("Proxy-Client-IP");
+            }
+            if (StringUtils.isEmpty(ip) || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+                ip = request.getHeader("WL-Proxy-Client-IP");
+            }
+            if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
+                ip = request.getHeader("HTTP_CLIENT_IP");
+            }
+            if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
+                ip = request.getHeader("HTTP_X_FORWARDED_FOR");
+            }
+            if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
+                ip = request.getRemoteAddr();
+            }
+        } catch (Exception e) {
+        	logger.error("IPUtils ERROR ", e);
+        }
+        
+//        //使用代理,则获取第一个IP地址
+//        if(StringUtils.isEmpty(ip) && ip.length() > 15) {
+//			if(ip.indexOf(",") > 0) {
+//				ip = ip.substring(0, ip.indexOf(","));
+//			}
+//		}
+        
+        return ip;
+    }
+	
+}

+ 104 - 0
src/main/java/io/renren/common/utils/MapTypeAdapter.java

@@ -0,0 +1,104 @@
+package io.renren.common.utils;
+
+import com.google.gson.Gson;
+import com.google.gson.TypeAdapter;
+import com.google.gson.TypeAdapterFactory;
+import com.google.gson.internal.LinkedTreeMap;
+import com.google.gson.internal.bind.ObjectTypeAdapter;
+import com.google.gson.reflect.TypeToken;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonToken;
+import com.google.gson.stream.JsonWriter;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 重写ObjectTypeAdapter方法
+ * 解决Gson转换导致int转为double问题等
+ *
+ * @author chenrj
+ * @email 1412736935@qq.com
+ * @since 2021/8/14
+ */
+public final class MapTypeAdapter extends TypeAdapter<Object> {
+    public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() {
+        @SuppressWarnings("unchecked")
+        @Override
+        public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
+            if (type.getRawType() == Object.class) {
+                return (TypeAdapter<T>) new MapTypeAdapter(gson);
+            }
+            return null;
+        }
+    };
+
+    private final Gson gson;
+
+    private MapTypeAdapter(Gson gson) {
+        this.gson = gson;
+    }
+
+    @Override
+    public Object read(JsonReader in) throws IOException {
+        JsonToken token = in.peek();
+        //判断字符串的实际类型
+        switch (token) {
+            case BEGIN_ARRAY:
+                List<Object> list = new ArrayList<>();
+                in.beginArray();
+                while (in.hasNext()) {
+                    list.add(read(in));
+                }
+                in.endArray();
+                return list;
+
+            case BEGIN_OBJECT:
+                Map<String, Object> map = new LinkedTreeMap<>();
+                in.beginObject();
+                while (in.hasNext()) {
+                    map.put(in.nextName(), read(in));
+                }
+                in.endObject();
+                return map;
+            case STRING:
+                return in.nextString();
+            case NUMBER:
+                String s = in.nextString();
+                if (s.contains(".")) {
+                    return Double.valueOf(s);
+                } else {
+                    try {
+                        return Integer.valueOf(s);
+                    } catch (Exception e) {
+                        return Long.valueOf(s);
+                    }
+                }
+            case BOOLEAN:
+                return in.nextBoolean();
+            case NULL:
+                in.nextNull();
+                return null;
+            default:
+                throw new IllegalStateException();
+        }
+    }
+
+    @Override
+    public void write(JsonWriter out, Object value) throws IOException {
+        if (value == null) {
+            out.nullValue();
+            return;
+        }
+        //noinspection unchecked
+        TypeAdapter<Object> typeAdapter = (TypeAdapter<Object>) gson.getAdapter(value.getClass());
+        if (typeAdapter instanceof ObjectTypeAdapter) {
+            out.beginObject();
+            out.endObject();
+            return;
+        }
+        typeAdapter.write(out, value);
+    }
+}

+ 26 - 0
src/main/java/io/renren/common/utils/MapUtils.java

@@ -0,0 +1,26 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.common.utils;
+
+import java.util.HashMap;
+
+
+/**
+ * Map工具类
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+public class MapUtils extends HashMap<String, Object> {
+
+    @Override
+    public MapUtils put(String key, Object value) {
+        super.put(key, value);
+        return this;
+    }
+}

+ 110 - 0
src/main/java/io/renren/common/utils/PageUtils.java

@@ -0,0 +1,110 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.common.utils;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 分页工具类
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+public class PageUtils implements Serializable {
+	private static final long serialVersionUID = 1L;
+	/**
+	 * 总记录数
+	 */
+	private int totalCount;
+	/**
+	 * 每页记录数
+	 */
+	private int pageSize;
+	/**
+	 * 总页数
+	 */
+	private int totalPage;
+	/**
+	 * 当前页数
+	 */
+	private int currPage;
+	/**
+	 * 列表数据
+	 */
+	private List<?> list;
+	
+	/**
+	 * 分页
+	 * @param list        列表数据
+	 * @param totalCount  总记录数
+	 * @param pageSize    每页记录数
+	 * @param currPage    当前页数
+	 */
+	public PageUtils(List<?> list, int totalCount, int pageSize, int currPage) {
+		this.list = list;
+		this.totalCount = totalCount;
+		this.pageSize = pageSize;
+		this.currPage = currPage;
+		this.totalPage = (int)Math.ceil((double)totalCount/pageSize);
+	}
+
+	/**
+	 * 分页
+	 */
+	public PageUtils(IPage<?> page) {
+		this.list = page.getRecords();
+		this.totalCount = (int)page.getTotal();
+		this.pageSize = (int)page.getSize();
+		this.currPage = (int)page.getCurrent();
+		this.totalPage = (int)page.getPages();
+	}
+
+	public int getTotalCount() {
+		return totalCount;
+	}
+
+	public void setTotalCount(int totalCount) {
+		this.totalCount = totalCount;
+	}
+
+	public int getPageSize() {
+		return pageSize;
+	}
+
+	public void setPageSize(int pageSize) {
+		this.pageSize = pageSize;
+	}
+
+	public int getTotalPage() {
+		return totalPage;
+	}
+
+	public void setTotalPage(int totalPage) {
+		this.totalPage = totalPage;
+	}
+
+	public int getCurrPage() {
+		return currPage;
+	}
+
+	public void setCurrPage(int currPage) {
+		this.currPage = currPage;
+	}
+
+	public List<?> getList() {
+		return list;
+	}
+
+	public void setList(List<?> list) {
+		this.list = list;
+	}
+	
+}

+ 77 - 0
src/main/java/io/renren/common/utils/Query.java

@@ -0,0 +1,77 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.common.utils;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.metadata.OrderItem;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import io.renren.common.xss.SQLFilter;
+import org.apache.commons.lang.StringUtils;
+
+import java.util.Map;
+
+/**
+ * 查询参数
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+public class Query<T> {
+
+    public IPage<T> getPage(Map<String, Object> params) {
+        return this.getPage(params, null, false);
+    }
+
+    public IPage<T> getPage(Map<String, Object> params, String defaultOrderField, boolean isAsc) {
+        //分页参数
+        long curPage = 1;
+        long limit = 10;
+
+        if(params.get(Constant.PAGE) != null){
+            curPage = Long.parseLong((String)params.get(Constant.PAGE));
+        }
+        if(params.get(Constant.LIMIT) != null){
+            limit = Long.parseLong((String)params.get(Constant.LIMIT));
+        }
+
+        //分页对象
+        Page<T> page = new Page<>(curPage, limit);
+
+        //分页参数
+        params.put(Constant.PAGE, page);
+
+        //排序字段
+        //防止SQL注入(因为sidx、order是通过拼接SQL实现排序的,会有SQL注入风险)
+        String orderField = SQLFilter.sqlInject((String)params.get(Constant.ORDER_FIELD));
+        String order = (String)params.get(Constant.ORDER);
+
+
+        //前端字段排序
+        if(StringUtils.isNotEmpty(orderField) && StringUtils.isNotEmpty(order)){
+            if(Constant.ASC.equalsIgnoreCase(order)) {
+                return  page.addOrder(OrderItem.asc(orderField));
+            }else {
+                return page.addOrder(OrderItem.desc(orderField));
+            }
+        }
+
+        //没有排序字段,则不排序
+        if(StringUtils.isBlank(defaultOrderField)){
+            return page;
+        }
+
+        //默认排序
+        if(isAsc) {
+            page.addOrder(OrderItem.asc(defaultOrderField));
+        }else {
+            page.addOrder(OrderItem.desc(defaultOrderField));
+        }
+
+        return page;
+    }
+}

+ 64 - 0
src/main/java/io/renren/common/utils/R.java

@@ -0,0 +1,64 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.common.utils;
+
+import org.apache.http.HttpStatus;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 返回数据
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+public class R extends HashMap<String, Object> {
+	private static final long serialVersionUID = 1L;
+	
+	public R() {
+		put("code", 0);
+		put("msg", "success");
+	}
+	
+	public static R error() {
+		return error(HttpStatus.SC_INTERNAL_SERVER_ERROR, "未知异常,请联系管理员");
+	}
+	
+	public static R error(String msg) {
+		return error(HttpStatus.SC_INTERNAL_SERVER_ERROR, msg);
+	}
+	
+	public static R error(int code, String msg) {
+		R r = new R();
+		r.put("code", code);
+		r.put("msg", msg);
+		return r;
+	}
+
+	public static R ok(String msg) {
+		R r = new R();
+		r.put("msg", msg);
+		return r;
+	}
+	
+	public static R ok(Map<String, Object> map) {
+		R r = new R();
+		r.putAll(map);
+		return r;
+	}
+	
+	public static R ok() {
+		return new R();
+	}
+
+	public R put(String key, Object value) {
+		super.put(key, value);
+		return this;
+	}
+}

+ 21 - 0
src/main/java/io/renren/common/utils/RedisKeys.java

@@ -0,0 +1,21 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.common.utils;
+
+/**
+ * Redis所有Keys
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+public class RedisKeys {
+
+    public static String getSysConfigKey(String key){
+        return "sys:config:" + key;
+    }
+}

+ 99 - 0
src/main/java/io/renren/common/utils/RedisUtils.java

@@ -0,0 +1,99 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.common.utils;
+
+import com.google.gson.Gson;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.*;
+import org.springframework.stereotype.Component;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Redis工具类
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+@Component
+public class RedisUtils {
+    @Autowired
+    private RedisTemplate<String, Object> redisTemplate;
+    @Autowired
+    private ValueOperations<String, String> valueOperations;
+    @Autowired
+    private HashOperations<String, String, Object> hashOperations;
+    @Autowired
+    private ListOperations<String, Object> listOperations;
+    @Autowired
+    private SetOperations<String, Object> setOperations;
+    @Autowired
+    private ZSetOperations<String, Object> zSetOperations;
+    /**  默认过期时长,单位:秒 */
+    public final static long DEFAULT_EXPIRE = 60 * 60 * 24;
+    /**  不设置过期时长 */
+    public final static long NOT_EXPIRE = -1;
+    private final static Gson gson = new Gson();
+
+    public void set(String key, Object value, long expire){
+        valueOperations.set(key, toJson(value));
+        if(expire != NOT_EXPIRE){
+            redisTemplate.expire(key, expire, TimeUnit.SECONDS);
+        }
+    }
+
+    public void set(String key, Object value){
+        set(key, value, DEFAULT_EXPIRE);
+    }
+
+    public <T> T get(String key, Class<T> clazz, long expire) {
+        String value = valueOperations.get(key);
+        if(expire != NOT_EXPIRE){
+            redisTemplate.expire(key, expire, TimeUnit.SECONDS);
+        }
+        return value == null ? null : fromJson(value, clazz);
+    }
+
+    public <T> T get(String key, Class<T> clazz) {
+        return get(key, clazz, NOT_EXPIRE);
+    }
+
+    public String get(String key, long expire) {
+        String value = valueOperations.get(key);
+        if(expire != NOT_EXPIRE){
+            redisTemplate.expire(key, expire, TimeUnit.SECONDS);
+        }
+        return value;
+    }
+
+    public String get(String key) {
+        return get(key, NOT_EXPIRE);
+    }
+
+    public void delete(String key) {
+        redisTemplate.delete(key);
+    }
+
+    /**
+     * Object转成JSON数据
+     */
+    private String toJson(Object object){
+        if(object instanceof Integer || object instanceof Long || object instanceof Float ||
+                object instanceof Double || object instanceof Boolean || object instanceof String){
+            return String.valueOf(object);
+        }
+        return gson.toJson(object);
+    }
+
+    /**
+     * JSON数据,转成Object
+     */
+    private <T> T fromJson(String json, Class<T> clazz){
+        return gson.fromJson(json, clazz);
+    }
+}

+ 61 - 0
src/main/java/io/renren/common/utils/ShiroUtils.java

@@ -0,0 +1,61 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.common.utils;
+
+import io.renren.common.exception.RRException;
+import io.renren.modules.sys.entity.SysUserEntity;
+import org.apache.shiro.SecurityUtils;
+import org.apache.shiro.session.Session;
+import org.apache.shiro.subject.Subject;
+
+/**
+ * Shiro工具类
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+public class ShiroUtils {
+
+	public static Session getSession() {
+		return SecurityUtils.getSubject().getSession();
+	}
+
+	public static Subject getSubject() {
+		return SecurityUtils.getSubject();
+	}
+
+	public static SysUserEntity getUserEntity() {
+		return (SysUserEntity)SecurityUtils.getSubject().getPrincipal();
+	}
+
+	public static Long getUserId() {
+		return getUserEntity().getUserId();
+	}
+	
+	public static void setSessionAttribute(Object key, Object value) {
+		getSession().setAttribute(key, value);
+	}
+
+	public static Object getSessionAttribute(Object key) {
+		return getSession().getAttribute(key);
+	}
+
+	public static boolean isLogin() {
+		return SecurityUtils.getSubject().getPrincipal() != null;
+	}
+
+	public static String getKaptcha(String key) {
+		Object kaptcha = getSessionAttribute(key);
+		if(kaptcha == null){
+			throw new RRException("验证码已失效");
+		}
+		getSession().removeAttribute(key);
+		return kaptcha.toString();
+	}
+
+}

+ 51 - 0
src/main/java/io/renren/common/utils/SpringContextUtils.java

@@ -0,0 +1,51 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.common.utils;
+
+import org.springframework.beans.BeansException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.stereotype.Component;
+
+/**
+ * Spring Context 工具类
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+@Component
+public class SpringContextUtils implements ApplicationContextAware {
+	public static ApplicationContext applicationContext; 
+
+	@Override
+	public void setApplicationContext(ApplicationContext applicationContext)
+			throws BeansException {
+		SpringContextUtils.applicationContext = applicationContext;
+	}
+
+	public static Object getBean(String name) {
+		return applicationContext.getBean(name);
+	}
+
+	public static <T> T getBean(String name, Class<T> requiredType) {
+		return applicationContext.getBean(name, requiredType);
+	}
+
+	public static boolean containsBean(String name) {
+		return applicationContext.containsBean(name);
+	}
+
+	public static boolean isSingleton(String name) {
+		return applicationContext.isSingleton(name);
+	}
+
+	public static Class<? extends Object> getType(String name) {
+		return applicationContext.getType(name);
+	}
+
+}

+ 32 - 0
src/main/java/io/renren/common/validator/Assert.java

@@ -0,0 +1,32 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.common.validator;
+
+import io.renren.common.exception.RRException;
+import org.apache.commons.lang.StringUtils;
+
+/**
+ * 数据校验
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+public abstract class Assert {
+
+    public static void isBlank(String str, String message) {
+        if (StringUtils.isBlank(str)) {
+            throw new RRException(message);
+        }
+    }
+
+    public static void isNull(Object object, String message) {
+        if (object == null) {
+            throw new RRException(message);
+        }
+    }
+}

+ 54 - 0
src/main/java/io/renren/common/validator/ValidatorUtils.java

@@ -0,0 +1,54 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ * <p>
+ * https://www.renren.io
+ * <p>
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.common.validator;
+
+import io.renren.common.exception.RRException;
+import io.renren.common.utils.Constant;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.Validation;
+import javax.validation.Validator;
+import java.util.Set;
+
+/**
+ * hibernate-validator校验工具类
+ *
+ * 参考文档:http://docs.jboss.org/hibernate/validator/5.4/reference/en-US/html_single/
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+public class ValidatorUtils {
+    private static Validator validator;
+
+    static {
+        validator = Validation.buildDefaultValidatorFactory().getValidator();
+    }
+
+    /**
+     * 校验对象
+     * @param object        待校验对象
+     * @param groups        待校验的组
+     * @throws RRException  校验不通过,则报RRException异常
+     */
+    public static void validateEntity(Object object, Class<?>... groups)
+            throws RRException {
+        Set<ConstraintViolation<Object>> constraintViolations = validator.validate(object, groups);
+        if (!constraintViolations.isEmpty()) {
+            StringBuilder msg = new StringBuilder();
+            for (ConstraintViolation<Object> constraint : constraintViolations) {
+                msg.append(constraint.getMessage()).append("<br>");
+            }
+            throw new RRException(msg.toString());
+        }
+    }
+
+    public static void validateEntity(Object object, Constant.CloudService type) {
+        validateEntity(object, type.getValidatorGroupClass());
+    }
+}

+ 17 - 0
src/main/java/io/renren/common/validator/group/AddGroup.java

@@ -0,0 +1,17 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.common.validator.group;
+
+/**
+ * 新增数据 Group
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+public interface AddGroup {
+}

+ 17 - 0
src/main/java/io/renren/common/validator/group/AliyunGroup.java

@@ -0,0 +1,17 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.common.validator.group;
+
+/**
+ * 阿里云
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+public interface AliyunGroup {
+}

+ 21 - 0
src/main/java/io/renren/common/validator/group/Group.java

@@ -0,0 +1,21 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.common.validator.group;
+
+import javax.validation.GroupSequence;
+
+/**
+ * 定义校验顺序,如果AddGroup组失败,则UpdateGroup组不会再校验
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+@GroupSequence({AddGroup.class, UpdateGroup.class})
+public interface Group {
+
+}

+ 17 - 0
src/main/java/io/renren/common/validator/group/QcloudGroup.java

@@ -0,0 +1,17 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.common.validator.group;
+
+/**
+ * 腾讯云
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+public interface QcloudGroup {
+}

+ 17 - 0
src/main/java/io/renren/common/validator/group/QiniuGroup.java

@@ -0,0 +1,17 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.common.validator.group;
+
+/**
+ * 七牛
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+public interface QiniuGroup {
+}

+ 19 - 0
src/main/java/io/renren/common/validator/group/UpdateGroup.java

@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.common.validator.group;
+
+/**
+ * 更新数据 Group
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+
+public interface UpdateGroup {
+
+}

+ 530 - 0
src/main/java/io/renren/common/xss/HTMLFilter.java

@@ -0,0 +1,530 @@
+package io.renren.common.xss;
+
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.logging.Logger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ *
+ * HTML filtering utility for protecting against XSS (Cross Site Scripting).
+ *
+ * This code is licensed LGPLv3
+ *
+ * This code is a Java port of the original work in PHP by Cal Hendersen.
+ * http://code.iamcal.com/php/lib_filter/
+ *
+ * The trickiest part of the translation was handling the differences in regex handling
+ * between PHP and Java.  These resources were helpful in the process:
+ *
+ * http://java.sun.com/j2se/1.4.2/docs/api/java/util/regex/Pattern.html
+ * http://us2.php.net/manual/en/reference.pcre.pattern.modifiers.php
+ * http://www.regular-expressions.info/modifiers.html
+ *
+ * A note on naming conventions: instance variables are prefixed with a "v"; global
+ * constants are in all caps.
+ *
+ * Sample use:
+ * String input = ...
+ * String clean = new HTMLFilter().filter( input );
+ *
+ * The class is not thread safe. Create a new instance if in doubt.
+ *
+ * If you find bugs or have suggestions on improvement (especially regarding
+ * performance), please contact us.  The latest version of this
+ * source, and our contact details, can be found at http://xss-html-filter.sf.net
+ *
+ * @author Joseph O'Connell
+ * @author Cal Hendersen
+ * @author Michael Semb Wever
+ */
+public final class HTMLFilter {
+
+    /** regex flag union representing /si modifiers in php **/
+    private static final int REGEX_FLAGS_SI = Pattern.CASE_INSENSITIVE | Pattern.DOTALL;
+    private static final Pattern P_COMMENTS = Pattern.compile("<!--(.*?)-->", Pattern.DOTALL);
+    private static final Pattern P_COMMENT = Pattern.compile("^!--(.*)--$", REGEX_FLAGS_SI);
+    private static final Pattern P_TAGS = Pattern.compile("<(.*?)>", Pattern.DOTALL);
+    private static final Pattern P_END_TAG = Pattern.compile("^/([a-z0-9]+)", REGEX_FLAGS_SI);
+    private static final Pattern P_START_TAG = Pattern.compile("^([a-z0-9]+)(.*?)(/?)$", REGEX_FLAGS_SI);
+    private static final Pattern P_QUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)=([\"'])(.*?)\\2", REGEX_FLAGS_SI);
+    private static final Pattern P_UNQUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)(=)([^\"\\s']+)", REGEX_FLAGS_SI);
+    private static final Pattern P_PROTOCOL = Pattern.compile("^([^:]+):", REGEX_FLAGS_SI);
+    private static final Pattern P_ENTITY = Pattern.compile("&#(\\d+);?");
+    private static final Pattern P_ENTITY_UNICODE = Pattern.compile("&#x([0-9a-f]+);?");
+    private static final Pattern P_ENCODE = Pattern.compile("%([0-9a-f]{2});?");
+    private static final Pattern P_VALID_ENTITIES = Pattern.compile("&([^&;]*)(?=(;|&|$))");
+    private static final Pattern P_VALID_QUOTES = Pattern.compile("(>|^)([^<]+?)(<|$)", Pattern.DOTALL);
+    private static final Pattern P_END_ARROW = Pattern.compile("^>");
+    private static final Pattern P_BODY_TO_END = Pattern.compile("<([^>]*?)(?=<|$)");
+    private static final Pattern P_XML_CONTENT = Pattern.compile("(^|>)([^<]*?)(?=>)");
+    private static final Pattern P_STRAY_LEFT_ARROW = Pattern.compile("<([^>]*?)(?=<|$)");
+    private static final Pattern P_STRAY_RIGHT_ARROW = Pattern.compile("(^|>)([^<]*?)(?=>)");
+    private static final Pattern P_AMP = Pattern.compile("&");
+    private static final Pattern P_QUOTE = Pattern.compile("<");
+    private static final Pattern P_LEFT_ARROW = Pattern.compile("<");
+    private static final Pattern P_RIGHT_ARROW = Pattern.compile(">");
+    private static final Pattern P_BOTH_ARROWS = Pattern.compile("<>");
+
+    // @xxx could grow large... maybe use sesat's ReferenceMap
+    private static final ConcurrentMap<String,Pattern> P_REMOVE_PAIR_BLANKS = new ConcurrentHashMap<String, Pattern>();
+    private static final ConcurrentMap<String,Pattern> P_REMOVE_SELF_BLANKS = new ConcurrentHashMap<String, Pattern>();
+
+    /** set of allowed html elements, along with allowed attributes for each element **/
+    private final Map<String, List<String>> vAllowed;
+    /** counts of open tags for each (allowable) html element **/
+    private final Map<String, Integer> vTagCounts = new HashMap<String, Integer>();
+
+    /** html elements which must always be self-closing (e.g. "<img />") **/
+    private final String[] vSelfClosingTags;
+    /** html elements which must always have separate opening and closing tags (e.g. "<b></b>") **/
+    private final String[] vNeedClosingTags;
+    /** set of disallowed html elements **/
+    private final String[] vDisallowed;
+    /** attributes which should be checked for valid protocols **/
+    private final String[] vProtocolAtts;
+    /** allowed protocols **/
+    private final String[] vAllowedProtocols;
+    /** tags which should be removed if they contain no content (e.g. "<b></b>" or "<b />") **/
+    private final String[] vRemoveBlanks;
+    /** entities allowed within html markup **/
+    private final String[] vAllowedEntities;
+    /** flag determining whether comments are allowed in input String. */
+    private final boolean stripComment;
+    private final boolean encodeQuotes;
+    private boolean vDebug = false;
+    /**
+     * flag determining whether to try to make tags when presented with "unbalanced"
+     * angle brackets (e.g. "<b text </b>" becomes "<b> text </b>").  If set to false,
+     * unbalanced angle brackets will be html escaped.
+     */
+    private final boolean alwaysMakeTags;
+
+    /** Default constructor.
+     *
+     */
+    public HTMLFilter() {
+        vAllowed = new HashMap<>();
+
+        final ArrayList<String> a_atts = new ArrayList<String>();
+        a_atts.add("href");
+        a_atts.add("target");
+        vAllowed.put("a", a_atts);
+
+        final ArrayList<String> img_atts = new ArrayList<String>();
+        img_atts.add("src");
+        img_atts.add("width");
+        img_atts.add("height");
+        img_atts.add("alt");
+        vAllowed.put("img", img_atts);
+
+        final ArrayList<String> no_atts = new ArrayList<String>();
+        vAllowed.put("b", no_atts);
+        vAllowed.put("strong", no_atts);
+        vAllowed.put("i", no_atts);
+        vAllowed.put("em", no_atts);
+
+        vSelfClosingTags = new String[]{"img"};
+        vNeedClosingTags = new String[]{"a", "b", "strong", "i", "em"};
+        vDisallowed = new String[]{};
+        vAllowedProtocols = new String[]{"http", "mailto", "https"}; // no ftp.
+        vProtocolAtts = new String[]{"src", "href"};
+        vRemoveBlanks = new String[]{"a", "b", "strong", "i", "em"};
+        vAllowedEntities = new String[]{"amp", "gt", "lt", "quot"};
+        stripComment = true;
+        encodeQuotes = true;
+        alwaysMakeTags = true;
+    }
+
+    /** Set debug flag to true. Otherwise use default settings. See the default constructor.
+     *
+     * @param debug turn debug on with a true argument
+     */
+    public HTMLFilter(final boolean debug) {
+        this();
+        vDebug = debug;
+
+    }
+
+    /** Map-parameter configurable constructor.
+     *
+     * @param conf map containing configuration. keys match field names.
+     */
+    public HTMLFilter(final Map<String,Object> conf) {
+
+        assert conf.containsKey("vAllowed") : "configuration requires vAllowed";
+        assert conf.containsKey("vSelfClosingTags") : "configuration requires vSelfClosingTags";
+        assert conf.containsKey("vNeedClosingTags") : "configuration requires vNeedClosingTags";
+        assert conf.containsKey("vDisallowed") : "configuration requires vDisallowed";
+        assert conf.containsKey("vAllowedProtocols") : "configuration requires vAllowedProtocols";
+        assert conf.containsKey("vProtocolAtts") : "configuration requires vProtocolAtts";
+        assert conf.containsKey("vRemoveBlanks") : "configuration requires vRemoveBlanks";
+        assert conf.containsKey("vAllowedEntities") : "configuration requires vAllowedEntities";
+
+        vAllowed = Collections.unmodifiableMap((HashMap<String, List<String>>) conf.get("vAllowed"));
+        vSelfClosingTags = (String[]) conf.get("vSelfClosingTags");
+        vNeedClosingTags = (String[]) conf.get("vNeedClosingTags");
+        vDisallowed = (String[]) conf.get("vDisallowed");
+        vAllowedProtocols = (String[]) conf.get("vAllowedProtocols");
+        vProtocolAtts = (String[]) conf.get("vProtocolAtts");
+        vRemoveBlanks = (String[]) conf.get("vRemoveBlanks");
+        vAllowedEntities = (String[]) conf.get("vAllowedEntities");
+        stripComment =  conf.containsKey("stripComment") ? (Boolean) conf.get("stripComment") : true;
+        encodeQuotes = conf.containsKey("encodeQuotes") ? (Boolean) conf.get("encodeQuotes") : true;
+        alwaysMakeTags = conf.containsKey("alwaysMakeTags") ? (Boolean) conf.get("alwaysMakeTags") : true;
+    }
+
+    private void reset() {
+        vTagCounts.clear();
+    }
+
+    private void debug(final String msg) {
+        if (vDebug) {
+            Logger.getAnonymousLogger().info(msg);
+        }
+    }
+
+    //---------------------------------------------------------------
+    // my versions of some PHP library functions
+    public static String chr(final int decimal) {
+        return String.valueOf((char) decimal);
+    }
+
+    public static String htmlSpecialChars(final String s) {
+        String result = s;
+        result = regexReplace(P_AMP, "&amp;", result);
+        result = regexReplace(P_QUOTE, "&quot;", result);
+        result = regexReplace(P_LEFT_ARROW, "&lt;", result);
+        result = regexReplace(P_RIGHT_ARROW, "&gt;", result);
+        return result;
+    }
+
+    //---------------------------------------------------------------
+    /**
+     * given a user submitted input String, filter out any invalid or restricted
+     * html.
+     *
+     * @param input text (i.e. submitted by a user) than may contain html
+     * @return "clean" version of input, with only valid, whitelisted html elements allowed
+     */
+    public String filter(final String input) {
+        reset();
+        String s = input;
+
+        debug("************************************************");
+        debug("              INPUT: " + input);
+
+        s = escapeComments(s);
+        debug("     escapeComments: " + s);
+
+        s = balanceHTML(s);
+        debug("        balanceHTML: " + s);
+
+        s = checkTags(s);
+        debug("          checkTags: " + s);
+
+        s = processRemoveBlanks(s);
+        debug("processRemoveBlanks: " + s);
+
+        s = validateEntities(s);
+        debug("    validateEntites: " + s);
+
+        debug("************************************************\n\n");
+        return s;
+    }
+
+    public boolean isAlwaysMakeTags(){
+        return alwaysMakeTags;
+    }
+
+    public boolean isStripComments(){
+        return stripComment;
+    }
+
+    private String escapeComments(final String s) {
+        final Matcher m = P_COMMENTS.matcher(s);
+        final StringBuffer buf = new StringBuffer();
+        if (m.find()) {
+            final String match = m.group(1); //(.*?)
+            m.appendReplacement(buf, Matcher.quoteReplacement("<!--" + htmlSpecialChars(match) + "-->"));
+        }
+        m.appendTail(buf);
+
+        return buf.toString();
+    }
+
+    private String balanceHTML(String s) {
+        if (alwaysMakeTags) {
+            //
+            // try and form html
+            //
+            s = regexReplace(P_END_ARROW, "", s);
+            s = regexReplace(P_BODY_TO_END, "<$1>", s);
+            s = regexReplace(P_XML_CONTENT, "$1<$2", s);
+
+        } else {
+            //
+            // escape stray brackets
+            //
+            s = regexReplace(P_STRAY_LEFT_ARROW, "&lt;$1", s);
+            s = regexReplace(P_STRAY_RIGHT_ARROW, "$1$2&gt;<", s);
+
+            //
+            // the last regexp causes '<>' entities to appear
+            // (we need to do a lookahead assertion so that the last bracket can
+            // be used in the next pass of the regexp)
+            //
+            s = regexReplace(P_BOTH_ARROWS, "", s);
+        }
+
+        return s;
+    }
+
+    private String checkTags(String s) {
+        Matcher m = P_TAGS.matcher(s);
+
+        final StringBuffer buf = new StringBuffer();
+        while (m.find()) {
+            String replaceStr = m.group(1);
+            replaceStr = processTag(replaceStr);
+            m.appendReplacement(buf, Matcher.quoteReplacement(replaceStr));
+        }
+        m.appendTail(buf);
+
+        s = buf.toString();
+
+        // these get tallied in processTag
+        // (remember to reset before subsequent calls to filter method)
+        for (String key : vTagCounts.keySet()) {
+            for (int ii = 0; ii < vTagCounts.get(key); ii++) {
+                s += "</" + key + ">";
+            }
+        }
+
+        return s;
+    }
+
+    private String processRemoveBlanks(final String s) {
+        String result = s;
+        for (String tag : vRemoveBlanks) {
+            if(!P_REMOVE_PAIR_BLANKS.containsKey(tag)){
+                P_REMOVE_PAIR_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?></" + tag + ">"));
+            }
+            result = regexReplace(P_REMOVE_PAIR_BLANKS.get(tag), "", result);
+            if(!P_REMOVE_SELF_BLANKS.containsKey(tag)){
+                P_REMOVE_SELF_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?/>"));
+            }
+            result = regexReplace(P_REMOVE_SELF_BLANKS.get(tag), "", result);
+        }
+
+        return result;
+    }
+
+    private static String regexReplace(final Pattern regex_pattern, final String replacement, final String s) {
+        Matcher m = regex_pattern.matcher(s);
+        return m.replaceAll(replacement);
+    }
+
+    private String processTag(final String s) {
+        // ending tags
+        Matcher m = P_END_TAG.matcher(s);
+        if (m.find()) {
+            final String name = m.group(1).toLowerCase();
+            if (allowed(name)) {
+                if (!inArray(name, vSelfClosingTags)) {
+                    if (vTagCounts.containsKey(name)) {
+                        vTagCounts.put(name, vTagCounts.get(name) - 1);
+                        return "</" + name + ">";
+                    }
+                }
+            }
+        }
+
+        // starting tags
+        m = P_START_TAG.matcher(s);
+        if (m.find()) {
+            final String name = m.group(1).toLowerCase();
+            final String body = m.group(2);
+            String ending = m.group(3);
+
+            //debug( "in a starting tag, name='" + name + "'; body='" + body + "'; ending='" + ending + "'" );
+            if (allowed(name)) {
+                String params = "";
+
+                final Matcher m2 = P_QUOTED_ATTRIBUTES.matcher(body);
+                final Matcher m3 = P_UNQUOTED_ATTRIBUTES.matcher(body);
+                final List<String> paramNames = new ArrayList<String>();
+                final List<String> paramValues = new ArrayList<String>();
+                while (m2.find()) {
+                    paramNames.add(m2.group(1)); //([a-z0-9]+)
+                    paramValues.add(m2.group(3)); //(.*?)
+                }
+                while (m3.find()) {
+                    paramNames.add(m3.group(1)); //([a-z0-9]+)
+                    paramValues.add(m3.group(3)); //([^\"\\s']+)
+                }
+
+                String paramName, paramValue;
+                for (int ii = 0; ii < paramNames.size(); ii++) {
+                    paramName = paramNames.get(ii).toLowerCase();
+                    paramValue = paramValues.get(ii);
+
+//          debug( "paramName='" + paramName + "'" );
+//          debug( "paramValue='" + paramValue + "'" );
+//          debug( "allowed? " + vAllowed.get( name ).contains( paramName ) );
+
+                    if (allowedAttribute(name, paramName)) {
+                        if (inArray(paramName, vProtocolAtts)) {
+                            paramValue = processParamProtocol(paramValue);
+                        }
+                        params += " " + paramName + "=\"" + paramValue + "\"";
+                    }
+                }
+
+                if (inArray(name, vSelfClosingTags)) {
+                    ending = " /";
+                }
+
+                if (inArray(name, vNeedClosingTags)) {
+                    ending = "";
+                }
+
+                if (ending == null || ending.length() < 1) {
+                    if (vTagCounts.containsKey(name)) {
+                        vTagCounts.put(name, vTagCounts.get(name) + 1);
+                    } else {
+                        vTagCounts.put(name, 1);
+                    }
+                } else {
+                    ending = " /";
+                }
+                return "<" + name + params + ending + ">";
+            } else {
+                return "";
+            }
+        }
+
+        // comments
+        m = P_COMMENT.matcher(s);
+        if (!stripComment && m.find()) {
+            return  "<" + m.group() + ">";
+        }
+
+        return "";
+    }
+
+    private String processParamProtocol(String s) {
+        s = decodeEntities(s);
+        final Matcher m = P_PROTOCOL.matcher(s);
+        if (m.find()) {
+            final String protocol = m.group(1);
+            if (!inArray(protocol, vAllowedProtocols)) {
+                // bad protocol, turn into local anchor link instead
+                s = "#" + s.substring(protocol.length() + 1, s.length());
+                if (s.startsWith("#//")) {
+                    s = "#" + s.substring(3, s.length());
+                }
+            }
+        }
+
+        return s;
+    }
+
+    private String decodeEntities(String s) {
+        StringBuffer buf = new StringBuffer();
+
+        Matcher m = P_ENTITY.matcher(s);
+        while (m.find()) {
+            final String match = m.group(1);
+            final int decimal = Integer.decode(match).intValue();
+            m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
+        }
+        m.appendTail(buf);
+        s = buf.toString();
+
+        buf = new StringBuffer();
+        m = P_ENTITY_UNICODE.matcher(s);
+        while (m.find()) {
+            final String match = m.group(1);
+            final int decimal = Integer.valueOf(match, 16).intValue();
+            m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
+        }
+        m.appendTail(buf);
+        s = buf.toString();
+
+        buf = new StringBuffer();
+        m = P_ENCODE.matcher(s);
+        while (m.find()) {
+            final String match = m.group(1);
+            final int decimal = Integer.valueOf(match, 16).intValue();
+            m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
+        }
+        m.appendTail(buf);
+        s = buf.toString();
+
+        s = validateEntities(s);
+        return s;
+    }
+
+    private String validateEntities(final String s) {
+        StringBuffer buf = new StringBuffer();
+
+        // validate entities throughout the string
+        Matcher m = P_VALID_ENTITIES.matcher(s);
+        while (m.find()) {
+            final String one = m.group(1); //([^&;]*)
+            final String two = m.group(2); //(?=(;|&|$))
+            m.appendReplacement(buf, Matcher.quoteReplacement(checkEntity(one, two)));
+        }
+        m.appendTail(buf);
+
+        return encodeQuotes(buf.toString());
+    }
+
+    private String encodeQuotes(final String s){
+        if(encodeQuotes){
+            StringBuffer buf = new StringBuffer();
+            Matcher m = P_VALID_QUOTES.matcher(s);
+            while (m.find()) {
+                final String one = m.group(1); //(>|^)
+                final String two = m.group(2); //([^<]+?)
+                final String three = m.group(3); //(<|$)
+                m.appendReplacement(buf, Matcher.quoteReplacement(one + regexReplace(P_QUOTE, "&quot;", two) + three));
+            }
+            m.appendTail(buf);
+            return buf.toString();
+        }else{
+            return s;
+        }
+    }
+
+    private String checkEntity(final String preamble, final String term) {
+
+        return ";".equals(term) && isValidEntity(preamble)
+                ? '&' + preamble
+                : "&amp;" + preamble;
+    }
+
+    private boolean isValidEntity(final String entity) {
+        return inArray(entity, vAllowedEntities);
+    }
+
+    private static boolean inArray(final String s, final String[] array) {
+        for (String item : array) {
+            if (item != null && item.equals(s)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean allowed(final String name) {
+        return (vAllowed.isEmpty() || vAllowed.containsKey(name)) && !inArray(name, vDisallowed);
+    }
+
+    private boolean allowedAttribute(final String name, final String paramName) {
+        return allowed(name) && (vAllowed.isEmpty() || vAllowed.get(name).contains(paramName));
+    }
+}

+ 50 - 0
src/main/java/io/renren/common/xss/SQLFilter.java

@@ -0,0 +1,50 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.common.xss;
+
+import io.renren.common.exception.RRException;
+import org.apache.commons.lang.StringUtils;
+
+/**
+ * SQL过滤
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+public class SQLFilter {
+
+    /**
+     * SQL注入过滤
+     * @param str  待验证的字符串
+     */
+    public static String sqlInject(String str){
+        if(StringUtils.isBlank(str)){
+            return null;
+        }
+        //去掉'|"|;|\字符
+        str = StringUtils.replace(str, "'", "");
+        str = StringUtils.replace(str, "\"", "");
+        str = StringUtils.replace(str, ";", "");
+        str = StringUtils.replace(str, "\\", "");
+
+        //转换成小写
+        str = str.toLowerCase();
+
+        //非法字符
+        String[] keywords = {"master", "truncate", "insert", "select", "delete", "update", "declare", "alter", "drop"};
+
+        //判断是否包含非法字符
+        for(String keyword : keywords){
+            if(str.indexOf(keyword) != -1){
+                throw new RRException("包含非法字符");
+            }
+        }
+
+        return str;
+    }
+}

+ 37 - 0
src/main/java/io/renren/common/xss/XssFilter.java

@@ -0,0 +1,37 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.common.xss;
+
+import javax.servlet.*;
+import javax.servlet.http.HttpServletRequest;
+import java.io.IOException;
+
+/**
+ * XSS过滤
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+public class XssFilter implements Filter {
+
+	@Override
+	public void init(FilterConfig config) throws ServletException {
+	}
+
+	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+            throws IOException, ServletException {
+		XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper(
+				(HttpServletRequest) request);
+		chain.doFilter(xssRequest, response);
+	}
+
+	@Override
+	public void destroy() {
+	}
+
+}

+ 147 - 0
src/main/java/io/renren/common/xss/XssHttpServletRequestWrapper.java

@@ -0,0 +1,147 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.common.xss;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang.StringUtils;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+
+import javax.servlet.ReadListener;
+import javax.servlet.ServletInputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * XSS过滤处理
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
+    //没被包装过的HttpServletRequest(特殊场景,需要自己过滤)
+    HttpServletRequest orgRequest;
+    //html过滤
+    private final static HTMLFilter htmlFilter = new HTMLFilter();
+
+    public XssHttpServletRequestWrapper(HttpServletRequest request) {
+        super(request);
+        orgRequest = request;
+    }
+
+    @Override
+    public ServletInputStream getInputStream() throws IOException {
+        //非json类型,直接返回
+        if(!MediaType.APPLICATION_JSON_VALUE.equalsIgnoreCase(super.getHeader(HttpHeaders.CONTENT_TYPE))){
+            return super.getInputStream();
+        }
+
+        //为空,直接返回
+        String json = IOUtils.toString(super.getInputStream(), "utf-8");
+        if (StringUtils.isBlank(json)) {
+            return super.getInputStream();
+        }
+
+        //xss过滤
+        json = xssEncode(json);
+        final ByteArrayInputStream bis = new ByteArrayInputStream(json.getBytes("utf-8"));
+        return new ServletInputStream() {
+            @Override
+            public boolean isFinished() {
+                return true;
+            }
+
+            @Override
+            public boolean isReady() {
+                return true;
+            }
+
+            @Override
+            public void setReadListener(ReadListener readListener) {
+
+            }
+
+            @Override
+            public int read() throws IOException {
+                return bis.read();
+            }
+        };
+    }
+
+    @Override
+    public String getParameter(String name) {
+        String value = super.getParameter(xssEncode(name));
+        if (StringUtils.isNotBlank(value)) {
+            value = xssEncode(value);
+        }
+        return value;
+    }
+
+    @Override
+    public String[] getParameterValues(String name) {
+        String[] parameters = super.getParameterValues(name);
+        if (parameters == null || parameters.length == 0) {
+            return null;
+        }
+
+        for (int i = 0; i < parameters.length; i++) {
+            parameters[i] = xssEncode(parameters[i]);
+        }
+        return parameters;
+    }
+
+    @Override
+    public Map<String,String[]> getParameterMap() {
+        Map<String,String[]> map = new LinkedHashMap<>();
+        Map<String,String[]> parameters = super.getParameterMap();
+        for (String key : parameters.keySet()) {
+            String[] values = parameters.get(key);
+            for (int i = 0; i < values.length; i++) {
+                values[i] = xssEncode(values[i]);
+            }
+            map.put(key, values);
+        }
+        return map;
+    }
+
+    @Override
+    public String getHeader(String name) {
+        String value = super.getHeader(xssEncode(name));
+        if (StringUtils.isNotBlank(value)) {
+            value = xssEncode(value);
+        }
+        return value;
+    }
+
+    private String xssEncode(String input) {
+        return htmlFilter.filter(input);
+    }
+
+    /**
+     * 获取最原始的request
+     */
+    public HttpServletRequest getOrgRequest() {
+        return orgRequest;
+    }
+
+    /**
+     * 获取最原始的request
+     */
+    public static HttpServletRequest getOrgRequest(HttpServletRequest request) {
+        if (request instanceof XssHttpServletRequestWrapper) {
+            return ((XssHttpServletRequestWrapper) request).getOrgRequest();
+        }
+
+        return request;
+    }
+
+}

+ 26 - 0
src/main/java/io/renren/config/CorsConfig.java

@@ -0,0 +1,26 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.config;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.CorsRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+@Configuration
+public class CorsConfig implements WebMvcConfigurer {
+
+    @Override
+    public void addCorsMappings(CorsRegistry registry) {
+        registry.addMapping("/**")
+            .allowedOriginPatterns("*")
+            .allowCredentials(true)
+            .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
+            .maxAge(3600);
+    }
+}

+ 49 - 0
src/main/java/io/renren/config/FilterConfig.java

@@ -0,0 +1,49 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.config;
+
+import io.renren.common.xss.XssFilter;
+import org.springframework.boot.web.servlet.FilterRegistrationBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.filter.DelegatingFilterProxy;
+
+import javax.servlet.DispatcherType;
+
+/**
+ * Filter配置
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+@Configuration
+public class FilterConfig {
+
+    @Bean
+    public FilterRegistrationBean shiroFilterRegistration() {
+        FilterRegistrationBean registration = new FilterRegistrationBean();
+        registration.setFilter(new DelegatingFilterProxy("shiroFilter"));
+        //该值缺省为false,表示生命周期由SpringApplicationContext管理,设置为true则表示由ServletContainer管理
+        registration.addInitParameter("targetFilterLifecycle", "true");
+        registration.setEnabled(true);
+        registration.setOrder(Integer.MAX_VALUE - 1);
+        registration.addUrlPatterns("/*");
+        return registration;
+    }
+
+    @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);
+        return registration;
+    }
+}

+ 39 - 0
src/main/java/io/renren/config/KaptchaConfig.java

@@ -0,0 +1,39 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.config;
+
+import com.google.code.kaptcha.impl.DefaultKaptcha;
+import com.google.code.kaptcha.util.Config;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import java.util.Properties;
+
+
+/**
+ * 生成验证码配置
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+@Configuration
+public class KaptchaConfig {
+
+    @Bean
+    public DefaultKaptcha producer() {
+        Properties properties = new Properties();
+        properties.put("kaptcha.border", "no");
+        properties.put("kaptcha.textproducer.font.color", "black");
+        properties.put("kaptcha.textproducer.char.space", "5");
+        properties.put("kaptcha.textproducer.font.names", "Arial,Courier,cmr10,宋体,楷体,微软雅黑");
+        Config config = new Config(properties);
+        DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
+        defaultKaptcha.setConfig(config);
+        return defaultKaptcha;
+    }
+}

+ 31 - 0
src/main/java/io/renren/config/MybatisPlusConfig.java

@@ -0,0 +1,31 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.config;
+
+import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * mybatis-plus配置
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+@Configuration
+public class MybatisPlusConfig {
+
+    /**
+     * 分页插件
+     */
+    @Bean
+    public PaginationInterceptor paginationInterceptor() {
+        return new PaginationInterceptor();
+    }
+
+}

+ 63 - 0
src/main/java/io/renren/config/RedisConfig.java

@@ -0,0 +1,63 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.config;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.data.redis.core.*;
+import org.springframework.data.redis.serializer.StringRedisSerializer;
+
+/**
+ * Redis配置
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+@Configuration
+public class RedisConfig {
+    @Autowired
+    private RedisConnectionFactory factory;
+
+    @Bean
+    public RedisTemplate<String, Object> redisTemplate() {
+        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
+        redisTemplate.setKeySerializer(new StringRedisSerializer());
+        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
+        redisTemplate.setHashValueSerializer(new StringRedisSerializer());
+        redisTemplate.setValueSerializer(new StringRedisSerializer());
+        redisTemplate.setConnectionFactory(factory);
+        return redisTemplate;
+    }
+
+    @Bean
+    public HashOperations<String, String, Object> hashOperations(RedisTemplate<String, Object> redisTemplate) {
+        return redisTemplate.opsForHash();
+    }
+
+    @Bean
+    public ValueOperations<String, String> valueOperations(RedisTemplate<String, String> redisTemplate) {
+        return redisTemplate.opsForValue();
+    }
+
+    @Bean
+    public ListOperations<String, Object> listOperations(RedisTemplate<String, Object> redisTemplate) {
+        return redisTemplate.opsForList();
+    }
+
+    @Bean
+    public SetOperations<String, Object> setOperations(RedisTemplate<String, Object> redisTemplate) {
+        return redisTemplate.opsForSet();
+    }
+
+    @Bean
+    public ZSetOperations<String, Object> zSetOperations(RedisTemplate<String, Object> redisTemplate) {
+        return redisTemplate.opsForZSet();
+    }
+}

+ 81 - 0
src/main/java/io/renren/config/ShiroConfig.java

@@ -0,0 +1,81 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.config;
+
+import io.renren.modules.sys.oauth2.OAuth2Filter;
+import io.renren.modules.sys.oauth2.OAuth2Realm;
+import org.apache.shiro.mgt.SecurityManager;
+import org.apache.shiro.spring.LifecycleBeanPostProcessor;
+import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
+import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
+import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import javax.servlet.Filter;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * Shiro配置
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+@Configuration
+public class ShiroConfig {
+
+    @Bean("securityManager")
+    public SecurityManager securityManager(OAuth2Realm oAuth2Realm) {
+        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
+        securityManager.setRealm(oAuth2Realm);
+        securityManager.setRememberMeManager(null);
+        return securityManager;
+    }
+
+    @Bean("shiroFilter")
+    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
+        ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
+        shiroFilter.setSecurityManager(securityManager);
+
+        //oauth过滤
+        Map<String, Filter> filters = new HashMap<>();
+        filters.put("oauth2", new OAuth2Filter());
+        shiroFilter.setFilters(filters);
+
+        Map<String, String> filterMap = new LinkedHashMap<>();
+        filterMap.put("/webjars/**", "anon");
+        filterMap.put("/druid/**", "anon");
+        filterMap.put("/app/**", "anon");
+        filterMap.put("/sys/login", "anon");
+        filterMap.put("/swagger/**", "anon");
+        filterMap.put("/v2/api-docs", "anon");
+        filterMap.put("/swagger-ui.html", "anon");
+        filterMap.put("/swagger-resources/**", "anon");
+        filterMap.put("/captcha.jpg", "anon");
+        filterMap.put("/aaa.txt", "anon");
+        filterMap.put("/**", "oauth2");
+        shiroFilter.setFilterChainDefinitionMap(filterMap);
+
+        return shiroFilter;
+    }
+
+    @Bean("lifecycleBeanPostProcessor")
+    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
+        return new LifecycleBeanPostProcessor();
+    }
+
+    @Bean
+    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
+        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
+        advisor.setSecurityManager(securityManager);
+        return advisor;
+    }
+
+}

+ 61 - 0
src/main/java/io/renren/config/SwaggerConfig.java

@@ -0,0 +1,61 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.config;
+
+import io.swagger.annotations.ApiOperation;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+import springfox.documentation.builders.ApiInfoBuilder;
+import springfox.documentation.builders.PathSelectors;
+import springfox.documentation.builders.RequestHandlerSelectors;
+import springfox.documentation.service.ApiInfo;
+import springfox.documentation.service.ApiKey;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spring.web.plugins.Docket;
+import springfox.documentation.swagger2.annotations.EnableSwagger2;
+
+import java.util.List;
+
+import static com.google.common.collect.Lists.newArrayList;
+
+@Configuration
+@EnableSwagger2
+public class SwaggerConfig implements WebMvcConfigurer {
+
+    @Bean
+    public Docket createRestApi() {
+        return new Docket(DocumentationType.SWAGGER_2)
+            .apiInfo(apiInfo())
+            .select()
+            //加了ApiOperation注解的类,才生成接口文档
+            .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
+            //包下的类,才生成接口文档
+            //.apis(RequestHandlerSelectors.basePackage("io.renren.controller"))
+            .paths(PathSelectors.any())
+            .build()
+            .securitySchemes(security());
+    }
+
+    private ApiInfo apiInfo() {
+        return new ApiInfoBuilder()
+            .title("人人开源")
+            .description("renren-fast文档")
+            .termsOfServiceUrl("https://www.renren.io")
+            .version("3.0.0")
+            .build();
+    }
+
+    private List<ApiKey> security() {
+        return newArrayList(
+            new ApiKey("token", "token", "header")
+        );
+    }
+
+}

+ 24 - 0
src/main/java/io/renren/datasource/annotation/DataSource.java

@@ -0,0 +1,24 @@
+/**
+ * Copyright (c) 2018 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.datasource.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * 多数据源注解
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+@Target({ElementType.METHOD, ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Inherited
+public @interface DataSource {
+    String value() default "";
+}

+ 71 - 0
src/main/java/io/renren/datasource/aspect/DataSourceAspect.java

@@ -0,0 +1,71 @@
+/**
+ * Copyright (c) 2018 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.datasource.aspect;
+
+
+import io.renren.datasource.annotation.DataSource;
+import io.renren.datasource.config.DynamicContextHolder;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Pointcut;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.core.Ordered;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
+
+import java.lang.reflect.Method;
+
+/**
+ * 多数据源,切面处理类
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+@Aspect
+@Component
+@Order(Ordered.HIGHEST_PRECEDENCE)
+public class DataSourceAspect {
+    protected Logger logger = LoggerFactory.getLogger(getClass());
+
+    @Pointcut("@annotation(io.renren.datasource.annotation.DataSource) " +
+            "|| @within(io.renren.datasource.annotation.DataSource)")
+    public void dataSourcePointCut() {
+
+    }
+
+    @Around("dataSourcePointCut()")
+    public Object around(ProceedingJoinPoint point) throws Throwable {
+        MethodSignature signature = (MethodSignature) point.getSignature();
+        Class targetClass = point.getTarget().getClass();
+        Method method = signature.getMethod();
+
+        DataSource targetDataSource = (DataSource)targetClass.getAnnotation(DataSource.class);
+        DataSource methodDataSource = method.getAnnotation(DataSource.class);
+        if(targetDataSource != null || methodDataSource != null){
+            String value;
+            if(methodDataSource != null){
+                value = methodDataSource.value();
+            }else {
+                value = targetDataSource.value();
+            }
+
+            DynamicContextHolder.push(value);
+            logger.debug("set datasource is {}", value);
+        }
+
+        try {
+            return point.proceed();
+        } finally {
+            DynamicContextHolder.poll();
+            logger.debug("clean datasource");
+        }
+    }
+}

+ 57 - 0
src/main/java/io/renren/datasource/config/DynamicContextHolder.java

@@ -0,0 +1,57 @@
+/**
+ * Copyright (c) 2018 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.datasource.config;
+
+import java.util.ArrayDeque;
+import java.util.Deque;
+
+/**
+ * 多数据源上下文
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+public class DynamicContextHolder {
+    @SuppressWarnings("unchecked")
+    private static final ThreadLocal<Deque<String>> CONTEXT_HOLDER = new ThreadLocal() {
+        @Override
+        protected Object initialValue() {
+            return new ArrayDeque();
+        }
+    };
+
+    /**
+     * 获得当前线程数据源
+     *
+     * @return 数据源名称
+     */
+    public static String peek() {
+        return CONTEXT_HOLDER.get().peek();
+    }
+
+    /**
+     * 设置当前线程数据源
+     *
+     * @param dataSource 数据源名称
+     */
+    public static void push(String dataSource) {
+        CONTEXT_HOLDER.get().push(dataSource);
+    }
+
+    /**
+     * 清空当前线程数据源
+     */
+    public static void poll() {
+        Deque<String> deque = CONTEXT_HOLDER.get();
+        deque.poll();
+        if (deque.isEmpty()) {
+            CONTEXT_HOLDER.remove();
+        }
+    }
+
+}

+ 25 - 0
src/main/java/io/renren/datasource/config/DynamicDataSource.java

@@ -0,0 +1,25 @@
+/**
+ * Copyright (c) 2018 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.datasource.config;
+
+import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
+
+/**
+ * 多数据源
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+public class DynamicDataSource extends AbstractRoutingDataSource {
+
+    @Override
+    protected Object determineCurrentLookupKey() {
+        return DynamicContextHolder.peek();
+    }
+
+}

+ 63 - 0
src/main/java/io/renren/datasource/config/DynamicDataSourceConfig.java

@@ -0,0 +1,63 @@
+/**
+ * Copyright (c) 2018 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.datasource.config;
+
+import com.alibaba.druid.pool.DruidDataSource;
+import io.renren.datasource.properties.DataSourceProperties;
+import io.renren.datasource.properties.DynamicDataSourceProperties;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 配置多数据源
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+@Configuration
+@EnableConfigurationProperties(DynamicDataSourceProperties.class)
+public class DynamicDataSourceConfig {
+    @Autowired
+    private DynamicDataSourceProperties properties;
+
+    @Bean
+    @ConfigurationProperties(prefix = "spring.datasource.druid")
+    public DataSourceProperties dataSourceProperties() {
+        return new DataSourceProperties();
+    }
+
+    @Bean
+    public DynamicDataSource dynamicDataSource(DataSourceProperties dataSourceProperties) {
+        DynamicDataSource dynamicDataSource = new DynamicDataSource();
+        dynamicDataSource.setTargetDataSources(getDynamicDataSource());
+
+        //默认数据源
+        DruidDataSource defaultDataSource = DynamicDataSourceFactory.buildDruidDataSource(dataSourceProperties);
+        dynamicDataSource.setDefaultTargetDataSource(defaultDataSource);
+
+        return dynamicDataSource;
+    }
+
+    private Map<Object, Object> getDynamicDataSource(){
+        Map<String, DataSourceProperties> dataSourcePropertiesMap = properties.getDatasource();
+        Map<Object, Object> targetDataSources = new HashMap<>(dataSourcePropertiesMap.size());
+        dataSourcePropertiesMap.forEach((k, v) -> {
+            DruidDataSource druidDataSource = DynamicDataSourceFactory.buildDruidDataSource(v);
+            targetDataSources.put(k, druidDataSource);
+        });
+
+        return targetDataSources;
+    }
+
+}

+ 54 - 0
src/main/java/io/renren/datasource/config/DynamicDataSourceFactory.java

@@ -0,0 +1,54 @@
+/**
+ * Copyright (c) 2018 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.datasource.config;
+
+import com.alibaba.druid.pool.DruidDataSource;
+import io.renren.datasource.properties.DataSourceProperties;
+
+import java.sql.SQLException;
+
+/**
+ * DruidDataSource
+ *
+ * @author Mark sunlightcs@gmail.com
+ * @since 1.0.0
+ */
+public class DynamicDataSourceFactory {
+
+    public static DruidDataSource buildDruidDataSource(DataSourceProperties properties) {
+        DruidDataSource druidDataSource = new DruidDataSource();
+        druidDataSource.setDriverClassName(properties.getDriverClassName());
+        druidDataSource.setUrl(properties.getUrl());
+        druidDataSource.setUsername(properties.getUsername());
+        druidDataSource.setPassword(properties.getPassword());
+
+        druidDataSource.setInitialSize(properties.getInitialSize());
+        druidDataSource.setMaxActive(properties.getMaxActive());
+        druidDataSource.setMinIdle(properties.getMinIdle());
+        druidDataSource.setMaxWait(properties.getMaxWait());
+        druidDataSource.setTimeBetweenEvictionRunsMillis(properties.getTimeBetweenEvictionRunsMillis());
+        druidDataSource.setMinEvictableIdleTimeMillis(properties.getMinEvictableIdleTimeMillis());
+        druidDataSource.setMaxEvictableIdleTimeMillis(properties.getMaxEvictableIdleTimeMillis());
+        druidDataSource.setValidationQuery(properties.getValidationQuery());
+        druidDataSource.setValidationQueryTimeout(properties.getValidationQueryTimeout());
+        druidDataSource.setTestOnBorrow(properties.isTestOnBorrow());
+        druidDataSource.setTestOnReturn(properties.isTestOnReturn());
+        druidDataSource.setPoolPreparedStatements(properties.isPoolPreparedStatements());
+        druidDataSource.setMaxOpenPreparedStatements(properties.getMaxOpenPreparedStatements());
+        druidDataSource.setSharePreparedStatements(properties.isSharePreparedStatements());
+
+        try {
+            druidDataSource.setFilters(properties.getFilters());
+            druidDataSource.init();
+        } catch (SQLException e) {
+            e.printStackTrace();
+        }
+        return druidDataSource;
+    }
+}

+ 202 - 0
src/main/java/io/renren/datasource/properties/DataSourceProperties.java

@@ -0,0 +1,202 @@
+/**
+ * Copyright (c) 2018 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.datasource.properties;
+
+/**
+ * 多数据源属性
+ *
+ * @author Mark sunlightcs@gmail.com
+ * @since 1.0.0
+ */
+public class DataSourceProperties {
+    private String driverClassName;
+    private String url;
+    private String username;
+    private String password;
+
+    /**
+     * Druid默认参数
+     */
+    private int initialSize = 2;
+    private int maxActive = 10;
+    private int minIdle = -1;
+    private long maxWait = 60 * 1000L;
+    private long timeBetweenEvictionRunsMillis = 60 * 1000L;
+    private long minEvictableIdleTimeMillis = 1000L * 60L * 30L;
+    private long maxEvictableIdleTimeMillis = 1000L * 60L * 60L * 7;
+    private String validationQuery = "select 1";
+    private int validationQueryTimeout = -1;
+    private boolean testOnBorrow = false;
+    private boolean testOnReturn = false;
+    private boolean testWhileIdle = true;
+    private boolean poolPreparedStatements = false;
+    private int maxOpenPreparedStatements = -1;
+    private boolean sharePreparedStatements = false;
+    private String filters = "stat,wall";
+
+    public String getDriverClassName() {
+        return driverClassName;
+    }
+
+    public void setDriverClassName(String driverClassName) {
+        this.driverClassName = driverClassName;
+    }
+
+    public String getUrl() {
+        return url;
+    }
+
+    public void setUrl(String url) {
+        this.url = url;
+    }
+
+    public String getUsername() {
+        return username;
+    }
+
+    public void setUsername(String username) {
+        this.username = username;
+    }
+
+    public String getPassword() {
+        return password;
+    }
+
+    public void setPassword(String password) {
+        this.password = password;
+    }
+
+    public int getInitialSize() {
+        return initialSize;
+    }
+
+    public void setInitialSize(int initialSize) {
+        this.initialSize = initialSize;
+    }
+
+    public int getMaxActive() {
+        return maxActive;
+    }
+
+    public void setMaxActive(int maxActive) {
+        this.maxActive = maxActive;
+    }
+
+    public int getMinIdle() {
+        return minIdle;
+    }
+
+    public void setMinIdle(int minIdle) {
+        this.minIdle = minIdle;
+    }
+
+    public long getMaxWait() {
+        return maxWait;
+    }
+
+    public void setMaxWait(long maxWait) {
+        this.maxWait = maxWait;
+    }
+
+    public long getTimeBetweenEvictionRunsMillis() {
+        return timeBetweenEvictionRunsMillis;
+    }
+
+    public void setTimeBetweenEvictionRunsMillis(long timeBetweenEvictionRunsMillis) {
+        this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
+    }
+
+    public long getMinEvictableIdleTimeMillis() {
+        return minEvictableIdleTimeMillis;
+    }
+
+    public void setMinEvictableIdleTimeMillis(long minEvictableIdleTimeMillis) {
+        this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
+    }
+
+    public long getMaxEvictableIdleTimeMillis() {
+        return maxEvictableIdleTimeMillis;
+    }
+
+    public void setMaxEvictableIdleTimeMillis(long maxEvictableIdleTimeMillis) {
+        this.maxEvictableIdleTimeMillis = maxEvictableIdleTimeMillis;
+    }
+
+    public String getValidationQuery() {
+        return validationQuery;
+    }
+
+    public void setValidationQuery(String validationQuery) {
+        this.validationQuery = validationQuery;
+    }
+
+    public int getValidationQueryTimeout() {
+        return validationQueryTimeout;
+    }
+
+    public void setValidationQueryTimeout(int validationQueryTimeout) {
+        this.validationQueryTimeout = validationQueryTimeout;
+    }
+
+    public boolean isTestOnBorrow() {
+        return testOnBorrow;
+    }
+
+    public void setTestOnBorrow(boolean testOnBorrow) {
+        this.testOnBorrow = testOnBorrow;
+    }
+
+    public boolean isTestOnReturn() {
+        return testOnReturn;
+    }
+
+    public void setTestOnReturn(boolean testOnReturn) {
+        this.testOnReturn = testOnReturn;
+    }
+
+    public boolean isTestWhileIdle() {
+        return testWhileIdle;
+    }
+
+    public void setTestWhileIdle(boolean testWhileIdle) {
+        this.testWhileIdle = testWhileIdle;
+    }
+
+    public boolean isPoolPreparedStatements() {
+        return poolPreparedStatements;
+    }
+
+    public void setPoolPreparedStatements(boolean poolPreparedStatements) {
+        this.poolPreparedStatements = poolPreparedStatements;
+    }
+
+    public int getMaxOpenPreparedStatements() {
+        return maxOpenPreparedStatements;
+    }
+
+    public void setMaxOpenPreparedStatements(int maxOpenPreparedStatements) {
+        this.maxOpenPreparedStatements = maxOpenPreparedStatements;
+    }
+
+    public boolean isSharePreparedStatements() {
+        return sharePreparedStatements;
+    }
+
+    public void setSharePreparedStatements(boolean sharePreparedStatements) {
+        this.sharePreparedStatements = sharePreparedStatements;
+    }
+
+    public String getFilters() {
+        return filters;
+    }
+
+    public void setFilters(String filters) {
+        this.filters = filters;
+    }
+}

+ 33 - 0
src/main/java/io/renren/datasource/properties/DynamicDataSourceProperties.java

@@ -0,0 +1,33 @@
+/**
+ * Copyright (c) 2018 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.datasource.properties;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * 多数据源属性
+ *
+ * @author Mark sunlightcs@gmail.com
+ * @since 1.0.0
+ */
+@ConfigurationProperties(prefix = "dynamic")
+public class DynamicDataSourceProperties {
+    private Map<String, DataSourceProperties> datasource = new LinkedHashMap<>();
+
+    public Map<String, DataSourceProperties> getDatasource() {
+        return datasource;
+    }
+
+    public void setDatasource(Map<String, DataSourceProperties> datasource) {
+        this.datasource = datasource;
+    }
+}

+ 22 - 0
src/main/java/io/renren/modules/app/annotation/Login.java

@@ -0,0 +1,22 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.modules.app.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * app登录效验
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface Login {
+}

+ 25 - 0
src/main/java/io/renren/modules/app/annotation/LoginUser.java

@@ -0,0 +1,25 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.modules.app.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 登录用户信息
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+@Target(ElementType.PARAMETER)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface LoginUser {
+
+}

+ 42 - 0
src/main/java/io/renren/modules/app/config/WebMvcConfig.java

@@ -0,0 +1,42 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.modules.app.config;
+
+import io.renren.modules.app.interceptor.AuthorizationInterceptor;
+import io.renren.modules.app.resolver.LoginUserHandlerMethodArgumentResolver;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.method.support.HandlerMethodArgumentResolver;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+import java.util.List;
+
+/**
+ * MVC配置
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+@Configuration
+public class WebMvcConfig implements WebMvcConfigurer {
+    @Autowired
+    private AuthorizationInterceptor authorizationInterceptor;
+    @Autowired
+    private LoginUserHandlerMethodArgumentResolver loginUserHandlerMethodArgumentResolver;
+
+    @Override
+    public void addInterceptors(InterceptorRegistry registry) {
+        registry.addInterceptor(authorizationInterceptor).addPathPatterns("/app/**");
+    }
+
+    @Override
+    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
+        argumentResolvers.add(loginUserHandlerMethodArgumentResolver);
+    }
+}

+ 64 - 0
src/main/java/io/renren/modules/app/controller/AppLoginController.java

@@ -0,0 +1,64 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.modules.app.controller;
+
+
+import io.renren.common.utils.R;
+import io.renren.common.validator.ValidatorUtils;
+import io.renren.modules.app.form.LoginForm;
+import io.renren.modules.app.service.UserService;
+import io.renren.modules.app.utils.JwtUtils;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * APP登录授权
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+@RestController
+@RequestMapping("/app")
+@Api("APP登录接口")
+public class AppLoginController {
+    @Autowired
+    private UserService userService;
+    @Autowired
+    private JwtUtils jwtUtils;
+
+    /**
+     * 登录
+     */
+    @PostMapping("login")
+    @ApiOperation("登录")
+    public R login(@RequestBody LoginForm form){
+        //表单校验
+        ValidatorUtils.validateEntity(form);
+
+        //用户登录
+        long userId = userService.login(form);
+
+        //生成token
+        String token = jwtUtils.generateToken(userId);
+
+        Map<String, Object> map = new HashMap<>();
+        map.put("token", token);
+        map.put("expire", jwtUtils.getExpire());
+
+        return R.ok(map);
+    }
+
+}

+ 55 - 0
src/main/java/io/renren/modules/app/controller/AppRegisterController.java

@@ -0,0 +1,55 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.modules.app.controller;
+
+
+import io.renren.common.utils.R;
+import io.renren.common.validator.ValidatorUtils;
+import io.renren.modules.app.entity.UserEntity;
+import io.renren.modules.app.form.RegisterForm;
+import io.renren.modules.app.service.UserService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.apache.commons.codec.digest.DigestUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.Date;
+
+/**
+ * 注册
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+@RestController
+@RequestMapping("/app")
+@Api("APP注册接口")
+public class AppRegisterController {
+    @Autowired
+    private UserService userService;
+
+    @PostMapping("register")
+    @ApiOperation("注册")
+    public R register(@RequestBody RegisterForm form){
+        //表单校验
+        ValidatorUtils.validateEntity(form);
+
+        UserEntity user = new UserEntity();
+        user.setMobile(form.getMobile());
+        user.setUsername(form.getMobile());
+        user.setPassword(DigestUtils.sha256Hex(form.getPassword()));
+        user.setCreateTime(new Date());
+        userService.save(user);
+
+        return R.ok();
+    }
+}

+ 53 - 0
src/main/java/io/renren/modules/app/controller/AppTestController.java

@@ -0,0 +1,53 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.modules.app.controller;
+
+
+import io.renren.common.utils.R;
+import io.renren.modules.app.annotation.Login;
+import io.renren.modules.app.annotation.LoginUser;
+import io.renren.modules.app.entity.UserEntity;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestAttribute;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * APP测试接口
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+@RestController
+@RequestMapping("/app")
+@Api("APP测试接口")
+public class AppTestController {
+
+    @Login
+    @GetMapping("userInfo")
+    @ApiOperation("获取用户信息")
+    public R userInfo(@LoginUser UserEntity user){
+        return R.ok().put("user", user);
+    }
+
+    @Login
+    @GetMapping("userId")
+    @ApiOperation("获取用户ID")
+    public R userInfo(@RequestAttribute("userId") Integer userId){
+        return R.ok().put("userId", userId);
+    }
+
+    @GetMapping("notToken")
+    @ApiOperation("忽略Token验证测试")
+    public R notToken(){
+        return R.ok().put("msg", "无需token也能访问。。。");
+    }
+
+}

+ 23 - 0
src/main/java/io/renren/modules/app/dao/UserDao.java

@@ -0,0 +1,23 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.modules.app.dao;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import io.renren.modules.app.entity.UserEntity;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 用户
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+@Mapper
+public interface UserDao extends BaseMapper<UserEntity> {
+
+}

+ 59 - 0
src/main/java/io/renren/modules/app/entity/UserEntity.java

@@ -0,0 +1,59 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.modules.app.entity;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+
+/**
+ * 用户
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+@Data
+@TableName("tb_user")
+public class UserEntity implements Serializable {
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * 用户ID
+	 */
+	@TableId
+	private Long userId;
+	/**
+	 * 用户名
+	 */
+	private String username;
+	/**
+	 * 手机号
+	 */
+	private String mobile;
+	/**
+	 * 密码
+	 */
+	private String password;
+	/**
+	 * 创建时间
+	 */
+	private Date createTime;
+
+	public UserEntity() {
+	}
+
+	public UserEntity(String username, String password) {
+		this.username = username;
+		this.password = password;
+	}
+
+}

+ 33 - 0
src/main/java/io/renren/modules/app/form/LoginForm.java

@@ -0,0 +1,33 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.modules.app.form;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+
+/**
+ * 登录表单
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+@Data
+@ApiModel(value = "登录表单")
+public class LoginForm {
+    @ApiModelProperty(value = "手机号")
+    @NotBlank(message="手机号不能为空")
+    private String mobile;
+
+    @ApiModelProperty(value = "密码")
+    @NotBlank(message="密码不能为空")
+    private String password;
+
+}

+ 33 - 0
src/main/java/io/renren/modules/app/form/RegisterForm.java

@@ -0,0 +1,33 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.modules.app.form;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+
+/**
+ * 注册表单
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+@Data
+@ApiModel(value = "注册表单")
+public class RegisterForm {
+    @ApiModelProperty(value = "手机号")
+    @NotBlank(message="手机号不能为空")
+    private String mobile;
+
+    @ApiModelProperty(value = "密码")
+    @NotBlank(message="密码不能为空")
+    private String password;
+
+}

+ 72 - 0
src/main/java/io/renren/modules/app/interceptor/AuthorizationInterceptor.java

@@ -0,0 +1,72 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.modules.app.interceptor;
+
+
+import io.jsonwebtoken.Claims;
+import io.renren.common.exception.RRException;
+import io.renren.modules.app.utils.JwtUtils;
+import io.renren.modules.app.annotation.Login;
+import org.apache.commons.lang.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.stereotype.Component;
+import org.springframework.web.method.HandlerMethod;
+import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * 权限(Token)验证
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+@Component
+public class AuthorizationInterceptor extends HandlerInterceptorAdapter {
+    @Autowired
+    private JwtUtils jwtUtils;
+
+    public static final String USER_KEY = "userId";
+
+    @Override
+    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
+        Login annotation;
+        if(handler instanceof HandlerMethod) {
+            annotation = ((HandlerMethod) handler).getMethodAnnotation(Login.class);
+        }else{
+            return true;
+        }
+
+        if(annotation == null){
+            return true;
+        }
+
+        //获取用户凭证
+        String token = request.getHeader(jwtUtils.getHeader());
+        if(StringUtils.isBlank(token)){
+            token = request.getParameter(jwtUtils.getHeader());
+        }
+
+        //凭证为空
+        if(StringUtils.isBlank(token)){
+            throw new RRException(jwtUtils.getHeader() + "不能为空", HttpStatus.UNAUTHORIZED.value());
+        }
+
+        Claims claims = jwtUtils.getClaimByToken(token);
+        if(claims == null || jwtUtils.isTokenExpired(claims.getExpiration())){
+            throw new RRException(jwtUtils.getHeader() + "失效,请重新登录", HttpStatus.UNAUTHORIZED.value());
+        }
+
+        //设置userId到request里,后续根据userId,获取用户信息
+        request.setAttribute(USER_KEY, Long.parseLong(claims.getSubject()));
+
+        return true;
+    }
+}

+ 53 - 0
src/main/java/io/renren/modules/app/resolver/LoginUserHandlerMethodArgumentResolver.java

@@ -0,0 +1,53 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.modules.app.resolver;
+
+import io.renren.modules.app.annotation.LoginUser;
+import io.renren.modules.app.entity.UserEntity;
+import io.renren.modules.app.interceptor.AuthorizationInterceptor;
+import io.renren.modules.app.service.UserService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.MethodParameter;
+import org.springframework.stereotype.Component;
+import org.springframework.web.bind.support.WebDataBinderFactory;
+import org.springframework.web.context.request.NativeWebRequest;
+import org.springframework.web.context.request.RequestAttributes;
+import org.springframework.web.method.support.HandlerMethodArgumentResolver;
+import org.springframework.web.method.support.ModelAndViewContainer;
+
+/**
+ * 有@LoginUser注解的方法参数,注入当前登录用户
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+@Component
+public class LoginUserHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {
+    @Autowired
+    private UserService userService;
+
+    @Override
+    public boolean supportsParameter(MethodParameter parameter) {
+        return parameter.getParameterType().isAssignableFrom(UserEntity.class) && parameter.hasParameterAnnotation(LoginUser.class);
+    }
+
+    @Override
+    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer container,
+                                  NativeWebRequest request, WebDataBinderFactory factory) throws Exception {
+        //获取用户ID
+        Object object = request.getAttribute(AuthorizationInterceptor.USER_KEY, RequestAttributes.SCOPE_REQUEST);
+        if(object == null){
+            return null;
+        }
+
+        //获取用户信息
+        UserEntity user = userService.getById((Long)object);
+
+        return user;
+    }
+}

+ 31 - 0
src/main/java/io/renren/modules/app/service/UserService.java

@@ -0,0 +1,31 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.modules.app.service;
+
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import io.renren.modules.app.entity.UserEntity;
+import io.renren.modules.app.form.LoginForm;
+
+/**
+ * 用户
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+public interface UserService extends IService<UserEntity> {
+
+	UserEntity queryByMobile(String mobile);
+
+	/**
+	 * 用户登录
+	 * @param form    登录表单
+	 * @return        返回用户ID
+	 */
+	long login(LoginForm form);
+}

+ 44 - 0
src/main/java/io/renren/modules/app/service/impl/UserServiceImpl.java

@@ -0,0 +1,44 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.modules.app.service.impl;
+
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import io.renren.common.exception.RRException;
+import io.renren.common.validator.Assert;
+import io.renren.modules.app.dao.UserDao;
+import io.renren.modules.app.entity.UserEntity;
+import io.renren.modules.app.form.LoginForm;
+import io.renren.modules.app.service.UserService;
+import org.apache.commons.codec.digest.DigestUtils;
+import org.springframework.stereotype.Service;
+
+
+@Service("userService")
+public class UserServiceImpl extends ServiceImpl<UserDao, UserEntity> implements UserService {
+
+	@Override
+	public UserEntity queryByMobile(String mobile) {
+		return baseMapper.selectOne(new QueryWrapper<UserEntity>().eq("mobile", mobile));
+	}
+
+	@Override
+	public long login(LoginForm form) {
+		UserEntity user = queryByMobile(form.getMobile());
+		Assert.isNull(user, "手机号或密码错误");
+
+		//密码错误
+		if(!user.getPassword().equals(DigestUtils.sha256Hex(form.getPassword()))){
+			throw new RRException("手机号或密码错误");
+		}
+
+		return user.getUserId();
+	}
+}

+ 95 - 0
src/main/java/io/renren/modules/app/utils/JwtUtils.java

@@ -0,0 +1,95 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.modules.app.utils;
+
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.SignatureAlgorithm;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+import java.util.Date;
+
+/**
+ * jwt工具类
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+@ConfigurationProperties(prefix = "renren.jwt")
+@Component
+public class JwtUtils {
+    private Logger logger = LoggerFactory.getLogger(getClass());
+
+    private String secret;
+    private long expire;
+    private String header;
+
+    /**
+     * 生成jwt token
+     */
+    public String generateToken(long userId) {
+        Date nowDate = new Date();
+        //过期时间
+        Date expireDate = new Date(nowDate.getTime() + expire * 1000);
+
+        return Jwts.builder()
+                .setHeaderParam("typ", "JWT")
+                .setSubject(userId+"")
+                .setIssuedAt(nowDate)
+                .setExpiration(expireDate)
+                .signWith(SignatureAlgorithm.HS512, secret)
+                .compact();
+    }
+
+    public Claims getClaimByToken(String token) {
+        try {
+            return Jwts.parser()
+                    .setSigningKey(secret)
+                    .parseClaimsJws(token)
+                    .getBody();
+        }catch (Exception e){
+            logger.debug("validate is token error ", e);
+            return null;
+        }
+    }
+
+    /**
+     * token是否过期
+     * @return  true:过期
+     */
+    public boolean isTokenExpired(Date expiration) {
+        return expiration.before(new Date());
+    }
+
+    public String getSecret() {
+        return secret;
+    }
+
+    public void setSecret(String secret) {
+        this.secret = secret;
+    }
+
+    public long getExpire() {
+        return expire;
+    }
+
+    public void setExpire(long expire) {
+        this.expire = expire;
+    }
+
+    public String getHeader() {
+        return header;
+    }
+
+    public void setHeader(String header) {
+        this.header = header;
+    }
+}

+ 66 - 0
src/main/java/io/renren/modules/job/config/ScheduleConfig.java

@@ -0,0 +1,66 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.modules.job.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 Mark sunlightcs@gmail.com
+ */
+@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", "RenrenScheduler");
+        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.springframework.scheduling.quartz.LocalDataSourceJobStore");
+        //集群配置
+        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_");
+        prop.put("org.quartz.jobStore.selectWithLockSQL", "SELECT * FROM {0}LOCKS UPDLOCK WHERE LOCK_NAME = ?");
+
+        //PostgreSQL数据库,需要打开此注释
+        //prop.put("org.quartz.jobStore.driverDelegateClass", "org.quartz.impl.jdbcjobstore.PostgreSQLDelegate");
+
+        factory.setQuartzProperties(prop);
+
+        factory.setSchedulerName("RenrenScheduler");
+        //延时启动
+        factory.setStartupDelay(30);
+        factory.setApplicationContextSchedulerContextKey("applicationContextKey");
+        //可选,QuartzScheduler 启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了
+        factory.setOverwriteExistingJobs(true);
+        //设置自动启动,默认为true
+        factory.setAutoStartup(true);
+
+        return factory;
+    }
+}

+ 132 - 0
src/main/java/io/renren/modules/job/controller/ScheduleJobController.java

@@ -0,0 +1,132 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.modules.job.controller;
+
+import io.renren.common.annotation.SysLog;
+import io.renren.common.utils.PageUtils;
+import io.renren.common.utils.R;
+import io.renren.common.validator.ValidatorUtils;
+import io.renren.modules.job.entity.ScheduleJobEntity;
+import io.renren.modules.job.service.ScheduleJobService;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Map;
+
+/**
+ * 定时任务
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+@RestController
+@RequestMapping("/sys/schedule")
+public class ScheduleJobController {
+	@Autowired
+	private ScheduleJobService scheduleJobService;
+	
+	/**
+	 * 定时任务列表
+	 */
+	@RequestMapping("/list")
+	@RequiresPermissions("sys:schedule:list")
+	public R list(@RequestParam Map<String, Object> params){
+		PageUtils page = scheduleJobService.queryPage(params);
+
+		return R.ok().put("page", page);
+	}
+	
+	/**
+	 * 定时任务信息
+	 */
+	@RequestMapping("/info/{jobId}")
+	@RequiresPermissions("sys:schedule:info")
+	public R info(@PathVariable("jobId") Long jobId){
+		ScheduleJobEntity schedule = scheduleJobService.getById(jobId);
+		
+		return R.ok().put("schedule", schedule);
+	}
+	
+	/**
+	 * 保存定时任务
+	 */
+	@SysLog("保存定时任务")
+	@RequestMapping("/save")
+	@RequiresPermissions("sys:schedule:save")
+	public R save(@RequestBody ScheduleJobEntity scheduleJob){
+		ValidatorUtils.validateEntity(scheduleJob);
+		
+		scheduleJobService.saveJob(scheduleJob);
+		
+		return R.ok();
+	}
+	
+	/**
+	 * 修改定时任务
+	 */
+	@SysLog("修改定时任务")
+	@RequestMapping("/update")
+	@RequiresPermissions("sys:schedule:update")
+	public R update(@RequestBody ScheduleJobEntity scheduleJob){
+		ValidatorUtils.validateEntity(scheduleJob);
+				
+		scheduleJobService.update(scheduleJob);
+		
+		return R.ok();
+	}
+	
+	/**
+	 * 删除定时任务
+	 */
+	@SysLog("删除定时任务")
+	@RequestMapping("/delete")
+	@RequiresPermissions("sys:schedule:delete")
+	public R delete(@RequestBody Long[] jobIds){
+		scheduleJobService.deleteBatch(jobIds);
+		
+		return R.ok();
+	}
+	
+	/**
+	 * 立即执行任务
+	 */
+	@SysLog("立即执行任务")
+	@RequestMapping("/run")
+	@RequiresPermissions("sys:schedule:run")
+	public R run(@RequestBody Long[] jobIds){
+		scheduleJobService.run(jobIds);
+		
+		return R.ok();
+	}
+	
+	/**
+	 * 暂停定时任务
+	 */
+	@SysLog("暂停定时任务")
+	@RequestMapping("/pause")
+	@RequiresPermissions("sys:schedule:pause")
+	public R pause(@RequestBody Long[] jobIds){
+		scheduleJobService.pause(jobIds);
+		
+		return R.ok();
+	}
+	
+	/**
+	 * 恢复定时任务
+	 */
+	@SysLog("恢复定时任务")
+	@RequestMapping("/resume")
+	@RequiresPermissions("sys:schedule:resume")
+	public R resume(@RequestBody Long[] jobIds){
+		scheduleJobService.resume(jobIds);
+		
+		return R.ok();
+	}
+
+}

+ 55 - 0
src/main/java/io/renren/modules/job/controller/ScheduleJobLogController.java

@@ -0,0 +1,55 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.modules.job.controller;
+
+import io.renren.common.utils.PageUtils;
+import io.renren.common.utils.R;
+import io.renren.modules.job.entity.ScheduleJobLogEntity;
+import io.renren.modules.job.service.ScheduleJobLogService;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.Map;
+
+/**
+ * 定时任务日志
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+@RestController
+@RequestMapping("/sys/scheduleLog")
+public class ScheduleJobLogController {
+	@Autowired
+	private ScheduleJobLogService scheduleJobLogService;
+	
+	/**
+	 * 定时任务日志列表
+	 */
+	@RequestMapping("/list")
+	@RequiresPermissions("sys:schedule:log")
+	public R list(@RequestParam Map<String, Object> params){
+		PageUtils page = scheduleJobLogService.queryPage(params);
+		
+		return R.ok().put("page", page);
+	}
+	
+	/**
+	 * 定时任务日志信息
+	 */
+	@RequestMapping("/info/{logId}")
+	public R info(@PathVariable("logId") Long logId){
+		ScheduleJobLogEntity log = scheduleJobLogService.getById(logId);
+		
+		return R.ok().put("log", log);
+	}
+}

+ 29 - 0
src/main/java/io/renren/modules/job/dao/ScheduleJobDao.java

@@ -0,0 +1,29 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.modules.job.dao;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import io.renren.modules.job.entity.ScheduleJobEntity;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.Map;
+
+/**
+ * 定时任务
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+@Mapper
+public interface ScheduleJobDao extends BaseMapper<ScheduleJobEntity> {
+	
+	/**
+	 * 批量更新状态
+	 */
+	int updateBatch(Map<String, Object> map);
+}

+ 23 - 0
src/main/java/io/renren/modules/job/dao/ScheduleJobLogDao.java

@@ -0,0 +1,23 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.modules.job.dao;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import io.renren.modules.job.entity.ScheduleJobLogEntity;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 定时任务日志
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+@Mapper
+public interface ScheduleJobLogDao extends BaseMapper<ScheduleJobLogEntity> {
+	
+}

+ 74 - 0
src/main/java/io/renren/modules/job/entity/ScheduleJobEntity.java

@@ -0,0 +1,74 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.modules.job.entity;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 定时任务
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+@Data
+@TableName("schedule_job")
+public class ScheduleJobEntity implements Serializable {
+	private static final long serialVersionUID = 1L;
+	
+	/**
+	 * 任务调度参数key
+	 */
+    public static final String JOB_PARAM_KEY = "JOB_PARAM_KEY";
+	
+	/**
+	 * 任务id
+	 */
+	@TableId
+	private Long jobId;
+
+	/**
+	 * spring bean名称
+	 */
+	@NotBlank(message="bean名称不能为空")
+	private String beanName;
+	
+	/**
+	 * 参数
+	 */
+	private String params;
+	
+	/**
+	 * cron表达式
+	 */
+	@NotBlank(message="cron表达式不能为空")
+	private String cronExpression;
+
+	/**
+	 * 任务状态
+	 */
+	private Integer status;
+
+	/**
+	 * 备注
+	 */
+	private String remark;
+
+	/**
+	 * 创建时间
+	 */
+	@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+	private Date createTime;
+
+}

+ 71 - 0
src/main/java/io/renren/modules/job/entity/ScheduleJobLogEntity.java

@@ -0,0 +1,71 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.modules.job.entity;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 定时任务日志
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+@Data
+@TableName("schedule_job_log")
+public class ScheduleJobLogEntity implements Serializable {
+	private static final long serialVersionUID = 1L;
+	
+	/**
+	 * 日志id
+	 */
+	@TableId
+	private Long logId;
+	
+	/**
+	 * 任务id
+	 */
+	private Long jobId;
+	
+	/**
+	 * spring bean名称
+	 */
+	private String beanName;
+	
+	/**
+	 * 参数
+	 */
+	private String params;
+	
+	/**
+	 * 任务状态    0:成功    1:失败
+	 */
+	private Integer status;
+	
+	/**
+	 * 失败信息
+	 */
+	private String error;
+	
+	/**
+	 * 耗时(单位:毫秒)
+	 */
+	private Integer times;
+	
+	/**
+	 * 创建时间
+	 */
+	@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+	private Date createTime;
+	
+}

+ 26 - 0
src/main/java/io/renren/modules/job/service/ScheduleJobLogService.java

@@ -0,0 +1,26 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.modules.job.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import io.renren.common.utils.PageUtils;
+import io.renren.modules.job.entity.ScheduleJobLogEntity;
+
+import java.util.Map;
+
+/**
+ * 定时任务日志
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+public interface ScheduleJobLogService extends IService<ScheduleJobLogEntity> {
+
+	PageUtils queryPage(Map<String, Object> params);
+	
+}

+ 60 - 0
src/main/java/io/renren/modules/job/service/ScheduleJobService.java

@@ -0,0 +1,60 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.modules.job.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import io.renren.common.utils.PageUtils;
+import io.renren.modules.job.entity.ScheduleJobEntity;
+
+import java.util.Map;
+
+/**
+ * 定时任务
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+public interface ScheduleJobService extends IService<ScheduleJobEntity> {
+
+	PageUtils queryPage(Map<String, Object> params);
+
+	/**
+	 * 保存定时任务
+	 */
+	void saveJob(ScheduleJobEntity scheduleJob);
+	
+	/**
+	 * 更新定时任务
+	 */
+	void update(ScheduleJobEntity scheduleJob);
+	
+	/**
+	 * 批量删除定时任务
+	 */
+	void deleteBatch(Long[] jobIds);
+	
+	/**
+	 * 批量更新定时任务状态
+	 */
+	int updateBatch(Long[] jobIds, int status);
+	
+	/**
+	 * 立即执行
+	 */
+	void run(Long[] jobIds);
+	
+	/**
+	 * 暂停运行
+	 */
+	void pause(Long[] jobIds);
+	
+	/**
+	 * 恢复运行
+	 */
+	void resume(Long[] jobIds);
+}

+ 39 - 0
src/main/java/io/renren/modules/job/service/impl/ScheduleJobLogServiceImpl.java

@@ -0,0 +1,39 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.modules.job.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import io.renren.common.utils.PageUtils;
+import io.renren.common.utils.Query;
+import io.renren.modules.job.dao.ScheduleJobLogDao;
+import io.renren.modules.job.entity.ScheduleJobLogEntity;
+import io.renren.modules.job.service.ScheduleJobLogService;
+import org.apache.commons.lang.StringUtils;
+import org.springframework.stereotype.Service;
+
+import java.util.Map;
+
+@Service("scheduleJobLogService")
+public class ScheduleJobLogServiceImpl extends ServiceImpl<ScheduleJobLogDao, ScheduleJobLogEntity> implements ScheduleJobLogService {
+
+	@Override
+	public PageUtils queryPage(Map<String, Object> params) {
+		String jobId = (String)params.get("jobId");
+
+		IPage<ScheduleJobLogEntity> page = this.page(
+			new Query<ScheduleJobLogEntity>().getPage(params),
+			new QueryWrapper<ScheduleJobLogEntity>().like(StringUtils.isNotBlank(jobId),"job_id", jobId)
+		);
+
+		return new PageUtils(page);
+	}
+
+}

+ 131 - 0
src/main/java/io/renren/modules/job/service/impl/ScheduleJobServiceImpl.java

@@ -0,0 +1,131 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.modules.job.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import io.renren.common.utils.Constant;
+import io.renren.common.utils.PageUtils;
+import io.renren.common.utils.Query;
+import io.renren.modules.job.dao.ScheduleJobDao;
+import io.renren.modules.job.entity.ScheduleJobEntity;
+import io.renren.modules.job.service.ScheduleJobService;
+import io.renren.modules.job.utils.ScheduleUtils;
+import org.apache.commons.lang.StringUtils;
+import org.quartz.CronTrigger;
+import org.quartz.Scheduler;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.annotation.PostConstruct;
+import java.util.*;
+
+@Service("scheduleJobService")
+public class ScheduleJobServiceImpl extends ServiceImpl<ScheduleJobDao, ScheduleJobEntity> implements ScheduleJobService {
+	@Autowired
+    private Scheduler scheduler;
+	
+	/**
+	 * 项目启动时,初始化定时器
+	 */
+	@PostConstruct
+	public void init(){
+		List<ScheduleJobEntity> scheduleJobList = this.list();
+		for(ScheduleJobEntity scheduleJob : scheduleJobList){
+			CronTrigger cronTrigger = ScheduleUtils.getCronTrigger(scheduler, scheduleJob.getJobId());
+            //如果不存在,则创建
+            if(cronTrigger == null) {
+                ScheduleUtils.createScheduleJob(scheduler, scheduleJob);
+            }else {
+                ScheduleUtils.updateScheduleJob(scheduler, scheduleJob);
+            }
+		}
+	}
+
+	@Override
+	public PageUtils queryPage(Map<String, Object> params) {
+		String beanName = (String)params.get("beanName");
+
+		IPage<ScheduleJobEntity> page = this.page(
+			new Query<ScheduleJobEntity>().getPage(params),
+			new QueryWrapper <ScheduleJobEntity>().like(StringUtils.isNotBlank(beanName),"bean_name", beanName)
+		);
+
+		return new PageUtils(page);
+	}
+
+
+	@Override
+	@Transactional(rollbackFor = Exception.class)
+	public void saveJob(ScheduleJobEntity scheduleJob) {
+		scheduleJob.setCreateTime(new Date());
+		scheduleJob.setStatus(Constant.ScheduleStatus.NORMAL.getValue());
+        this.save(scheduleJob);
+        
+        ScheduleUtils.createScheduleJob(scheduler, scheduleJob);
+    }
+	
+	@Override
+	@Transactional(rollbackFor = Exception.class)
+	public void update(ScheduleJobEntity scheduleJob) {
+        ScheduleUtils.updateScheduleJob(scheduler, scheduleJob);
+                
+        this.updateById(scheduleJob);
+    }
+
+	@Override
+	@Transactional(rollbackFor = Exception.class)
+    public void deleteBatch(Long[] jobIds) {
+    	for(Long jobId : jobIds){
+    		ScheduleUtils.deleteScheduleJob(scheduler, jobId);
+    	}
+    	
+    	//删除数据
+    	this.removeByIds(Arrays.asList(jobIds));
+	}
+
+	@Override
+    public int updateBatch(Long[] jobIds, int status){
+    	Map<String, Object> map = new HashMap<>(2);
+    	map.put("list", Arrays.asList(jobIds));
+    	map.put("status", status);
+    	return baseMapper.updateBatch(map);
+    }
+    
+	@Override
+	@Transactional(rollbackFor = Exception.class)
+    public void run(Long[] jobIds) {
+    	for(Long jobId : jobIds){
+    		ScheduleUtils.run(scheduler, this.getById(jobId));
+    	}
+    }
+
+	@Override
+	@Transactional(rollbackFor = Exception.class)
+    public void pause(Long[] jobIds) {
+        for(Long jobId : jobIds){
+    		ScheduleUtils.pauseJob(scheduler, jobId);
+    	}
+        
+    	updateBatch(jobIds, Constant.ScheduleStatus.PAUSE.getValue());
+    }
+
+	@Override
+	@Transactional(rollbackFor = Exception.class)
+    public void resume(Long[] jobIds) {
+    	for(Long jobId : jobIds){
+    		ScheduleUtils.resumeJob(scheduler, jobId);
+    	}
+
+    	updateBatch(jobIds, Constant.ScheduleStatus.NORMAL.getValue());
+    }
+    
+}

+ 24 - 0
src/main/java/io/renren/modules/job/task/ITask.java

@@ -0,0 +1,24 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.modules.job.task;
+
+/**
+ * 定时任务接口,所有定时任务都要实现该接口
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+public interface ITask {
+
+    /**
+     * 执行定时任务接口
+     *
+     * @param params   参数,多参数使用JSON数据
+     */
+    void run(String params);
+}

+ 30 - 0
src/main/java/io/renren/modules/job/task/TestTask.java

@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.modules.job.task;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+/**
+ * 测试定时任务(演示Demo,可删除)
+ *
+ * testTask为spring bean的名称
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+@Component("testTask")
+public class TestTask implements ITask {
+	private Logger logger = LoggerFactory.getLogger(getClass());
+
+	@Override
+	public void run(String params){
+		logger.debug("TestTask定时任务正在执行,参数为:{}", params);
+	}
+}

+ 81 - 0
src/main/java/io/renren/modules/job/utils/ScheduleJob.java

@@ -0,0 +1,81 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.modules.job.utils;
+
+import io.renren.common.utils.SpringContextUtils;
+import io.renren.modules.job.entity.ScheduleJobEntity;
+import io.renren.modules.job.entity.ScheduleJobLogEntity;
+import io.renren.modules.job.service.ScheduleJobLogService;
+import org.apache.commons.lang.StringUtils;
+import org.quartz.JobExecutionContext;
+import org.quartz.JobExecutionException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.scheduling.quartz.QuartzJobBean;
+
+import java.lang.reflect.Method;
+import java.util.Date;
+
+
+/**
+ * 定时任务
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+public class ScheduleJob extends QuartzJobBean {
+	private Logger logger = LoggerFactory.getLogger(getClass());
+
+    @Override
+    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
+        ScheduleJobEntity scheduleJob = (ScheduleJobEntity) context.getMergedJobDataMap()
+        		.get(ScheduleJobEntity.JOB_PARAM_KEY);
+        
+        //获取spring bean
+        ScheduleJobLogService scheduleJobLogService = (ScheduleJobLogService) SpringContextUtils.getBean("scheduleJobLogService");
+        
+        //数据库保存执行记录
+        ScheduleJobLogEntity log = new ScheduleJobLogEntity();
+        log.setJobId(scheduleJob.getJobId());
+        log.setBeanName(scheduleJob.getBeanName());
+        log.setParams(scheduleJob.getParams());
+        log.setCreateTime(new Date());
+        
+        //任务开始时间
+        long startTime = System.currentTimeMillis();
+        
+        try {
+            //执行任务
+        	logger.debug("任务准备执行,任务ID:" + scheduleJob.getJobId());
+
+			Object target = SpringContextUtils.getBean(scheduleJob.getBeanName());
+			Method method = target.getClass().getDeclaredMethod("run", String.class);
+			method.invoke(target, scheduleJob.getParams());
+			
+			//任务执行总时长
+			long times = System.currentTimeMillis() - startTime;
+			log.setTimes((int)times);
+			//任务状态    0:成功    1:失败
+			log.setStatus(0);
+			
+			logger.debug("任务执行完毕,任务ID:" + scheduleJob.getJobId() + "  总共耗时:" + times + "毫秒");
+		} catch (Exception e) {
+			logger.error("任务执行失败,任务ID:" + scheduleJob.getJobId(), e);
+			
+			//任务执行总时长
+			long times = System.currentTimeMillis() - startTime;
+			log.setTimes((int)times);
+			
+			//任务状态    0:成功    1:失败
+			log.setStatus(1);
+			log.setError(StringUtils.substring(e.toString(), 0, 2000));
+		}finally {
+			scheduleJobLogService.save(log);
+		}
+    }
+}

+ 156 - 0
src/main/java/io/renren/modules/job/utils/ScheduleUtils.java

@@ -0,0 +1,156 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.modules.job.utils;
+
+import io.renren.common.exception.RRException;
+import io.renren.common.utils.Constant;
+import io.renren.modules.job.entity.ScheduleJobEntity;
+import org.quartz.*;
+
+/**
+ * 定时任务工具类
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+public class ScheduleUtils {
+    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) {
+            throw new RRException("获取定时任务CronTrigger出现异常", e);
+        }
+    }
+
+    /**
+     * 创建定时任务
+     */
+    public static void createScheduleJob(Scheduler scheduler, ScheduleJobEntity scheduleJob) {
+        try {
+        	//构建job信息
+            JobDetail jobDetail = JobBuilder.newJob(ScheduleJob.class).withIdentity(getJobKey(scheduleJob.getJobId())).build();
+
+            //表达式调度构建器
+            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression())
+            		.withMisfireHandlingInstructionDoNothing();
+
+            //按新的cronExpression表达式构建一个新的trigger
+            CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(getTriggerKey(scheduleJob.getJobId())).withSchedule(scheduleBuilder).build();
+
+            //放入参数,运行时的方法可以获取
+            jobDetail.getJobDataMap().put(ScheduleJobEntity.JOB_PARAM_KEY, scheduleJob);
+
+            scheduler.scheduleJob(jobDetail, trigger);
+            
+            //暂停任务
+            if(scheduleJob.getStatus() == Constant.ScheduleStatus.PAUSE.getValue()){
+            	pauseJob(scheduler, scheduleJob.getJobId());
+            }
+        } catch (SchedulerException e) {
+            throw new RRException("创建定时任务失败", e);
+        }
+    }
+    
+    /**
+     * 更新定时任务
+     */
+    public static void updateScheduleJob(Scheduler scheduler, ScheduleJobEntity scheduleJob) {
+        try {
+            TriggerKey triggerKey = getTriggerKey(scheduleJob.getJobId());
+
+            //表达式调度构建器
+            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression())
+            		.withMisfireHandlingInstructionDoNothing();
+
+            CronTrigger trigger = getCronTrigger(scheduler, scheduleJob.getJobId());
+            
+            //按新的cronExpression表达式重新构建trigger
+            trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
+            
+            //参数
+            trigger.getJobDataMap().put(ScheduleJobEntity.JOB_PARAM_KEY, scheduleJob);
+            
+            scheduler.rescheduleJob(triggerKey, trigger);
+            
+            //暂停任务
+            if(scheduleJob.getStatus() == Constant.ScheduleStatus.PAUSE.getValue()){
+            	pauseJob(scheduler, scheduleJob.getJobId());
+            }
+            
+        } catch (SchedulerException e) {
+            throw new RRException("更新定时任务失败", e);
+        }
+    }
+
+    /**
+     * 立即执行任务
+     */
+    public static void run(Scheduler scheduler, ScheduleJobEntity scheduleJob) {
+        try {
+        	//参数
+        	JobDataMap dataMap = new JobDataMap();
+        	dataMap.put(ScheduleJobEntity.JOB_PARAM_KEY, scheduleJob);
+        	
+            scheduler.triggerJob(getJobKey(scheduleJob.getJobId()), dataMap);
+        } catch (SchedulerException e) {
+            throw new RRException("立即执行定时任务失败", e);
+        }
+    }
+
+    /**
+     * 暂停任务
+     */
+    public static void pauseJob(Scheduler scheduler, Long jobId) {
+        try {
+            scheduler.pauseJob(getJobKey(jobId));
+        } catch (SchedulerException e) {
+            throw new RRException("暂停定时任务失败", e);
+        }
+    }
+
+    /**
+     * 恢复任务
+     */
+    public static void resumeJob(Scheduler scheduler, Long jobId) {
+        try {
+            scheduler.resumeJob(getJobKey(jobId));
+        } catch (SchedulerException e) {
+            throw new RRException("暂停定时任务失败", e);
+        }
+    }
+
+    /**
+     * 删除定时任务
+     */
+    public static void deleteScheduleJob(Scheduler scheduler, Long jobId) {
+        try {
+            scheduler.deleteJob(getJobKey(jobId));
+        } catch (SchedulerException e) {
+            throw new RRException("删除定时任务失败", e);
+        }
+    }
+}

+ 149 - 0
src/main/java/io/renren/modules/kubesphere/config/KubeSphereConfig.java

@@ -0,0 +1,149 @@
+package io.renren.modules.kubesphere.config;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.web.client.RestTemplateBuilder;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.client.RestTemplate;
+
+import java.sql.SQLOutput;
+
+
+/**
+ * @author chenrj
+ * @email 1412736935@qq.com
+ * @since 2021/7/29
+ */
+@Configuration
+public class KubeSphereConfig {
+    public static String kubesphereUrl;
+    public static String host;
+
+    // s2i相关url
+    public static String S2I_BUILDER_URL;
+    public static String S2I_CREATE_BINARY_QUERY_URL;
+    public static String S2I_UPLOAD_FILE_QUERY_URL;
+    public static String S2I_RUN;
+    // 存储卷相关url
+    public static String STORAGE_VOLUME_CREATE;
+    // 应用相关
+    public static String APPLICATION_DRY_RUN;
+    public static String INGRESS_DRY_RUN;
+    public static String DEPLOYMENT_DRY_RUN;
+    public static String SERVICE_DRY_RUN;
+    public static String APPLICATION;
+    public static String INGRESS;
+    public static String DEPLOYMENT;
+    public static String SERVICE;
+    // token
+    public static String OAUTH_TOKEN;
+
+    @Value("${kubesphereUrl}")
+    public void setS2iBuilderUrl(String kubesphereUrl) {
+        KubeSphereConfig.S2I_BUILDER_URL = kubesphereUrl + "/apis/devops.kubesphere.io/v1alpha1/namespaces/mkcloud/s2ibuilders";
+    }
+    @Value("${kubesphereUrl}")
+    public void setS2iCreateBinaryQueryUrl(String kubesphereUrl) {
+        KubeSphereConfig.S2I_CREATE_BINARY_QUERY_URL = kubesphereUrl + "/apis/devops.kubesphere.io/v1alpha1/namespaces/mkcloud/s2ibinaries";
+    }
+    @Value("${kubesphereUrl}")
+    public void setS2iUploadFileQueryUrl(String kubesphereUrl) {
+        KubeSphereConfig.S2I_UPLOAD_FILE_QUERY_URL = kubesphereUrl + "/kapis/devops.kubesphere.io/v1alpha2/namespaces/mkcloud/s2ibinaries/%s/file";
+    }
+    @Value("${kubesphereUrl}")
+    public void setS2iRun(String kubesphereUrl) {
+        KubeSphereConfig.S2I_RUN = kubesphereUrl + "/apis/devops.kubesphere.io/v1alpha1/namespaces/mkcloud/s2iruns";
+    }
+
+    @Value("${kubesphereUrl}")
+    public void setStorageVolumeCreate(String kubesphereUrl) {
+        KubeSphereConfig.STORAGE_VOLUME_CREATE = kubesphereUrl + "/api/v1/namespaces/mkcloud/persistentvolumeclaims";
+    }
+
+    @Value("${kubesphereUrl}")
+    public void setApplicationDryRun(String kubesphereUrl) {
+        KubeSphereConfig.APPLICATION_DRY_RUN = kubesphereUrl + "/apis/app.k8s.io/v1beta1/namespaces/mkcloud/applications?dryRun=All";
+    }
+    @Value("${kubesphereUrl}")
+    public void setIngressDryRun(String kubesphereUrl) {
+        KubeSphereConfig.INGRESS_DRY_RUN = kubesphereUrl + "/apis/extensions/v1beta1/namespaces/mkcloud/ingresses?dryRun=All";
+    }
+    @Value("${kubesphereUrl}")
+    public void setDeploymentDryRun(String kubesphereUrl) {
+        KubeSphereConfig.DEPLOYMENT_DRY_RUN = kubesphereUrl + "/apis/apps/v1/namespaces/mkcloud/deployments?dryRun=All";
+    }
+    @Value("${kubesphereUrl}")
+    public void setServiceDryRun(String kubesphereUrl) {
+        KubeSphereConfig.SERVICE_DRY_RUN = kubesphereUrl + "/api/v1/namespaces/mkcloud/services?dryRun=All";
+    }
+    @Value("${kubesphereUrl}")
+    public void setAPPLICATION(String kubesphereUrl) {
+        KubeSphereConfig.APPLICATION = kubesphereUrl + "/apis/app.k8s.io/v1beta1/namespaces/mkcloud/applications";
+    }
+    @Value("${kubesphereUrl}")
+    public void setINGRESS(String kubesphereUrl) {
+        KubeSphereConfig.INGRESS = kubesphereUrl + "/apis/extensions/v1beta1/namespaces/mkcloud/ingresses";
+    }
+    @Value("${kubesphereUrl}")
+    public void setDEPLOYMENT(String kubesphereUrl) {
+        KubeSphereConfig.DEPLOYMENT = kubesphereUrl + "/apis/apps/v1/namespaces/mkcloud/deployments";
+    }
+    @Value("${kubesphereUrl}")
+    public void setSERVICE(String kubesphereUrl) {
+        KubeSphereConfig.SERVICE = kubesphereUrl + "/api/v1/namespaces/mkcloud/services";
+    }
+
+    @Value("${kubesphereUrl}")
+    public void setOauthToken(String kubesphereUrl) {
+        KubeSphereConfig.OAUTH_TOKEN = kubesphereUrl + "/oauth/token";
+    }
+
+    @Value("${kubesphereUrl}")
+    public void setKubesphereUrl(String kubesphereUrl) {
+        KubeSphereConfig.kubesphereUrl = kubesphereUrl;
+    }
+
+
+
+//    public static class URL {
+
+
+
+    //public static String host;
+
+//        @Value("${kubesphereUrl}")
+//        public void setHost(String host) {
+//            URL.host = host;
+//        }
+
+
+
+
+
+//    }
+
+    //    public static final String ORIGIN = "http://42.192.195.253:30881";
+//    public static final String UA = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.164 Safari/537.36";
+//    public static final String REFERER = "http://42.192.195.253:30881";
+//    public static final String ACCEPT = "*/*";
+    public static String ORIGIN;
+    @Value("${kubesphereUrl}")
+    public static void setORIGIN(String ORIGIN) {
+        KubeSphereConfig.ORIGIN = ORIGIN;
+    }
+
+    public static final String UA = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.164 Safari/537.36";
+
+    public static String REFERER = KubeSphereConfig.kubesphereUrl;
+    @Value("${kubesphereUrl}")
+    public static void setREFERER(String REFERER) {
+        KubeSphereConfig.REFERER = REFERER;
+    }
+
+    public static final String ACCEPT = "*/*";
+
+    @Bean
+    public RestTemplate restTemplate(RestTemplateBuilder builder) {
+        // Do any additional configuration here
+        return builder.build();
+    }
+}

+ 53 - 0
src/main/java/io/renren/modules/kubesphere/interceptor/KubeSphereTokenManger.java

@@ -0,0 +1,53 @@
+package io.renren.modules.kubesphere.interceptor;
+
+import io.renren.modules.app.entity.UserEntity;
+
+/**
+ * KubeSphere token 管理器
+ *
+ * @author chenrj
+ * @email 1412736935@qq.com
+ * @since 2021/8/1
+ */
+public class KubeSphereTokenManger {
+    private static volatile KubeSphereTokenManger sInstance;
+    private static String token;
+    private static UserEntity user;
+
+    private KubeSphereTokenManger() {
+
+    }
+
+    public static KubeSphereTokenManger get() {
+        if (sInstance == null) {
+            synchronized (KubeSphereTokenManger.class) {
+                if (sInstance == null) {
+                    sInstance = new KubeSphereTokenManger();
+                }
+            }
+        }
+        return sInstance;
+    }
+
+    public void setUser(UserEntity user) {
+        this.user = user;
+    }
+
+    public UserEntity getUser() {
+        return user;
+    }
+
+    public void setToken(String token) {
+        this.token = token;
+    }
+
+    public String getToken() {
+        return token;
+    }
+
+    public static void clearMemory() {
+        KubeSphereTokenManger.user = null;
+        KubeSphereTokenManger.token = null;
+    }
+
+}

+ 51 - 0
src/main/java/io/renren/modules/kubesphere/service/OauthTokenService.java

@@ -0,0 +1,51 @@
+package io.renren.modules.kubesphere.service;
+
+import io.renren.common.utils.GsonUtil;
+import io.renren.modules.app.entity.UserEntity;
+import io.renren.modules.kubesphere.config.KubeSphereConfig;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Service;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.web.client.RestTemplate;
+
+import java.util.Map;
+
+/**
+ * 令牌获取
+ *
+ * @author chenrj
+ * @email 1412736935@qq.com
+ * @since 2021/8/18
+ */
+@Service
+public class OauthTokenService {
+    private RestTemplate restTemplate = new RestTemplate();
+    private final Logger logger = LoggerFactory.getLogger(getClass());
+
+    public String oauthToken(UserEntity user) {
+        String username = user.getUsername();
+        String password = user.getPassword();
+        HttpHeaders headers = new HttpHeaders();
+        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
+        LinkedMultiValueMap<String, Object> params = new LinkedMultiValueMap<>();
+        // 这里是为了登陆kubesphere,要向kuberphere发送token
+        params.add("grant_type", "password");
+        params.add("username", "admin");
+        params.add("password", "MKcloud123");
+        HttpEntity<LinkedMultiValueMap<String, Object>> request = new HttpEntity<>(params, headers);
+        // 泛型为接口返回数据的类型
+        ResponseEntity<String> response = restTemplate.postForEntity(KubeSphereConfig.OAUTH_TOKEN, request, String.class);
+        // 获取响应主体
+        String res = response.getBody();
+        Map<String, String> map = GsonUtil.convertJsonToMap(res);
+        String token = map.get("access_token");
+        logger.info(token);
+        return token;
+    }
+
+}

+ 62 - 0
src/main/java/io/renren/modules/oss/cloud/AliyunCloudStorageService.java

@@ -0,0 +1,62 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.modules.oss.cloud;
+
+import com.aliyun.oss.OSSClient;
+import io.renren.common.exception.RRException;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+
+/**
+ * 阿里云存储
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+public class AliyunCloudStorageService extends CloudStorageService {
+    private OSSClient client;
+
+    public AliyunCloudStorageService(CloudStorageConfig config){
+        this.config = config;
+
+        //初始化
+        init();
+    }
+
+    private void init(){
+        client = new OSSClient(config.getAliyunEndPoint(), config.getAliyunAccessKeyId(),
+                config.getAliyunAccessKeySecret());
+    }
+
+    @Override
+    public String upload(byte[] data, String path) {
+        return upload(new ByteArrayInputStream(data), path);
+    }
+
+    @Override
+    public String upload(InputStream inputStream, String path) {
+        try {
+            client.putObject(config.getAliyunBucketName(), path, inputStream);
+        } catch (Exception e){
+            throw new RRException("上传文件失败,请检查配置信息", e);
+        }
+
+        return config.getAliyunDomain() + "/" + path;
+    }
+
+    @Override
+    public String uploadSuffix(byte[] data, String suffix) {
+        return upload(data, getPath(config.getAliyunPrefix(), suffix));
+    }
+
+    @Override
+    public String uploadSuffix(InputStream inputStream, String suffix) {
+        return upload(inputStream, getPath(config.getAliyunPrefix(), suffix));
+    }
+}

+ 94 - 0
src/main/java/io/renren/modules/oss/cloud/CloudStorageConfig.java

@@ -0,0 +1,94 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.modules.oss.cloud;
+
+
+import io.renren.common.validator.group.AliyunGroup;
+import io.renren.common.validator.group.QcloudGroup;
+import io.renren.common.validator.group.QiniuGroup;
+import lombok.Data;
+import org.hibernate.validator.constraints.Range;
+import org.hibernate.validator.constraints.URL;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+
+/**
+ * 云存储配置信息
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+@Data
+public class CloudStorageConfig implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    //类型 1:七牛  2:阿里云  3:腾讯云
+    @Range(min=1, max=3, message = "类型错误")
+    private Integer type;
+
+    //七牛绑定的域名
+    @NotBlank(message="七牛绑定的域名不能为空", groups = QiniuGroup.class)
+    @URL(message = "七牛绑定的域名格式不正确", groups = QiniuGroup.class)
+    private String qiniuDomain;
+    //七牛路径前缀
+    private String qiniuPrefix;
+    //七牛ACCESS_KEY
+    @NotBlank(message="七牛AccessKey不能为空", groups = QiniuGroup.class)
+    private String qiniuAccessKey;
+    //七牛SECRET_KEY
+    @NotBlank(message="七牛SecretKey不能为空", groups = QiniuGroup.class)
+    private String qiniuSecretKey;
+    //七牛存储空间名
+    @NotBlank(message="七牛空间名不能为空", groups = QiniuGroup.class)
+    private String qiniuBucketName;
+
+    //阿里云绑定的域名
+    @NotBlank(message="阿里云绑定的域名不能为空", groups = AliyunGroup.class)
+    @URL(message = "阿里云绑定的域名格式不正确", groups = AliyunGroup.class)
+    private String aliyunDomain;
+    //阿里云路径前缀
+    private String aliyunPrefix;
+    //阿里云EndPoint
+    @NotBlank(message="阿里云EndPoint不能为空", groups = AliyunGroup.class)
+    private String aliyunEndPoint;
+    //阿里云AccessKeyId
+    @NotBlank(message="阿里云AccessKeyId不能为空", groups = AliyunGroup.class)
+    private String aliyunAccessKeyId;
+    //阿里云AccessKeySecret
+    @NotBlank(message="阿里云AccessKeySecret不能为空", groups = AliyunGroup.class)
+    private String aliyunAccessKeySecret;
+    //阿里云BucketName
+    @NotBlank(message="阿里云BucketName不能为空", groups = AliyunGroup.class)
+    private String aliyunBucketName;
+
+    //腾讯云绑定的域名
+    @NotBlank(message="腾讯云绑定的域名不能为空", groups = QcloudGroup.class)
+    @URL(message = "腾讯云绑定的域名格式不正确", groups = QcloudGroup.class)
+    private String qcloudDomain;
+    //腾讯云路径前缀
+    private String qcloudPrefix;
+    //腾讯云AppId
+    @NotNull(message="腾讯云AppId不能为空", groups = QcloudGroup.class)
+    private Integer qcloudAppId;
+    //腾讯云SecretId
+    @NotBlank(message="腾讯云SecretId不能为空", groups = QcloudGroup.class)
+    private String qcloudSecretId;
+    //腾讯云SecretKey
+    @NotBlank(message="腾讯云SecretKey不能为空", groups = QcloudGroup.class)
+    private String qcloudSecretKey;
+    //腾讯云BucketName
+    @NotBlank(message="腾讯云BucketName不能为空", groups = QcloudGroup.class)
+    private String qcloudBucketName;
+    //腾讯云COS所属地区
+    @NotBlank(message="所属地区不能为空", groups = QcloudGroup.class)
+    private String qcloudRegion;
+
+
+}

+ 78 - 0
src/main/java/io/renren/modules/oss/cloud/CloudStorageService.java

@@ -0,0 +1,78 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.modules.oss.cloud;
+
+import io.renren.common.utils.DateUtils;
+import org.apache.commons.lang.StringUtils;
+
+import java.io.InputStream;
+import java.util.Date;
+import java.util.UUID;
+
+/**
+ * 云存储(支持七牛、阿里云、腾讯云、又拍云)
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+public abstract class CloudStorageService {
+    /** 云存储配置信息 */
+    CloudStorageConfig config;
+
+    /**
+     * 文件路径
+     * @param prefix 前缀
+     * @param suffix 后缀
+     * @return 返回上传路径
+     */
+    public String getPath(String prefix, String suffix) {
+        //生成uuid
+        String uuid = UUID.randomUUID().toString().replaceAll("-", "");
+        //文件路径
+        String path = DateUtils.format(new Date(), "yyyyMMdd") + "/" + uuid;
+
+        if(StringUtils.isNotBlank(prefix)){
+            path = prefix + "/" + path;
+        }
+
+        return path + suffix;
+    }
+
+    /**
+     * 文件上传
+     * @param data    文件字节数组
+     * @param path    文件路径,包含文件名
+     * @return        返回http地址
+     */
+    public abstract String upload(byte[] data, String path);
+
+    /**
+     * 文件上传
+     * @param data     文件字节数组
+     * @param suffix   后缀
+     * @return         返回http地址
+     */
+    public abstract String uploadSuffix(byte[] data, String suffix);
+
+    /**
+     * 文件上传
+     * @param inputStream   字节流
+     * @param path          文件路径,包含文件名
+     * @return              返回http地址
+     */
+    public abstract String upload(InputStream inputStream, String path);
+
+    /**
+     * 文件上传
+     * @param inputStream  字节流
+     * @param suffix       后缀
+     * @return             返回http地址
+     */
+    public abstract String uploadSuffix(InputStream inputStream, String suffix);
+
+}

+ 44 - 0
src/main/java/io/renren/modules/oss/cloud/OSSFactory.java

@@ -0,0 +1,44 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.modules.oss.cloud;
+
+
+import io.renren.common.utils.ConfigConstant;
+import io.renren.common.utils.Constant;
+import io.renren.common.utils.SpringContextUtils;
+import io.renren.modules.sys.service.SysConfigService;
+
+/**
+ * 文件上传Factory
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+public final class OSSFactory {
+    private static SysConfigService sysConfigService;
+
+    static {
+        OSSFactory.sysConfigService = (SysConfigService) SpringContextUtils.getBean("sysConfigService");
+    }
+
+    public static CloudStorageService build(){
+        //获取云存储配置信息
+        CloudStorageConfig config = sysConfigService.getConfigObject(ConfigConstant.CLOUD_STORAGE_CONFIG_KEY, CloudStorageConfig.class);
+
+        if(config.getType() == Constant.CloudService.QINIU.getValue()){
+            return new QiniuCloudStorageService(config);
+        }else if(config.getType() == Constant.CloudService.ALIYUN.getValue()){
+            return new AliyunCloudStorageService(config);
+        }else if(config.getType() == Constant.CloudService.QCLOUD.getValue()){
+            return new QcloudCloudStorageService(config);
+        }
+
+        return null;
+    }
+
+}

+ 88 - 0
src/main/java/io/renren/modules/oss/cloud/QcloudCloudStorageService.java

@@ -0,0 +1,88 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.modules.oss.cloud;
+
+
+import com.alibaba.fastjson.JSONObject;
+import com.qcloud.cos.COSClient;
+import com.qcloud.cos.ClientConfig;
+import com.qcloud.cos.request.UploadFileRequest;
+import com.qcloud.cos.sign.Credentials;
+import io.renren.common.exception.RRException;
+import org.apache.commons.io.IOUtils;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * 腾讯云存储
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+public class QcloudCloudStorageService extends CloudStorageService {
+    private COSClient client;
+
+    public QcloudCloudStorageService(CloudStorageConfig config){
+        this.config = config;
+
+        //初始化
+        init();
+    }
+
+    private void init(){
+    	Credentials credentials = new Credentials(config.getQcloudAppId(), config.getQcloudSecretId(),
+                config.getQcloudSecretKey());
+    	
+    	//初始化客户端配置
+        ClientConfig clientConfig = new ClientConfig();
+        //设置bucket所在的区域,华南:gz 华北:tj 华东:sh
+        clientConfig.setRegion(config.getQcloudRegion());
+        
+    	client = new COSClient(clientConfig, credentials);
+    }
+
+    @Override
+    public String upload(byte[] data, String path) {
+        //腾讯云必需要以"/"开头
+        if(!path.startsWith("/")) {
+            path = "/" + path;
+        }
+        
+        //上传到腾讯云
+        UploadFileRequest request = new UploadFileRequest(config.getQcloudBucketName(), path, data);
+        String response = client.uploadFile(request);
+
+        JSONObject jsonObject = JSONObject.parseObject(response);
+        if(jsonObject.getInteger("code") != 0) {
+            throw new RRException("文件上传失败," + jsonObject.getString("message"));
+        }
+
+        return config.getQcloudDomain() + path;
+    }
+
+    @Override
+    public String upload(InputStream inputStream, String path) {
+    	try {
+            byte[] data = IOUtils.toByteArray(inputStream);
+            return this.upload(data, path);
+        } catch (IOException e) {
+            throw new RRException("上传文件失败", e);
+        }
+    }
+
+    @Override
+    public String uploadSuffix(byte[] data, String suffix) {
+        return upload(data, getPath(config.getQcloudPrefix(), suffix));
+    }
+
+    @Override
+    public String uploadSuffix(InputStream inputStream, String suffix) {
+        return upload(inputStream, getPath(config.getQcloudPrefix(), suffix));
+    }
+}

+ 77 - 0
src/main/java/io/renren/modules/oss/cloud/QiniuCloudStorageService.java

@@ -0,0 +1,77 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.modules.oss.cloud;
+
+import com.qiniu.common.Zone;
+import com.qiniu.http.Response;
+import com.qiniu.storage.Configuration;
+import com.qiniu.storage.UploadManager;
+import com.qiniu.util.Auth;
+import io.renren.common.exception.RRException;
+import org.apache.commons.io.IOUtils;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * 七牛云存储
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+public class QiniuCloudStorageService extends CloudStorageService {
+    private UploadManager uploadManager;
+    private String token;
+
+    public QiniuCloudStorageService(CloudStorageConfig config){
+        this.config = config;
+
+        //初始化
+        init();
+    }
+
+    private void init(){
+        uploadManager = new UploadManager(new Configuration(Zone.autoZone()));
+        token = Auth.create(config.getQiniuAccessKey(), config.getQiniuSecretKey()).
+                uploadToken(config.getQiniuBucketName());
+    }
+
+    @Override
+    public String upload(byte[] data, String path) {
+        try {
+            Response res = uploadManager.put(data, path, token);
+            if (!res.isOK()) {
+                throw new RuntimeException("上传七牛出错:" + res.toString());
+            }
+        } catch (Exception e) {
+            throw new RRException("上传文件失败,请核对七牛配置信息", e);
+        }
+
+        return config.getQiniuDomain() + "/" + path;
+    }
+
+    @Override
+    public String upload(InputStream inputStream, String path) {
+        try {
+            byte[] data = IOUtils.toByteArray(inputStream);
+            return this.upload(data, path);
+        } catch (IOException e) {
+            throw new RRException("上传文件失败", e);
+        }
+    }
+
+    @Override
+    public String uploadSuffix(byte[] data, String suffix) {
+        return upload(data, getPath(config.getQiniuPrefix(), suffix));
+    }
+
+    @Override
+    public String uploadSuffix(InputStream inputStream, String suffix) {
+        return upload(inputStream, getPath(config.getQiniuPrefix(), suffix));
+    }
+}

+ 125 - 0
src/main/java/io/renren/modules/oss/controller/SysOssController.java

@@ -0,0 +1,125 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.modules.oss.controller;
+
+import com.google.gson.Gson;
+import io.renren.common.exception.RRException;
+import io.renren.common.utils.ConfigConstant;
+import io.renren.common.utils.Constant;
+import io.renren.common.utils.PageUtils;
+import io.renren.common.utils.R;
+import io.renren.common.validator.ValidatorUtils;
+import io.renren.common.validator.group.AliyunGroup;
+import io.renren.common.validator.group.QcloudGroup;
+import io.renren.common.validator.group.QiniuGroup;
+import io.renren.modules.oss.cloud.CloudStorageConfig;
+import io.renren.modules.oss.cloud.OSSFactory;
+import io.renren.modules.oss.entity.SysOssEntity;
+import io.renren.modules.oss.service.SysOssService;
+import io.renren.modules.sys.service.SysConfigService;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.util.Arrays;
+import java.util.Date;
+import java.util.Map;
+
+/**
+ * 文件上传
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+@RestController
+@RequestMapping("sys/oss")
+public class SysOssController {
+	@Autowired
+	private SysOssService sysOssService;
+    @Autowired
+    private SysConfigService sysConfigService;
+
+    private final static String KEY = ConfigConstant.CLOUD_STORAGE_CONFIG_KEY;
+	
+	/**
+	 * 列表
+	 */
+	@GetMapping("/list")
+	@RequiresPermissions("sys:oss:all")
+	public R list(@RequestParam Map<String, Object> params){
+		PageUtils page = sysOssService.queryPage(params);
+
+		return R.ok().put("page", page);
+	}
+
+
+    /**
+     * 云存储配置信息
+     */
+    @GetMapping("/config")
+    @RequiresPermissions("sys:oss:all")
+    public R config(){
+        CloudStorageConfig config = sysConfigService.getConfigObject(KEY, CloudStorageConfig.class);
+
+        return R.ok().put("config", config);
+    }
+
+
+	/**
+	 * 保存云存储配置信息
+	 */
+	@PostMapping("/saveConfig")
+	@RequiresPermissions("sys:oss:all")
+	public R saveConfig(@RequestBody CloudStorageConfig config){
+		//校验类型
+		ValidatorUtils.validateEntity(config);
+		ValidatorUtils.validateEntity(config, Constant.CloudService.getByValue(config.getType()));
+
+        sysConfigService.updateValueByKey(KEY, new Gson().toJson(config));
+
+		return R.ok();
+	}
+	
+
+	/**
+	 * 上传文件
+	 */
+	@PostMapping("/upload")
+	@RequiresPermissions("sys:oss:all")
+	public R upload(@RequestParam("file") MultipartFile file) throws Exception {
+		if (file.isEmpty()) {
+			throw new RRException("上传文件不能为空");
+		}
+
+		//上传文件
+		String suffix = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."));
+		String url = OSSFactory.build().uploadSuffix(file.getBytes(), suffix);
+
+		//保存文件信息
+		SysOssEntity ossEntity = new SysOssEntity();
+		ossEntity.setUrl(url);
+		ossEntity.setCreateDate(new Date());
+		sysOssService.save(ossEntity);
+
+		return R.ok().put("url", url);
+	}
+
+
+	/**
+	 * 删除
+	 */
+	@PostMapping("/delete")
+	@RequiresPermissions("sys:oss:all")
+	public R delete(@RequestBody Long[] ids){
+		sysOssService.removeByIds(Arrays.asList(ids));
+
+		return R.ok();
+	}
+
+}

+ 23 - 0
src/main/java/io/renren/modules/oss/dao/SysOssDao.java

@@ -0,0 +1,23 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.modules.oss.dao;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import io.renren.modules.oss.entity.SysOssEntity;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 文件上传
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+@Mapper
+public interface SysOssDao extends BaseMapper<SysOssEntity> {
+	
+}

+ 36 - 0
src/main/java/io/renren/modules/oss/entity/SysOssEntity.java

@@ -0,0 +1,36 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package io.renren.modules.oss.entity;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+
+/**
+ * 文件上传
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+@Data
+@TableName("sys_oss")
+public class SysOssEntity implements Serializable {
+	private static final long serialVersionUID = 1L;
+	
+	@TableId
+	private Long id;
+	//URL地址
+	private String url;
+	//创建时间
+	private Date createDate;
+
+}

Alguns arquivos não foram mostrados porque muitos arquivos mudaram nesse diff